SwiftUI:在设备方向上重绘会使计时器无效
Posted
技术标签:
【中文标题】SwiftUI:在设备方向上重绘会使计时器无效【英文标题】:SwiftUI: Repaint on Device orientation invalidates Timer 【发布时间】:2020-04-14 19:15:52 【问题描述】:我正在使用 SwiftUI 制作秒表。
出于设计目的,我在方向更改时重新绘制 UI,但这会使我的计时器无效。我用了these solutions之一
如何在方向更改时保持计时器?
这是模型:
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
)
【问题讨论】:
如何链接StopWatch
的实例和您的视图? StopWatch
实例应该是您的Model
的一部分。此外,使用计时器增加计数器也不准确;由于Timer
不准确,您将获得相当大的偏差。您应该使用 start Date
而不是计数器来跟踪经过的时间。
谢谢。我用StopWatch
的实例编辑问题。感谢您的建议,我会尝试重构以使用Date
按照我说的将秒表移到模型上,这样你就不会在每次创建新的视图实例时都创建一个新的秒表实例。
【参考方案1】:
由于您的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()
【讨论】:
我不得不将整个StopWatch
类移动到 Model
内,因为在 Model
内创建 StopWatch
的新实例只是在方向更改时更新 UI,但现在它位于功能最少。
您不需要这样做。您需要将您的文本内容绑定到self.model.stopwatch.$stopWatchTime.value
以上是关于SwiftUI:在设备方向上重绘会使计时器无效的主要内容,如果未能解决你的问题,请参考以下文章
SwiftUI:具有计时器样式的文本在提供的日期更改后不会更新