Swiftui 如何禁用父视图的`onAppear`方法?

Swiftui 如何禁用父视图的`onAppear`方法?,swiftui,Swiftui,我目前正在使用SwiftUI开发一个应用程序 我正在尝试制作一个计时器应用程序,我想知道如何禁用父视图的onApear方法或解决我现在遇到的问题的任何方法 当我使用Binding值从父视图(ListView)导航到详细视图时,onAppear方法工作,然后在我的代码中重置Binding值 如果我没有使用方法(func setTimer()在我的代码中)在onAppear中初始化ListRow,绑定值工作正常,但是当我看到第一个视图时,我无法显示初始时间值 我如何解决这个问题 代码如下: 斯威夫

我目前正在使用SwiftUI开发一个应用程序

我正在尝试制作一个计时器应用程序,我想知道如何禁用父视图的
onApear
方法或解决我现在遇到的问题的任何方法

当我使用
Binding
值从父视图(ListView)导航到详细视图时,
onAppear
方法工作,然后在我的代码中重置
Binding

如果我没有使用方法(
func setTimer()
在我的代码中)在
onAppear
中初始化ListRow,绑定值工作正常,但是当我看到第一个视图时,我无法显示初始时间值

我如何解决这个问题


代码如下:

斯威夫特时代酒店

import SwiftUI

@main
struct TimerApp: App {
    @ObservedObject var timeManager = TimeManager()

    var body: some Scene {
        WindowGroup {
            ListTimerView()
                .environmentObject(timeManager)
        }
    }
}
斯威夫特时间经理

import SwiftUI

class TimeManager: ObservableObject {
    
    @Published var multipleTimers:[MultipleTimer] = [
        MultipleTimer( hourSelection: 0, minSelection: 2, secSelection: 0),
        MultipleTimer( hourSelection: 0, minSelection: 0, secSelection: 30),
        MultipleTimer( hourSelection: 1, minSelection: 30, secSelection: 0)

    ]
    
    var timer = Timer.publish(every: 0.05, on: .main, in: .common).autoconnect()
}

enum TimeFormat {
    case hr
    case min
    case sec
}

enum TimerStatus {
    case running
    case pause
    case stopped
}

struct MultipleTimer:Hashable {
    var hourSelection: Int
    var minSelection: Int
    var secSelection: Int
}
ListTimerView.swift

import SwiftUI

struct ListTimerView: View {
    @EnvironmentObject var timeManager:TimeManager
    
    @State var isAddSheet = false
    
    var body: some View {
        NavigationView(){
            VStack{
                ForEach(timeManager.multipleTimers, id:\.self){multipleTimer in
                    ListTimerRow(hourSelection: multipleTimer.hourSelection, minSelection:  multipleTimer.minSelection, secSelection:  multipleTimer.secSelection)
                }
            }
        }
    }
}
import SwiftUI

struct DetailTimerView: View {
    
    @EnvironmentObject var timeManager:TimeManager
    
    @Binding var hourSelection: Int
    @Binding var minSelection: Int
    @Binding var secSelection: Int
    
    @Binding var duration: Double
    
    @Binding var displayedTimeFormat: TimeFormat
    
    var body: some View {
        VStack{
            Text(self.displayTimer())
                .font(.title)
        }
    }
    
    func displayTimer() -> String {
        
        let hr = Int(duration) / 3600
        let min = Int(duration) % 3600 / 60
        let sec = Int(duration) % 3600 % 60
        
        switch displayedTimeFormat {
        case .hr:
            return String(format: "%02d:%02d:%02d", hr, min, sec)
        case .min:
            return String(format: "%02d:%02d", min, sec)
        case .sec:
            return String(format: "%02d:%02d", min, sec)
        }
    }
}
ListTimerRow.swift

import SwiftUI

struct ListTimerRow: View {
    
    @EnvironmentObject var timeManager:TimeManager
    
    @State var hourSelection: Int
    @State var minSelection: Int
    @State var secSelection: Int
    
    @State var duration: Double = 0
    @State var maxValue: Double = 0
    
    @State var displayedTimeFormat: TimeFormat = .min
    @State var timerStatus: TimerStatus = .stopped
    
