SwiftUI计时器视图暂停屏幕上的所有其他视图

时间:2020-04-26 03:39:28

标签: ios swift timer swiftui uinavigationbar

我很好奇,即使有人滚动时,是否有一种方法可以使计时器随UI更新(就像现在一样)。另外,我想确保随着UI每秒更新一次,屏幕不会冻结。这个问题已根据我之前收到的有用答案进行了更新。

struct ContentView: View {
    var body: some View {
        NavigationView{
            NavigationLink(destination: ScrollTest()){
                HStack {
                     Text("Next Screen")
                }
            }
        }
    }
}
struct ScrollTest: View {
    @ObservedObject var timer = SectionTimer(duration: 60)
    var body: some View {
        HStack{
            List{
                Section(header: TimerNavigationView(timer: timer)){
                    ForEach((1...50).reversed(), id: \.self) {
                        Text("\($0)…").onAppear(){
                            self.timer.startTimer()
                        }

                    }
                }
            }.navigationBarItems(trailing: TimerNavigationView(timer: timer))
        }

    }
}
struct TimerNavigationView: View {
    @ObservedObject var timer: SectionTimer
    var body: some View{
        HStack {
            Text("\(timer.timeLeftFormatted) left")
            Spacer()
        }
    }
}
class SectionTimer:ObservableObject {
    private var endDate: Date
    private var timer: Timer?
    var timeRemaining: Double {
        didSet {
            self.setRemaining()
        }
    }
    @Published var timeLeftFormatted = ""

    init(duration: Int) {
        self.timeRemaining = Double(duration)
        self.endDate = Date().advanced(by: Double(duration))
        self.startTimer()

    }

    func startTimer() {
        guard self.timer == nil else {
            return
        }
        self.timer = Timer.scheduledTimer(withTimeInterval: 0.5, repeats: true, block: { (timer) in
            self.timeRemaining = self.endDate.timeIntervalSince(Date())
            if self.timeRemaining < 0 {
                timer.invalidate()
                self.timer = nil
            }
        })
    }

    private func setRemaining() {
        let min = max(floor(self.timeRemaining / 60),0)
        let sec = max(floor((self.timeRemaining - min*60).truncatingRemainder(dividingBy:60)),0)
        self.timeLeftFormatted = "\(Int(min)):\(Int(sec))"
    }

    func endTimer() {
        self.timer?.invalidate()
        self.timer = nil
    }
}

1 个答案:

答案 0 :(得分:1)

尽管SwiftUI有一个计时器,但在这种情况下,我认为使用它不是正确的方法。

您的模型应该为您安排时间。

如果您的视图直接观察其模型对象,而不是尝试观察可观察属性中的数组成员,这也将有所帮助。

您没有显示[h264 @ 0000026eb272f280] error while decoding MB 105 66, bytestream -21 [h264 @ 0000026eb2fcb740] error while decoding MB 100 53, bytestream -11 [h264 @ 0000026eb272f280] error while decoding MB 32 22, bytestream -11 [h264 @ 0000026ead9ee300] error while decoding MB 60 20, bytestream -25 [h264 @ 0000026eb27f00c0] error while decoding MB 9 62, bytestream -5 [h264 @ 0000026ead9ee780] error while decoding MB 85 44, bytestream -5 [h264 @ 0000026eb27f0800] error while decoding MB 64 25, bytestream -15 [h264 @ 0000026eb272f280] error while decoding MB 112 23, bytestream -17 [h264 @ 0000026eb2735200] error while decoding MB 30 21, bytestream -7 ,但这是我创建的:

SectionTimer

它使用class SectionTimer:ObservableObject { private var endDate: Date private var timer: Timer? var timeRemaining: Double { didSet { self.setRemaining() } } @Published var timeLeftFormatted = "" init(duration: Int) { self.timeRemaining = Double(duration) self.endDate = Date().advanced(by: Double(duration)) self.startTimer() } func startTimer() { guard self.timer == nil else { return } self.timer = Timer.scheduledTimer(withTimeInterval: 0.5, repeats: true, block: { (timer) in self.timeRemaining = self.endDate.timeIntervalSince(Date()) if self.timeRemaining < 0 { timer.invalidate() self.timer = nil } }) } private func setRemaining() { let min = max(floor(self.timeRemaining / 60),0) let sec = max(floor((self.timeRemaining - min*60).truncatingRemainder(dividingBy:60)),0) self.timeLeftFormatted = "\(Int(min)):\(Int(sec))" } func endTimer() { self.timer?.invalidate() self.timer = nil } } 而不是从剩余的计数器中减去;这是更精确的,因为计时器不会按精确的间隔计时。它将更新Date个发布的媒体资源。

要使用它,我对您的timeLeftFormatted进行了以下更改-

TimerNavigationView

您可以看到将计时器放入模型中如何大大简化视图。

您可以通过struct TimerNavigationView: View { @ObservedObject var timer: SectionTimer var body: some View{ HStack { Text("\(timer.timeLeftFormatted) left") Spacer() } } }

使用它

更新

问题中更新的代码有助于说明问题,我在this answer

中找到了解决方案

滚动视图滚动时,当前.navigationBarItems(trailing: TimerNavigationView(timer: self.test.timers[self.test.currentSection]))的模式会更改,并且不会触发计时器。

解决方案是自行安排RunLoop模式下的计时器,而不是依靠common获得的default模式-

scheduledTimer