【问题标题】:Create a Timer Publisher using Swift Combine使用 Swift Combine 创建一个 Timer Publisher
【发布时间】:2019-12-03 14:40:10
【问题描述】:

我一直在看Data Flow Through SwiftUI WWDC talk。他们有一张带有示例代码的幻灯片,其中他们使用连接到 SwiftUI 视图的 Timer 发布者,并随时间更新 UI。

我正在编写一些我想做完全相同的事情的代码,但无法弄清楚这个PodcastPlayer.currentTimePublisher 是如何实现的,然后连接到 UI 结构。我也看过所有关于 Combine 的视频。

我怎样才能做到这一点?

示例代码:

struct PlayerView : View {
  let episode: Episode
  @State private var isPlaying: Bool = true
  @State private var currentTime: TimeInterval = 0.0

  var body: some View {
    VStack { // ...
      Text("\(playhead, formatter: currentTimeFormatter)")
    }
    .onReceive(PodcastPlayer.currentTimePublisher) { newCurrentTime in
      self.currentTime = newCurrentTime
    }
  }
}

【问题讨论】:

    标签: swift swiftui publisher combine


    【解决方案1】:

    这里有一个组合计时器的示例。我使用的是全局变量,但您当然应该使用适用于您的场景的任何变量(环境对象、状态等)。

    import SwiftUI
    import Combine
    
    class MyTimer {
        let currentTimePublisher = Timer.TimerPublisher(interval: 1.0, runLoop: .main, mode: .default)
        let cancellable: AnyCancellable?
    
        init() {
            self.cancellable = currentTimePublisher.connect() as? AnyCancellable
        }
    
        deinit {
            self.cancellable?.cancel()
        }
    }
    
    let timer = MyTimer()
    
    struct Clock : View {
      @State private var currentTime: Date = Date()
    
      var body: some View {
        VStack {
          Text("\(currentTime)")
        }
        .onReceive(timer.currentTimePublisher) { newCurrentTime in
          self.currentTime = newCurrentTime
        }
      }
    }
    

    【讨论】:

    • 太棒了,谢谢。所以我猜在示例中他们将currentTimePublisher 作为静态类变量。
    • 看起来像。我没有详细看那个练习。我发布的答案只是使用组合创建计时器的一种方法。也许还有其他人......
    • 是的。而且效果很好。将它作为他们和我的情况的静态变量是有意义的,因为它应该只存在一个计时器。
    • 我认为 deinit 不需要,因为取消初始化时会自动调用 cancel()
    【解决方案2】:

    使用ObservableObject

    使用 Swift Combine 创建一个 Timer Publisher

    class TimeCounter: ObservableObject {
        @Published var time = 0
        
        lazy var timer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true) { _ in self.time += 1 }
        init() { timer.fire() }
    }
    

    就是这样!现在你只需要观察变化:

    struct ContentView: View {
        @StateObject var timeCounter = TimeCounter()
        
        var body: some View {
            Text("\(timeCounter.time)")
        }
    }
    

    【讨论】:

    • 这和Timer.TimerPublisher(interval: 1.0, runLoop: .main, mode: .default)有什么区别?
    【解决方案3】:

    我实现了一个带有新功能的组合计时器,允许您在不同的时间间隔之间切换。

    class CombineTimer {
    
        private let intervalSubject: CurrentValueSubject<TimeInterval, Never>
    
        var interval: TimeInterval {
            get {
                intervalSubject.value
            }
            set {
                intervalSubject.send(newValue)
            }
        }
    
        var publisher: AnyPublisher<Date, Never> {
            intervalSubject
                .map {
                    Timer.TimerPublisher(interval: $0, runLoop: .main, mode: .default).autoconnect()
                }
                .switchToLatest()
                .eraseToAnyPublisher()
        }
    
        init(interval: TimeInterval = 1.0) {
            intervalSubject = CurrentValueSubject<TimeInterval, Never>(interval)
        }
    
    }
    

    要启动计时器,只需订阅publisher 属性。

    SomeView()
        .onReceive(combineTimer.publisher) { date in
            // ...
        }
    

    您可以通过更改interval 属性切换到具有不同间隔的新计时器。

    combineTimer.interval = someNewInterval
    

    【讨论】:

    • 如何取消您的 CombineTimer?
    【解决方案4】:

    从 0 到 9 运行的计时器。

    struct PlayerView : View {
    
        @State private var currentTime: TimeInterval = 0.0  
        @ObservedObject var player = PodcastPlayer()        
        var body: some View {      
            Text("\(Int(currentTime))")
                .font(.largeTitle)
                .onReceive(player.$currentTimePublisher.filter { $0 < 10.0 }) { newCurrentTime in
                    self.currentTime = newCurrentTime
            }
        }
    }
    class PodcastPlayer: ObservableObject {
    
        @Published var currentTimePublisher: TimeInterval = 0.0     
        init() {
            Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { _ in
                self.currentTimePublisher += 1
            }
        }
    }
    

    【讨论】:

      猜你喜欢
      • 2020-10-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多