繁体   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