简体   繁体   中英

SwiftUI: Repaint on Device orientation invalidates Timer

I am making a stopwatch using SwiftUI.

I repaint the UI on orientation change for design purposes but this invalidates my timer. I used one of these solutions

How can I persist my timer on orientation change?

Here is the model:

class Model: ObservableObject {
    @Published var isLandScape: Bool = false
    @Published var isPhone: Bool =  UIDevice.current.userInterfaceIdiom == .phone
    @Published var isPhoneAndLandscape: Bool = false;
}

The custom UIHostingController:

extension Notification.Name {
    static let my_onViewWillTransition = Notification.Name("MainUIHostingController_viewWillTransition")
}

class MyUIHostingController<Content> : UIHostingController<Content> where Content : View {

    override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
        NotificationCenter.default.post(name: .my_onViewWillTransition, object: nil, userInfo: ["size": size])
        super.viewWillTransition(to: size, with: coordinator)
    }

}

That I use in the SceneDelegate:

func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
    if let windowScene = scene as? UIWindowScene {
        let window = UIWindow(windowScene: windowScene)
        window.rootViewController = MyUIHostingController(rootView: HomeView().environmentObject(model))
        self.window = window
        window.makeKeyAndVisible()
    }
}

And the stopwatch class:

class StopWatch: ObservableObject {
    @Published var stopWatchTime: String = "00:00:00";
    @Published var isPaused = true;
    var timer = Timer()
    private var counter: Int = 0

    func start() {
        isPaused.toggle();
        timer = Timer.scheduledTimer(withTimeInterval: 0.01, repeats: true) { timer in
           self.counter += 1
           self.stopWatchTime = StopWatch.convertCountToTimeString(counter: self.counter)
        }
    }


    func pause() {
        isPaused.toggle();
        timer.invalidate()
    }


    func reset() {
        self.timer.invalidate()
        self.isPaused = true;
        self.counter = 0;
        self.stopWatchTime = "00:00:00"
    }
}

Edit Here is how the stopwatch is used in the view

struct StopWatchUI: View {
    @ObservedObject var stopWatch = StopWatch()
    @Binding var isSureToResetWatch: Bool;
    @EnvironmentObject var model: Model

    var body: some View {

        return VStack {

            Text(self.stopWatch.stopWatchTime)
                .foregroundColor(.textPrimary)
                .font(.custom("Courier",size: model.isPhoneAndLandscape ? 50 : 120))
                .fontWeight(.heavy)
                .frame(minWidth: 0, maxWidth: .infinity, minHeight: nil, maxHeight: nil)
                .padding(15)
                .minimumScaleFactor(0.4)
                .cornerRadius(5)
                .lineLimit(1)

            HStack {
                StopWatchResetButton(isSureToResetWatch: $isSureToResetWatch, resetTime: self.stopWatch.reset)
                Spacer()
                StopWatchStartButton(
                    start: self.stopWatch.start,
                    pause: self.stopWatch.pause,
                    isStopWatchPaused: self.stopWatch.isPaused
                )
            }
        }
    }
}

As your StopWatch is a property within your view, a new instance of your stopwatch is being created each time a new instance of your view is created, such as when the device layout changes.

Your stopwatch should be a property of your Model . Your Model instance lives in the environment, so its lifetime is the lifetime of the hosting view controller:

class Model: ObservableObject {
    @Published var isLandScape: Bool = false
    @Published var isPhone: Bool =  UIDevice.current.userInterfaceIdiom == .phone
    @Published var isPhoneAndLandscape: Bool = false;
    var stopwatch = StopWatch()
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM