onReceive(self.timer)在NavigationView内部不起作用

时间:2020-08-24 20:58:48

标签: swiftui-navigationlink swiftui

我在Scrollview的此页面上有一个自定义标头,仅在它滚动到一定高度时才显示。我将GeometryReaderonReceive结合使用来不断检查当前滚动高度:

@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不再被触发。如果我将NavigationViewZStack交换,一切将再次按预期工作。

我已经看到了这个Timer onReceive not working inside NavigationView问题,但是我没有条件部分。这是SwiftUI错误还是我做错了?

1 个答案:

答案 0 :(得分:1)

以下是针对您的案例的可能解决方案的演示。在Xcode 11.4 / iOS 13.4上进行了测试(并且向前兼容)

这个想法不是通过计时器而是通过视图首选项已读取/跟踪的视图位置变化来做出反应。

enter image description here

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
        }
    }
}