SwiftUI: 如何建立一个可定制的定时器倒计时及其独立的视图?

我在博客上看到很多关于如何用Swift UI建立一个定时器的文章。但我无法弄清楚如何让我真正想要的东西工作。

我面临的2个主要问题是:1.我的TimerView每当它的父视图因状态改变而重建时,就会被重建2.我无法向我的@ObservedObject TimeCountDown发送参数。我无法向我的@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)),计时器会被冻结。

解决方案:

@George_E, 请在下面找到我的更新代码(请注意,我简化了 nextWord() func (a but useless here),但它确实修改了 round.m remainingDeck,这是 ObervableObject Round 中的一个 Published 属性。这将触发RoundPlayView的重建,并冻结定时器)。)

import SwiftUI
import Combine

class Round: ObservableObject {
   @Published var remainingDeck: [String] = ["Round#1", "Round#2", "Round#3", "Round4"]
   var roundDuration: Int = 5
}

struct ContentView: View {
  @ObservedObject var round = Round()

  var body: Some View {
      RoundPlayView(round: round)
  }
}

struct RoundPlayView: View {

   @ObservedObject var round: Round
   var duration: Int {
       return round.roundDuration
   }
   @State private var timerStart: Bool = true
   @State private var isTerminated: Bool = false

   init(round: Round) {
       self.round = round
   }

   var body: some View {
       return VStack {
           if self.isTerminated {
               EmptyView()
           } else {
               VStack {
                   CountdownView(duration: duration, onEnded: timerTerminated, start: $timerStart)

                   Button(action: { self.addWord() }) {
                       Text("Add words to the deck")
                   }
              }
           }
       }
   }


   private func nextWord() {
       round.remainingDeck.append("New Word")
   }

   private func timerTerminated() {
       terminateRound()
   }

   private func terminateRound() {
       self.isTerminated = true
   }
}

这是我的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(duration: Int, onEnded: (() -> Void)?, start: Binding<Bool>) {
       self.onEnded = onEnded
       self._countingDown = start
       self.remainingTime = 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?()
  }
}

本文来自投稿,不代表运维实战侠立场,如若转载,请注明出处:https://www.shizhanxia.com/1296.html

(0)
上一篇 2022年6月29日 下午4:06
下一篇 2022年6月29日 下午4:06

相关推荐

发表评论

登录后才能评论