如何通过变量更改(绑定)触发功能

时间:2020-05-11 10:18:07

标签: swiftui

我有一个计时器视图,我想重复使用它,我想通过这样运行的绑定变量来启动计时器:

不幸的是,我今天早上太累了,无法找到解决方法。 :(

也许我的解决方案不好,还有更简单的方法!?

struct TimerView: View {

    @State var hours : Int = 0
    @State var minutes : Int = 0
    @State var seconds : Int = 0
    @State var hundred : Int = 0

    @Binding var running : Bool

    let timer = Timer.publish (every: 0.01, on: .main, in: .common)
    @State var cancellable : Cancellable?

    func start() {
        self.cancellable = timer.connect()
    }

    func stop() {
        cancellable!.cancel()
    }

1 个答案:

答案 0 :(得分:3)

您可以执行以下操作,创建此扩展名

extension Binding {
    func didSet(execute: @escaping (Value) ->Void) -> Binding {
        return Binding(
            get: {
                return self.wrappedValue
            },
            set: {
                let snapshot = self.wrappedValue
                self.wrappedValue = $0
                execute(snapshot)
            }
        )
    }
}

还有父视图,而不是TimerView,您可以拥有类似的东西

//[...]
TimerView(running: $running.didSet{ value in /* your code or function here*/ })
//[...]

检查我的答案here

更新1

根据评论,我了解到您想从TimerView外部启动/停止计时器。

我找到了几种方法

第一个将TimerView声明为父视图中的变量

struct ContentView: View {
    @State var running: Bool = false
    @State var timerView: TimerView? = nil
    init() {
        timerView = TimerView(running: $running)
    }
    // rest...
}

但是根据您的初始化情况,这可能会很痛苦,因此我创建了一个小的扩展程序,使您可以像这样引用视图

extension View {
    func `referenced`<T>(by reference: Binding<T>) -> (Self?){
        DispatchQueue.main.async { reference.wrappedValue = self as! T }
        return self
    }
}

此扩展程序的用法非常简单

struct ContentView: View {
    @State var running: Bool = false
    @State var timerView: TimerView? = nil
    var body: some View {
        TimerView(running: $running).referenced(by: $timerView)
    }
}

然后您可以使用按钮Button(action: self.timerView.start()) { Text("Start Timer") }

对其进行控制

如果针对您的TimerView测试了此代码,但stop()代码似乎有问题。我在cancellable!.cancel()行中添加了注释,并添加了print,一切都按预期进行。

Here's我在Xcode Playground中使用的测试代码

我希望这是您要寻找的答案。