在 SwiftUI 中使用 Timer 触发 NavigationView 更改的正确方法
Correct way to use a Timer to trigger NavigationView change in SwiftUI
我有一个应用程序,我希望用户能够在其中启动定时任务,当时间用完时,导航层次结构应该弹出并将用户带回来。我的代码可以正常工作,但我不喜欢代码的味道。这是处理此类问题的正确方法吗?
class SimpleTimerManager: ObservableObject {
@Published var elapsedSeconds: Double = 0.0
private(set) var timer = Timer()
func start() {
timer = Timer.scheduledTimer(withTimeInterval: 0.01, repeats: true) {_ in
self.elapsedSeconds += 0.01
}
}
func stop() {
timer.invalidate()
elapsedSeconds = 0.0
}
}
struct ContentView: View {
@ObservedObject var timerManager = SimpleTimerManager()
var body: some View {
NavigationView {
NavigationLink(destination: CountDownIntervalView(
timerManager: timerManager, length: 5.0
)) {
Text("Start the timer!")
}
}
.navigationViewStyle(StackNavigationViewStyle())
}
}
struct CountDownIntervalView: View {
@ObservedObject var timerManager: SimpleTimerManager
var length: Double
@Environment(\.presentationMode) var mode: Binding<PresentationMode>
var interval: Double {
let interval = length - self.timerManager.elapsedSeconds
if interval <= 0 {
self.mode.wrappedValue.dismiss()
self.timerManager.stop()
}
return interval
}
var body: some View {
VStack {
Text("Time remaining: \(String(format: "%.2f", interval))")
Button(action: {
self.mode.wrappedValue.dismiss()
self.timerManager.stop()
}) {
Text("Quit early!")
}
}
.navigationBarBackButtonHidden(true)
.onAppear(perform: {
self.timerManager.start()
})
}
}
我有一个自定义后退按钮,我想保留它。我主要担心的是让代码停止计时器并在计算的 属性 中弹出导航感觉不对。我更喜欢
Text("\(interval)").onReceive(timerManager.timer, perform: { _ in
if self.interval <= 0 {
self.mode.wrappedValue.dismiss()
self.timerManager.stop()
}
})
在 CountDownIntervalView
中,但这会产生编译器错误 - Unable to infer complex closure return type; add explicit type to disambiguate
- 而且,老实说,我不确定 方法是否有意义要么(将有条件地弹出导航的代码附加到 UI 的一块)。解决此问题的“最佳实践”方法是什么?
感谢任何想法。
这是一个解决方案。使用 Xcode 11.4 / iOS 13.4
测试
struct CountDownIntervalView: View {
@ObservedObject var timerManager: SimpleTimerManager
var length: Double
@Environment(\.presentationMode) var mode: Binding<PresentationMode>
var interval: Double {
length - self.timerManager.elapsedSeconds
}
var body: some View {
VStack {
Text("Time remaining: \(String(format: "%.2f", interval))")
.onReceive(timerManager.$elapsedSeconds) { _ in
if self.interval <= 0 {
self.mode.wrappedValue.dismiss()
self.timerManager.stop()
}
}
// ... other your code
我有一个应用程序,我希望用户能够在其中启动定时任务,当时间用完时,导航层次结构应该弹出并将用户带回来。我的代码可以正常工作,但我不喜欢代码的味道。这是处理此类问题的正确方法吗?
class SimpleTimerManager: ObservableObject {
@Published var elapsedSeconds: Double = 0.0
private(set) var timer = Timer()
func start() {
timer = Timer.scheduledTimer(withTimeInterval: 0.01, repeats: true) {_ in
self.elapsedSeconds += 0.01
}
}
func stop() {
timer.invalidate()
elapsedSeconds = 0.0
}
}
struct ContentView: View {
@ObservedObject var timerManager = SimpleTimerManager()
var body: some View {
NavigationView {
NavigationLink(destination: CountDownIntervalView(
timerManager: timerManager, length: 5.0
)) {
Text("Start the timer!")
}
}
.navigationViewStyle(StackNavigationViewStyle())
}
}
struct CountDownIntervalView: View {
@ObservedObject var timerManager: SimpleTimerManager
var length: Double
@Environment(\.presentationMode) var mode: Binding<PresentationMode>
var interval: Double {
let interval = length - self.timerManager.elapsedSeconds
if interval <= 0 {
self.mode.wrappedValue.dismiss()
self.timerManager.stop()
}
return interval
}
var body: some View {
VStack {
Text("Time remaining: \(String(format: "%.2f", interval))")
Button(action: {
self.mode.wrappedValue.dismiss()
self.timerManager.stop()
}) {
Text("Quit early!")
}
}
.navigationBarBackButtonHidden(true)
.onAppear(perform: {
self.timerManager.start()
})
}
}
我有一个自定义后退按钮,我想保留它。我主要担心的是让代码停止计时器并在计算的 属性 中弹出导航感觉不对。我更喜欢
Text("\(interval)").onReceive(timerManager.timer, perform: { _ in
if self.interval <= 0 {
self.mode.wrappedValue.dismiss()
self.timerManager.stop()
}
})
在 CountDownIntervalView
中,但这会产生编译器错误 - Unable to infer complex closure return type; add explicit type to disambiguate
- 而且,老实说,我不确定 方法是否有意义要么(将有条件地弹出导航的代码附加到 UI 的一块)。解决此问题的“最佳实践”方法是什么?
感谢任何想法。
这是一个解决方案。使用 Xcode 11.4 / iOS 13.4
测试struct CountDownIntervalView: View {
@ObservedObject var timerManager: SimpleTimerManager
var length: Double
@Environment(\.presentationMode) var mode: Binding<PresentationMode>
var interval: Double {
length - self.timerManager.elapsedSeconds
}
var body: some View {
VStack {
Text("Time remaining: \(String(format: "%.2f", interval))")
.onReceive(timerManager.$elapsedSeconds) { _ in
if self.interval <= 0 {
self.mode.wrappedValue.dismiss()
self.timerManager.stop()
}
}
// ... other your code