    var body: some View {
        NavigationLink(destination:DetailTimerView(
            hourSelection: $hourSelection, minSelection: $minSelection, secSelection: $secSelection,
            duration:$duration, displayedTimeFormat:$displayedTimeFormat
        )){
            VStack{
                HStack{
                    Text(self.displayTimer())
                        .font(.title)
                    Spacer()
                    Image(systemName: "stop.circle.fill")
                        .font(.title)
                        .opacity(self.timerStatus == .stopped ? 0.1 : 1)
                        .onTapGesture {
                            if timerStatus != .stopped {
                                self.stop()
                            }
                        }
                    Image(systemName: self.timerStatus == .running ? "pause.circle.fill" : "play.circle.fill")
                        .font(.title)
                        .opacity(self.hourSelection == 0 && self.minSelection == 0 && self.secSelection == 0 ? 0.1 : 1)
                        .onTapGesture {
                            if timerStatus == .stopped {
                                self.setTimer()
                            }
                            if duration != 0 && timerStatus != .running {
                                self.start()
                            } else if timerStatus == .running {
                                self.pause()
                            }
                        }
                }
                Divider()
            }
        }
        .padding()
        .onAppear(){
            self.setTimer()
            print("onAppear checked")
        }
        .onReceive(timeManager.timer) { _ in
            guard self.timerStatus == .running else { return }
            
            if self.duration > 0 {
                self.duration -= 0.05
            } else {
                self.timerStatus = .stopped
            }
        }
    }
    
    
    func setTimer() {
        duration = Double(hourSelection * 3600 + minSelection * 60 + secSelection)
        maxValue = duration
        
        if duration < 60 {
            displayedTimeFormat = .sec
        } else if duration < 3600 {
            displayedTimeFormat = .min
        } else {
            displayedTimeFormat = .hr
        }
    }
    
    func displayTimer() -> String {
        
        let hr = Int(duration) / 3600
        let min = Int(duration) % 3600 / 60
        let sec = Int(duration) % 3600 % 60
        switch displayedTimeFormat {
        case .hr:
            return String(format: "%02d:%02d:%02d", hr, min, sec)
        case .min:
            return String(format: "%02d:%02d", min, sec)
        case .sec:
            return String(format: "%02d:%02d", min, sec)
            
        }
    }
    
    func start() {
        timerStatus = .running
    }
    
    func pause() {
        timerStatus = .pause
    }
    
    func stop() {
        timerStatus = .stopped
        duration = maxValue
    }
}

Xcode:12.0.1版

iOS:14.0


生命周期:SwiftUI应用程序

您不应该使用onAppear,但需要在类似于下面示例的init方法中设置状态的初始值:

self._duration = State(initialValue: initialDurationValue)
例如,ListTimerRow的init方法可能如下所示,您不需要在onAppear中调用setTimer方法

init () {
    let d = Double(hourSelection * 3600 + minSelection * 60 + secSelection)
    _duration = State(initialValue: d)
    _maxValue = State(initialValue: d)
    
    if d < 60 {
        displayedTimeFormat = State(initialValue: .sec)
    } else if d < 3600 {
        displayedTimeFormat = State(initialValue: .min)
    } else {
        displayedTimeFormat = State(initialValue: .hr)
    }

}
init(){
设d=Double(小时选择*3600+分钟选择*60+秒选择)
_持续时间=状态(初始值:d)
_maxValue=状态(初始值:d)
如果d<60{
displayedTimeFormat=状态(初始值:。秒)
}否则,如果d<3600{
displayedTimeFormat=状态(初始值:.min)
}否则{
displayedTimeFormat=状态(初始值:.hr)
}
}

我尝试了一些解决方案,最后选择了这种方式

  .onAppear(){
        if timerStatus == .stopped {
            self.setTimer()
        }
    }

我不确定我是否理解你的问题。你能缩小范围,让它更具体吗?@pawello2222,我需要使用
self.setTimer()
但是当导航到
DetailTimerView
时,计时器运行后,
DetailTimerView
中的绑定值会被重置。谢谢你的回答,有没有办法使用整个
setTimer()
方法而不使用onAppear方法?@Tio我已经更新了我的答案谢谢你的更新,我终于解决了这个问题,因为我发布了答案。谢谢你的支持!