首页 > 解决方案 > 计时器运行时,SwiftUI 警报不会关闭

问题描述

半相关问题:SwiftUI ActionSheet 在计时器运行时不会关闭

我目前正在处理的项目中遇到警报问题。当后台运行计时器时,呈现的警报不会消失。大多数情况下,它需要多次单击解除按钮才能消失。我在示例项目中以尽可能少的开销重新创建了这个问题。

我的主要项目在尝试在不同的视图上显示警报时出现此问题,但我无法在示例项目中重现该问题。通过在计时器正在运行的同一视图上切换警报,可以可靠地复制该问题。我还通过从文本字段中删除绑定来阻止文本字段视图更新进行了测试。第一次单击时警报仍然无法消除。我不确定是否有办法解决这个问题,并且正在寻找任何可能的建议。

Xcode 13.0/iOS 15.0 也出现在 iOS 14.0 中

Timerview.swift

struct TimerView: View {
    @ObservedObject var stopwatch = Stopwatch()
    @State var isAlertPresented:Bool = false
    var body: some View {
        VStack{
            Text(String(format: "%.1f", stopwatch.secondsElapsed))
                 .font(.system(size: 70.0))
                 .minimumScaleFactor(0.1)
                 .lineLimit(1)
            Button(action:{
                stopwatch.actionStartStop()
            }){
                Text("Toggle Timer")
            }
            
            Button(action:{
                isAlertPresented.toggle()
            }){
                Text("Toggle Alert")
            }
        }
        .alert(isPresented: $isAlertPresented){
            Alert(title:Text("Error"),message:Text("I  am presented"))
        }  
    }
}

秒表.swift

class Stopwatch: ObservableObject{
    @Published var secondsElapsed: TimeInterval = 0.0
        @Published var mode: stopWatchMode = .stopped
        
    
    func actionStartStop(){
        if mode == .stopped{
            start()
        }else{
            stop()
        }
    }
    
    var timer = Timer()
    func start() {
        secondsElapsed = 0.0
        mode = .running
        timer = Timer.scheduledTimer(withTimeInterval: 0.1, repeats: true) { timer in
            self.secondsElapsed += 0.1
        }
    }
    
    func stop() {
        timer.invalidate()
        mode = .stopped
    }
    
    enum stopWatchMode {
        case running
        case stopped
    }
}

编辑:将按钮移动到自定义视图解决了最初的问题,但是当按钮需要与 Observable 对象交互时是否有解决方案?

         Button(action:{
             do{
                 try stopwatch.actionDoThis()
             }catch{
                 isAlertPresented = true
             }
         }){
          Text("Toggle Alert")
         }.alert(isPresented: $isAlertPresented){
          Alert(title:Text("Error"),message:Text("I  am presented"))

标签: iosswiftswiftuialert

解决方案


每次计时器运行时 UI 都会重新创建,因为“secondsElapsed”是一个可观察的对象。SwiftUI 将自动监控“secondsElapsed”的变化,并重新调用视图的 body 属性。为了避免这种情况,我们需要将按钮和警报分开到另一个视图,如下所示。

struct TimerView: View {
   @ObservedObject var stopwatch = Stopwatch()
   @State var isAlertPresented:Bool = false
   var body: some View {
     VStack{
        Text(String(format: "%.1f", stopwatch.secondsElapsed))
            .font(.system(size: 70.0))
            .minimumScaleFactor(0.1)
            .lineLimit(1)
        Button(action:{
            stopwatch.actionStartStop()
        }){
            Text("Toggle Timer")
        }
        CustomAlertView(isAlertPresented: $isAlertPresented)
    }
  }
}

struct CustomAlertView: View {
  @Binding var isAlertPresented: Bool
    var body: some View {
       Button(action:{
        isAlertPresented.toggle()
       }){
        Text("Toggle Alert")
       }.alert(isPresented: $isAlertPresented){
        Alert(title:Text("Error"),message:Text("I  am presented"))
    }
  }
}

推荐阅读