SwiftUI天气App追加位置后MultiLocationView不刷新天气数据问题
问题根因
@Published修饰的引用类型(class)数组,仅能感知数组本身的增、删、元素替换操作,无法自动监听数组内每个WeatherViewViewModel实例的内部属性变化MultiLocationView中通过ForEach遍历数组渲染列表项时,未显式声明对子ViewModel的订阅,子ViewModel网络请求返回后更新自身@Published属性时,不会触发当前sheet内视图的重绘- 关闭sheet后
WeatherHomeView加载时会重新读取子ViewModel的最新属性,因此能正常展示数据,和网络请求逻辑本身无关。
修复方案
方案1:ForEach中显式声明对子ViewModel的观察(改造成本最低)
修改MultiLocationView的ForEach实现,遍历得到的每个子ViewModel标记为@ObservedObject,让列表项主动订阅子VM的属性变化:
// 原有错误写法示例:直接遍历vm,未做观察绑定 // ForEach(multiLocationVM.weatherVM) { vm in // LocationWeatherCell(vm: vm) // } // 修改后写法 ForEach(multiLocationVM.weatherVM) { vm in @ObservedObject var observableVM = vm LocationWeatherCell(vm: observableVM) }
如果是低版本SwiftUI不支持闭包内直接声明@ObservedObject,可以把LocationWeatherCell抽为独立子视图,在子视图的属性声明里用@ObservedObject var vm: WeatherViewViewModel接收实例即可,效果一致。
方案2:父ViewModel统一订阅子VM变化(适配复杂业务场景)
如果需要父VM统一感知所有子VM的更新(比如做本地持久化、列表排序、未读提示逻辑),在MultiLocationViewModel的appendLocation方法中给新追加的子VM添加变更订阅,子VM更新时主动触发父VM的变更通知:
import Combine class MultiLocationViewModel: ObservableObject { @Published var weatherVM: [WeatherViewViewModel] = [] private var cancellables = Set<AnyCancellable>() func appendLocation(cityName: String) { let newVM = WeatherViewViewModel(cityName: cityName) // 绑定子VM变更通知到父VM newVM.objectWillChange .sink { [weak self] _ in self?.objectWillChange.send() } .store(in: &cancellables) weatherVM.append(newVM) // 追加完成后立刻触发天气请求,不要依赖主视图的onAppear触发 newVM.fetchWeather() } }
避坑提示:如果原来的天气请求逻辑是放在
WeatherHomeView的.onAppearmodifier中执行,sheet弹出状态下主视图不会触发onAppear,新追加的子VM不会自动发起请求,必须在append新VM后主动调用一次请求方法。
验证标准
修改完成后测试:输入城市名点击加号追加新位置后,无需关闭弹出的MultiLocationView sheet,等网络请求返回即可在列表中实时看到对应位置的温度、天气状况、高低温数据,和主视图展示数据完全同步。
内容的提问来源于stack exchange,提问作者Lance Addison
相关产品推荐
相关产品推荐

