Swift DispatchSourceTimer使不相关的视图每秒重新加载

时间:2019-10-18 06:15:25

标签: ios swift timer uikit swiftui

[28/10/2019:我已经用UIKit重新创建了相同的测试项目...不足为奇,计时器不会重新加载任何视图控制器视图!因此,这看起来像是SwiftUI错误。 UIKIt版本:https://github.com/DominikButz/DispatchSourceTimerUIKit.git]

[2019年10月19日:请查看https://github.com/DominikButz/DispatchTimerSwiftUIReloadBug.git,以更好地了解此问题。在模拟器中启动时,只需点击“启动计时器”。检查调试控制台,您将看到每秒重新加载所有视图。]

原始帖子:

我正在SwiftUI中创建一个iOS应用,需要为此使用计时器。我在下面发布课程。

计时器可以正常工作(除非应用程序在后台运行时不继续,但这又是另一回事了……)。如您在resumeTimer函数中所见,计时器设置为每秒重复一次。

有一个视图,其中在每次触发计时器时都会更新“文本”标签。这很好。但是,问题在于,在计时器启动后(即每秒),所有在计时器启动后加载的其他视图都将重新加载。例如每秒重新加载不相关的列表(未放置计时器文本标签的),这使其不可用。

我知道这是因为,当我通过用户界面暂停或取消计时器时,调试器显示视图不再每秒都重新加载。我在每个视图的主体中放置了一个断点(带有“评估后继续”选项),调试器显示每次计时器触发时都会重新加载视图。

下面的计时器类在SceneDelegate中创建,然后作为环境对象传递到“内容”视图。我之前仅在放置计时器Text标签的视图中将其作为ObservedObject,但结果是相同的。参见下面的视图,该计时器会在计时器触发时重新加载...(gif屏幕截图)。

这可能是SwiftUI中的错误吗?或者我的计时器类可能有错误?感谢您向正确方向的提示

the view gets reloaded every time the timer fires

这是内容视图

struct ContentView: View {

    @EnvironmentObject var workoutModel: WorkoutModel
    @EnvironmentObject  var workoutWatch: WorkoutStopWatchTimer

    @State var selectedMenuIndex  = UserDefaults.standard.integer(forKey: UserDefaultKeys.selectedMenuIndex)
    @State private var workoutViewPresenting = false

    func setMenuIndex() {
        UserDefaults.standard.set(selectedMenuIndex, forKey: UserDefaultKeys.selectedMenuIndex)
    }

    var body: some View {

        TabView(selection:$selectedMenuIndex) {

            WorkoutSelectionView(workoutViewPresenting: self.$workoutViewPresenting).onAppear(perform: self.setMenuIndex)
                 .tabItem {
                     Image(systemName: "1.square.fill")
                     Text("Start Workout")
            }.tag(0)


            Text("History").onAppear(perform: self.setMenuIndex)  // I set a breakpoint here (with continue after evaluation checked)
                .tabItem {
                    Image(systemName: "2.square.fill")
                    Text("History")
            }.tag(1)

            ExerciseListView().onAppear(perform: self.setMenuIndex) // I set a breakpoint here (with continue after evaluation checked)
                .tabItem {
                    Image(systemName: "3.square.fill")
                    Text("Exercises")
                }.tag(2)


            Text("Profile").onAppear(perform: self.setMenuIndex)
                .tabItem {
                     Image(systemName: "person.fill")
                     Text("Profile")
            }.tag(3)

        }.sheet(isPresented: self.$workoutViewPresenting) {
            // the timer is passed to this view in which a Text label is updated every second to show the time
            WorkoutView().environmentObject(self.workoutModel).environmentObject(self.workoutWatch)
        }

    }

}

调试器控制台:查看我在上方的内容视图中添加的注释,以查看放置断点的位置。我没有多次打开和关闭这些视图,它们在计时器运行时每秒自动重新加载。

... 3加载历史 3 loading练习列表视图 4加载历史 4 loading练习列表视图 5加载历史 5 loading练习列表视图 6加载历史 6 loading练习列表视图 7加载历史 7 loading练习列表视图 ...

WorkoutStopWatchTimer类:ObservableObject {

   private var sourceTimer: DispatchSourceTimer?

    private let queue = DispatchQueue.init(label: "stopwatch.timer", qos: .background, attributes: [], autoreleaseFrequency: .never, target: nil)
    private var counter: Int = 0  // seconds

    var endDate: Date?
    var duration: TimeInterval?

    @Published var timeDisplayString = "0:00"

    var paused = false

   var isActive: Bool {
        return  self.sourceTimer != nil
    }

    func start() {
        self.paused = false

        guard let _ = self.sourceTimer else {
            self.startTimer()
            return
        }

        self.resumeTimer()
    }

     func finish() {

        guard self.sourceTimer != nil else {return}
        self.endDate = Date()
        self.duration = TimeInterval(exactly: Double(self.counter))

        self.sourceTimer?.setEventHandler {}
        self.sourceTimer?.cancel()
        if self.paused == true {
            self.sourceTimer?.resume()
        }
        self.sourceTimer = nil
        self.reset()

     }

    func pause() {
          self.paused = true
          self.sourceTimer?.suspend()

      }

     private func reset() {
         self.timeDisplayString = "0:00"
         self.counter = 0

     }

    private func startTimer() {
        self.sourceTimer = DispatchSource.makeTimerSource(flags: DispatchSource.TimerFlags.strict,
                                                          queue: self.queue)

        self.resumeTimer()
    }

    private func resumeTimer() {
        self.sourceTimer?.setEventHandler { [weak self] in
          //  self.eventHandler = {
                self?.updateTimer()
          //  }
        }

        self.sourceTimer?.schedule(deadline: .now(),
                                   repeating: 1)
        self.sourceTimer?.resume()
    }

    private func updateTimer() {
        self.counter += 1

        DispatchQueue.main.async {
            self.timeDisplayString = WorkoutStopWatchTimer.convertCountToTimeString(counter: self.counter)
        }
    }


}

0 个答案:

没有答案