You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何让AppStorage变量变更仅下次App启动生效,不触发当前视图重跑

问题原因

@AppStorage 是 SwiftUI 自带的响应式属性包装器,本质是对 UserDefaults 的封装,会自动监听对应键值的变更,只要值发生修改,就会触发所在 View 的 body 重新计算渲染,所以你在注册页修改isLoggedIn等变量后,DispatcherView 会立刻响应变更刷新视图,导致当前导航栈的注册页被替换。

解决方案

核心思路是启动时只读取一次配置值存入本地状态,本次运行的渲染逻辑完全依赖本地状态,不响应@AppStorage的变更,修改后的值会在下次启动读取时生效,有两种实现方式可选:

方案1:新增本地状态缓存启动值(原有逻辑改动最小)

保留原有的@AppStorage定义,新增一组@State变量用来存储启动时读取的初始值,后续渲染判断全部使用本地@State变量,不再直接依赖@AppStorage变量。
修改后的代码示例:

struct DispatcherView: View {
    // 保留原@AppStorage定义,仅用于启动时读取初始值
    @AppStorage("isLoggedIn") var isLoggedIn = false
    @AppStorage("lastLoggedEmail") var lastLoggedEmail = ""
    @AppStorage("lastLoggedUserType") var lastLoggedUserType = ""
    @AppStorage("lastLoggedUserPassword") var lastLoggedUserPassword = ""

    // 新增本地状态,本次运行的渲染逻辑完全依赖这组值,不会自动更新
    @State private var localIsLoggedIn: Bool = false
    @State private var localLastLoggedEmail: String = ""
    @State private var localLastLoggedUserType: String = ""
    @State private var localLastLoggedUserPassword: String = ""

    @State var isActive: Bool = false
    @State var waitTime: Bool = false

    var body: some View {
        VStack{
            if self.isActive{
                // 所有判断逻辑替换为本地状态变量
                if localIsLoggedIn == false{
                    StartUpView()
                        .onAppear(perform: {
                            UNUserNotificationCenter.current().requestAuthorization(options: [.alert,.sound,.badge]) { (_,_) in
                            }
                        })
                }
                else{
                    if localLastLoggedUserType == "Driver"{
                        DriverContentView(email: $localLastLoggedEmail, userType: $localLastLoggedUserType)
                            .onAppear(perform: {
                                Auth.auth().signIn(withEmail: localLastLoggedEmail, password: localLastLoggedUserPassword) { (res, err) in
                                    if err != nil{print(err as Any)}
                                }
                                UNUserNotificationCenter.current().requestAuthorization(options: [.alert,.sound,.badge]) { (_,_) in
                                }
                            })
                    }
                    else if localLastLoggedUserType == "Admin"{
                        AdminContentView(email: $localLastLoggedEmail, userType: $localLastLoggedUserType)
                            .onAppear(perform: {
                                Auth.auth().signIn(withEmail: localLastLoggedEmail, password: localLastLoggedUserPassword) { (res, err) in
                                    if err != nil{print("ERROR signing user back in\(String(describing: err))")}
                                }
                                UNUserNotificationCenter.current().requestAuthorization(options: [.alert,.sound,.badge]) { (_,_) in
                                }
                            })
                    }
                }
            }
            else{
                VStack{
                    Spacer()
                    Image("splashScreen")
                        .resizable()
                        .scaledToFit()
                        .animation(waitTime == false ? .none : .spring())
                    Spacer()
                }
                .background(Color.white)
                .edgesIgnoringSafeArea(.all)
            }
        }
        .onAppear(){
            // 启动时一次性读取所有配置到本地状态,后续不再更新
            localIsLoggedIn = isLoggedIn
            localLastLoggedEmail = lastLoggedEmail
            localLastLoggedUserType = lastLoggedUserType
            localLastLoggedUserPassword = lastLoggedUserPassword

            DispatchQueue.main.asyncAfter(deadline: .now()+2) {
                withAnimation{
                    self.isActive = true
                }
            }
            DispatchQueue.main.asyncAfter(deadline: .now()+1) {
                waitTime = true
            }
        }
    }
}

注册页的赋值逻辑不需要修改,修改后的值会存入UserDefaults,下次启动时会被读取到本地状态生效。

方案2:直接读取UserDefaults(更简洁)

因为DispatcherView不需要响应配置变更,也不需要在这个页面修改配置,可以直接去掉@AppStorage属性包装器,启动时直接从UserDefaults读取值存入@State即可,完全避免自动监听:

struct DispatcherView: View {
    // 直接从UserDefaults读取初始值存入本地状态,无自动监听
    @State private var localIsLoggedIn: Bool = UserDefaults.standard.bool(forKey: "isLoggedIn")
    @State private var localLastLoggedEmail: String = UserDefaults.standard.string(forKey: "lastLoggedEmail") ?? ""
    @State private var localLastLoggedUserType: String = UserDefaults.standard.string(forKey: "lastLoggedUserType") ?? ""
    @State private var localLastLoggedUserPassword: String = UserDefaults.standard.string(forKey: "lastLoggedUserPassword") ?? ""

    @State var isActive: Bool = false
    @State var waitTime: Bool = false

    // 剩余body和onAppear逻辑和方案1一致,不需要再给本地变量赋值
}

内容的提问来源于stack exchange,提问作者Mohamed Elsayed

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.29 17:36:04