onReceive (self.timer) ไม่ทำงานใน NavigationView

Aug 25 2020

ฉันมีหน้านี้ของ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 ไม่ทำงานในคำถามNavigationViewแต่ฉันไม่มีองค์ประกอบตามเงื่อนไข นี่คือบั๊ก SwiftUI หรือฉันทำอะไรผิด?

คำตอบ

1 Asperi Aug 28 2020 at 14:57

นี่คือการสาธิตวิธีแก้ปัญหาที่เป็นไปได้สำหรับกรณีของคุณ ทดสอบด้วย 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
        }
    }
}