Я видел много блогов / статей о том, как построить таймер с помощью Swift UI. Но я не могу понять, что я действительно хочу работать.
2 основные проблемы, с которыми я сталкиваюсь: 1. Мой TimerView перестраивается всякий раз, когда его родительский вид перестраивается из-за изменения состояний 2. Я не могу отправить параметры в мое свойство @ObservedObject TimeCountDown (2 параметра: длительность, исходящая из другого представления, и завершение onEnded)
class Round: ObservableObject {
@Publishedvar items: [Item]
var duration: Double
init(items: [Item], duration: Double) {
self.items = items
self.duration = duration
}
}
Struct ParentView: View {
@ObservedObject var round: Round
@State private var isRoundTerminated: Bool = false
var body: Some View {
VStack {
if isRoundTerminated {
RoundEndView()
} else {
TimerView(duration: round.duration, onEnded: onTimerTerminated)
RoundPlayView(items: round.items)
}
}
}
}
struct TimerView: View {
@ObservedObject countdown = TimeCountDown()
var duration: Double
var onEnded: (() -> Void)?
///// I DO NOT KNOW HOW TO PROPAGATE THE onEnded completion TO countdown:TimeCountDown
var body: Some View {
Text("There are \(countdown.remainingTime) remaining secs")
.onAppend() {
timer.start(duration: duration)
/// MAYBE I COULD ADD THE onEnded WITHIN THE start() CALL?
}
}
}
class TimeCountDown : ObservableObject {
var timer : Timer!
@Published var remainingTime: Double = 60
var onEnded: (() -> Void)?
init(onEnded: @escaping (() -> Void)?) {
self.onEnded = onEnded
}
func start(duration: Double) {
self.timer?.invalidate()
self.remainingTime = duration
timer = Timer.scheduledTimer(timeInterval:1, target: self, selector:#selector(updateCountdown), userInfo: nil, repeats: true)
}
@objc private func updateCount() {
remainingTime -= 1
if remainingTime <= 0 {
killTimer()
self.onEnded?()
}
}
private func killTimer() {
timer?.invalidate()
timer = nil
}
}
Однако , что не работа ...
Я также пытался реализовать следующий TimerView:
struct CountdownView: View {
@State private var remainingTime: Int = 60
@Binding var countingDown: Bool
var onEnded: (() -> Void)?
let timer = Timer.publish(every: 1, on: .main, in: .common).autoconnect()
init(durations: TimerDuration, onEnded: (() -> Void)?, start: Binding<Bool>) {
self.onEnded = onEnded
self._countingDown = start
self.remainingTime = durations.duration
}
var body: some View {
Text("Remaining \(remainingTime) secs")
.onReceive(timer) {_ in
if self.countingDown {
if self.remainingTime > 0 {
self.remainingTime -= 1
} else {
self.onTerminated()
}
}
}
}
func onTerminated() {
timer.upstream.connect().cancel()
self.remainingTime = 0
onEnded?()
}
}
Однако, когда ParentView перестраивается очень часто (из-за изменений в round.items (@Published from Round: ObservableObject) таймер может быть заморожен.