带PresentationMode的SwiftUI NavigationView在多级导航层次结构中创建错误

带PresentationMode的SwiftUI NavigationView在多级导航层次结构中创建错误,swiftui,swiftui-navigationlink,swiftui-environment,Swiftui,Swiftui Navigationlink,Swiftui Environment,我有一个iOS 13.5 SwiftUI(macOS 10.15.6)应用程序,它要求用户在NavigationView层次结构中导航两层才能玩游戏。比赛是定时的。我想在两个级别中都使用自定义后退按钮,但如果我这样做,第二个级别中的计时器会以一种奇怪的方式中断。如果我在第一级放弃自定义后退按钮,使用系统后退按钮,一切都会正常工作。下面是一个复制问题的最小应用程序: class SimpleTimerManager: ObservableObject { @Published var elap

我有一个iOS 13.5 SwiftUI(macOS 10.15.6)应用程序,它要求用户在
NavigationView
层次结构中导航两层才能玩游戏。比赛是定时的。我想在两个级别中都使用自定义后退按钮,但如果我这样做,第二个级别中的计时器会以一种奇怪的方式中断。如果我在第一级放弃自定义后退按钮,使用系统后退按钮,一切都会正常工作。下面是一个复制问题的最小应用程序:

class SimpleTimerManager: ObservableObject {
  @Published var elapsedSeconds: Double = 0.0
  private(set) var timer = Timer()
  
  func start() {
    print("timer started")
    timer = Timer.scheduledTimer(withTimeInterval: 0.01, repeats: true) {_ in
      if (Int(self.elapsedSeconds * 100) % 100 == 0) { print ("\(self.elapsedSeconds)") }
      self.elapsedSeconds += 0.01
    }
  }
  
  func stop() {
    timer.invalidate()
    elapsedSeconds = 0.0
    print("timer stopped")
  }
}

struct ContentView: View {
  var body: some View {
    NavigationView {
      NavigationLink(destination: CountDownIntervalPassThroughView()) {
        Text("Start the timer!")
      }
    }
    .navigationViewStyle(StackNavigationViewStyle())
  }
}

struct CountDownIntervalPassThroughView: View {
  @Environment(\.presentationMode) var mode: Binding<PresentationMode>

  var body: some View {
    VStack {
      NavigationLink(destination: CountDownIntervalView()) {
        Text("One more click...")
      }
      Button(action: {
        self.mode.wrappedValue.dismiss()
        print("Going back from CountDownIntervalPassThroughView")
      }) {
        Text("Go back!")
      }
    }
    .navigationBarBackButtonHidden(true)
  }
}

struct CountDownIntervalView: View {
  @ObservedObject var timerManager = SimpleTimerManager()
  @Environment(\.presentationMode) var mode: Binding<PresentationMode>
  var interval: Double { 10.0 - self.timerManager.elapsedSeconds }
    
  var body: some View {
    VStack {
      Text("Time remaining: \(String(format: "%.2f", interval))")
        .onReceive(timerManager.$elapsedSeconds) { _ in
          print("\(self.interval)")
          if self.interval <= 0 {
            print("timer auto stop")
            self.timerManager.stop()
            self.mode.wrappedValue.dismiss()
          }
      }
      Button(action: {
        print("timer manual stop")
        self.timerManager.stop()
        self.mode.wrappedValue.dismiss()
      }) {
        Text("Quit early!")
      }
    }
    .onAppear(perform: {
      self.timerManager.start()
    })
    .navigationBarBackButtonHidden(true)
  }
}
类SimpleTimeManager:ObserveObject{
@已发布变量elapsedSeconds:Double=0.0
专用(设置)变量计时器=计时器()
func start(){
打印(“计时器启动”)
timer=timer.scheduledTimer(withTimeInterval:0.01,repeats:true){in
如果(Int(self.elapsedsons*100)%100==0){print(\(self.elapsedsons)}
self.elapsedSeconds+=0.01
}
}
函数停止(){
timer.invalidate()
elapsedSeconds=0.0
打印(“计时器停止”)
}
}
结构ContentView:View{
var body:一些观点{
导航视图{
导航链接(目标:CountDownIntervalPassThroughView()){
文本(“启动计时器!”)
}
}
.navigationViewStyle(StackNavigationViewStyle())
}
}
结构倒计时间隔passthroughview:视图{
@环境(\.presentationMode)变量模式:绑定
var body:一些观点{
VStack{
导航链接(目标:CountDownIntervalView()){
文本(“再次单击…”)
}
按钮(操作:{
self.mode.wrappedValue.disclose()文件
打印(“从倒计时间隔返回通过视图”)
}) {
文本(“返回!”)
}
}
.navigationBarBackButtonHidden(真)
}
}
结构倒计时IntervalView:视图{
@ObservedObject var timerManager=SimpleTimerManager()
@环境(\.presentationMode)变量模式:绑定
变量间隔:双精度{10.0-self.timermager.elapsedsons}
var body:一些观点{
VStack{
文本(“剩余时间:\(字符串(格式:%.2f”,间隔)))
.onReceive(timerManager.$elapsedSeconds){in
打印(“\(self.interval)”)

如果self.interval在Xcode 12/iOS 14上没有什么奇怪的是的,在iOS 14模拟器上测试过。我仍然使用11/13.5。再戳一戳,我会看到两个奇怪的行为。如果我不使用传递,一切都可以手动点击。但是如果我让计时器过期并创建一个弹出窗口,当我尝试重新启动它时,视图会立即弹出,但e timer一直在运行。如果我确实使用了pass-through,那么当我向下导航两层时计时器就会启动,但是视图不会更新。我想知道这是否是SwiftUI处理
onAppear
模式时的一个错误。请关闭
方法。一般来说,
onAppear
onDisappear
看起来超级脆弱,它们不会触发e您期望或完成其关闭的方式等。