使用Swift Combine创建Timer Publisher

eiv*_*dml 9 publisher swift swiftui combine

我一直在看通过SwiftUI WWDC进行数据流。他们有一张带有示例代码的幻灯片,其中他们使用Timer发布者,该发布者连接到SwiftUI视图,并随时间更新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
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

kon*_*iki 16

这里有一个合并计时器的例子。我使用的是全局变量,但是您当然应该使用适用于您的方案的任何变量(environmentObject,State等)。

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
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

  • 我认为 deinit 不需要,因为当你 deinit 时会自动调用 cancel() (2认同)

Moj*_*ini 11

使用 ObservableObject

创建使用斯威夫特结合了定时器出版商

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

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

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

  • 这和 Timer.TimerPublisher(interval: 1.0, runLoop: .main, mode: .default) 有什么区别? (4认同)

小智 5

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

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)
    }

}
Run Code Online (Sandbox Code Playgroud)

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

SomeView()
    .onReceive(combineTimer.publisher) { date in
        // ...
    }
Run Code Online (Sandbox Code Playgroud)

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

combineTimer.interval = someNewInterval
Run Code Online (Sandbox Code Playgroud)

  • 您将如何取消您的组合计时器? (3认同)