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

Sheet关闭后ObservableObject变更偶尔无法同步至视图

解决SwiftUI中@AppStorage结合ObservableObject更新视图不及时的问题

问题根源

你当前的实现存在核心问题:LocationView和LocationSearch各自创建了独立的SavedLocations实例。虽然@AppStorage会将数据持久化到UserDefaults,但ObservableObject的变更通知仅作用于当前实例,另一个实例无法实时感知数据变化,这就导致了新增位置后视图偶尔不刷新的情况。

解决方案

方案1:传递同一SavedLocations实例

让LocationSearch复用LocationView中的SavedLocations实例,而非新建,这样数据变更会直接触发两个视图同步刷新。

修改LocationSearch代码

struct LocationSearch: View {
    @Environment(\.dismiss) var dismiss
    @StateObject var locationService = LocationService()
    @ObservedObject var networking: Networking
    // 改为接收外部传入的实例,不再自行创建
    @ObservedObject var savedLocations: SavedLocations
    @State var showAlert = false
    
    var body: some View{
        Form {
            // 原有代码保留
            Section {
                List {
                    ForEach(locationService.searchResults, id: \.self) { completionResult in
                        Button("\(completionResult.city), \(completionResult.country)") {
                            networking.getCoordinate(addressString: completionResult.city) { coordinates, error in
                                if error == nil {
                                    networking.lastLocation = CLLocation(latitude: coordinates.latitude, longitude: coordinates.longitude)
                                    DispatchQueue.main.async() {
                                        let newLocation = Location(name: completionResult.city)
                                        
                                        savedLocations.all.append(newLocation)
                                        savedLocations.all = savedLocations.all.unique()
                                        
                                        dismiss()
                                    }
                                } else {
                                    print("Error setting custom location: \(String(describing: error))")
                                    showAlert.toggle()
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}

修改LocationView中Sheet的调用代码

.sheet(isPresented: $showingSheet) {
    // 传入当前视图的savedLocations实例
    LocationSearch(networking: networking, savedLocations: savedLocations)
}

方案2:将SavedLocations改为单例

如果需要在多个视图中共享该实例,可以将其设计为单例,确保全局只有一个实例存在:

class SavedLocations: ObservableObject {
    // 单例实例
    static let shared = SavedLocations()
    private init() {}
    
    @AppStorage("Locations") var all = [Location]()

    func deleteLocation(at offsets: IndexSet) {
        all.remove(atOffsets: offsets)
    }
}

然后在视图中使用单例:

  • LocationView中:@StateObject var savedLocations = SavedLocations.shared
  • LocationSearch中:@ObservedObject var savedLocations = SavedLocations.shared

额外注意事项

  1. Location类型需要遵循Equatable协议,因为你在ForEach中使用了id: \.self,确保SwiftUI能正确识别列表元素的变化。
  2. 自定义类型存储到@AppStorage时,需要遵循RawRepresentable协议,将类型转换为Data或其他可存储的原始类型,避免持久化失败。

内容的提问来源于stack exchange,提问作者John Harrington

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.16 21:55:14