簡體   English   中英

我怎樣才能通過綁定<timer>在 SwiftUI 中?</timer>

[英]How can I pass Binding<Timer> in SwiftUI?

我想將計時器從ContentView傳遞給SecondView ,但我不知道如何管理它,因為我以前從未使用過它。

有人可以幫我解決這個問題嗎?

內容視圖

struct ContentView: View {
    @State private var timer = Timer.publish(every: 1, tolerance: 0.5, on: .main, in: .common).autoconnect()
    @State private var timeRemaining = 10
    
    
    var body: some View {
        NavigationView {
            VStack {
                Text("\(timeRemaining)")
                    .onReceive(timer) { _ in
                        if timeRemaining > 0 {
                            timeRemaining -= 1
                        }
                    }
                
                NavigationLink {
                    SecondView(timer: ???) // <-- What should i pass here?
                } label: {
                    Text("Change View")
                }
            }
        }
    }
}

第二視圖

struct SecondView: View {
    @Binding var timer: ??? // <-- What type?
    @State private var timeRemaining = 5
    
    var body: some View {
        Text("Hello")
            .onReceive(timer) { _ in
                if timeRemaining > 0 {
                    timeRemaining -= 1
                }
            }
    }
}

struct SecondView_Previews: PreviewProvider {
    static var previews: some View {
        SecondView(timer: ???) // <-- Same thing here in SecondView preview
    }
}

有了這個計時器聲明,您就進入了Combine世界。 Combine是 Apple 的響應式框架。

首先,您需要導入它:

import Combine

我已經對代碼進行了注釋,但Combine是一個遙遠的領域,最好閱讀有關它的文檔,閱讀一些教程並嘗試一些事情。

文件

struct ContentView: View {
    // The typ here is Publishers.Autoconnect<Timer.TimerPublisher>
    // But we can erase it and the result will be a Publisher that emits a date and never throws an error: AnyPublisher<Date,Never>
    @State private var timer = Timer.publish(every: 1, tolerance: 0.5, on: .main, in: .common)
        .autoconnect()
        .eraseToAnyPublisher()
    @State private var timeRemaining = 10
    
    
    var body: some View {
        NavigationView {
            VStack {
                Text("\(timeRemaining)")
                    .onReceive(timer) { _ in
                        if timeRemaining > 0 {
                            timeRemaining -= 1
                        }
                    }
                
                NavigationLink {
                    // pass the publisher on
                    SecondView(timer: timer)
                } label: {
                    Text("Change View")
                }
            }
        }
    }
}

struct SecondView: View {
    //You don´t need binding here as this view never manipulates this publisher
    var timer: AnyPublisher<Date,Never>
    @State private var timeRemaining = 5
    
    var body: some View {
        Text("Hello")
            .onReceive(timer) { _ in
                if timeRemaining > 0 {
                    timeRemaining -= 1
                    print(timeRemaining)
                }
            }
    }
}

struct SecondView_Previews: PreviewProvider {
    // Creating a static private var should work here !not tested!
    @State static private var timer = Timer.publish(every: 1, tolerance: 0.5, on: .main, in: .common)
        .autoconnect()
        .eraseToAnyPublisher()
    static var previews: some View {
        SecondView(timer: timer)
    }
}

如上所述,您可以簡單地注入計時器發布者,但可能有一個更簡單的解決方案:

FirstView已經隨着計時器的每一個滴答聲而更新。 您可以簡單地將timeRemaning綁定傳遞給您的第二個視圖,然后它也會隨着計時器的每個滴答聲而更新(因為timeRemaining在每個滴答聲上都會發生變化)。 然后,您可以使用.onChange(of:)觀察timeRemaining的變化並做出反應:

struct SecondView: View {
    @Binding var timeRemaining: TimeInterval

    var body: some View {
        Text("Hello")
           .onChange(of: timeRemaining) {
               if $0 < 0 { 
                   timeRemaining = -1
               }
           }
    }
}
               

您不需要傳遞綁定,因為您沒有從第二個視圖更改 contentview 的計時器。 您可以將引用傳遞給計時器發布者,然后使用.onReceive()訂閱它。

import Combine // here

struct ContentView: View {
    let timer = Timer.publish(every: 1, tolerance: 0.5, on: .main, in: .common).autoconnect().eraseToAnyPublisher() //<= Here
    @State private var timeRemaining = 10
    
    
    var body: some View {
        NavigationView {
            VStack {
                Text("\(timeRemaining)")
                    .onReceive(timer) { _ in
                        if timeRemaining > 0 {
                            timeRemaining -= 1
                        }
                    }
                
                NavigationLink {
                    SecondView(timer: timer)
                } label: {
                    Text("Change View")
                }
            }
        }
    }
}

struct SecondView: View {
    let timer: AnyPublisher<Date, Never> // Here
    @State private var timeRemaining = 5
    
    var body: some View {
        VStack {
            Text("Hello")
                .onReceive(timer) { _ in
                    if timeRemaining > 0 {
                        timeRemaining -= 1
                    }
                }
            Text("time remaining \(timeRemaining)")
        }
    }
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM