我在Scrollview
的此页面上有一个自定义标头,仅在它滚动到一定高度时才显示。我将GeometryReader
与onReceive
结合使用来不断检查当前滚动高度:
@State var userInfoUpateInterval = Timer.publish(every: 0.1, on: .current, in: .tracking).autoconnect()
@State var showHeader: Bool = false
var body: some View {
NavigationView {
ZStack(alignment: .top) {
ScrollView(.vertical) {
GeometryReader { geometry in
Text("User info component").onReceive(self.userInfoUpateInterval) { (_) in
self.onUserInfoLayoutChange(geometry)
}
}
VStack {
Text("content")
}.frame(width: UIScreen.screenWidth, height: 1500)
}
ProfileHeader(title: "user.userName", showHeader: $showHeader)
}
}
}
在我将ZStack
包裹在NavigationView
中之前,滚动和标题的隐藏/显示是完美的。 onReceive
不再被触发。如果我将NavigationView
与ZStack
交换,一切将再次按预期工作。
我已经看到了这个Timer onReceive not working inside NavigationView问题,但是我没有条件部分。这是SwiftUI错误还是我做错了?
答案 0 :(得分:1)
以下是针对您的案例的可能解决方案的演示。在Xcode 11.4 / iOS 13.4上进行了测试(并且向前兼容)
这个想法不是通过计时器而是通过视图首选项已读取/跟踪的视图位置变化来做出反应。
struct ViewOffsetKey: PreferenceKey {
typealias Value = CGFloat
static var defaultValue: CGFloat { 0 }
static func reduce(value: inout Value, nextValue: () -> Value) {
value = value + nextValue()
}
}
struct DemoView: View {
@State var showHeader: Bool = false
var body: some View {
NavigationView {
ZStack(alignment: .top) {
ScrollView(.vertical) {
Text("User info component")
.background(GeometryReader {
Color.clear.preference(key: ViewOffsetKey.self,
value: -$0.frame(in: .named("scroll_area")).origin.y) })
VStack {
Text("content")
}.frame(maxWidth: .infinity, minHeight: 1500)
}.coordinateSpace(name: "scroll_area")
if showHeader {
Text("ProfileHeader")
}
}
}
.onPreferenceChange(ViewOffsetKey.self) {
self.showHeader = $0 > 200 // << your condition
}
}
}