简体   繁体   English

SwiftUI:在设备方向上重绘会使计时器无效

[英]SwiftUI: Repaint on Device orientation invalidates Timer

I am making a stopwatch using SwiftUI.我正在使用 SwiftUI 制作秒表。

I repaint the UI on orientation change for design purposes but this invalidates my timer.出于设计目的,我在方向更改时重新绘制 UI,但这会使我的计时器无效。 I used one of these solutions我使用了其中一种解决方案

How can I persist my timer on orientation change?如何在方向更改时保持计时器?

Here is the model:这是 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:自定义 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:我在 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:

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.由于StopWatch是视图中的一个属性,因此每次创建视图的新实例时都会创建一个新的秒表实例,例如当设备布局更改时。

Your stopwatch should be a property of your Model .您的秒表应该是您的Model的财产。 Your Model instance lives in the environment, so its lifetime is the lifetime of the hosting view controller:您的Model实例存在于环境中,因此它的生命周期是托管视图 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()
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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