首页 > 解决方案 > SwiftUI:在设备方向上重绘会使计时器无效

问题描述

我正在使用 SwiftUI 制作秒表。

出于设计目的,我在方向更改时重新绘制 UI,但这会使我的计时器无效。我使用了其中一种解决方案

如何在方向更改时保持计时器?

这是模型:

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

自定义 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)
    }

}

我在 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()
    }
}

还有秒表类:

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"
    }
}

编辑这是秒表在视图中的使用方式

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
                )
            }
        }
    }
}

标签: iosswiftui

解决方案


由于您StopWatch是视图中的属性,因此每次创建视图的新实例时都会创建秒表的新实例,例如当设备布局更改时。

您的秒表应该是您的财产Model。你的Model实例存在于环境中,所以它的生命周期就是宿主视图控制器的生命周期:

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

推荐阅读