SwiftUI中修改子对象属性后退后视图不更新问题求助
问题:SwiftUI列表修改子对象属性后无法自动刷新
我已经将代码精简到最小复现版本:当修改数组中某个对象的属性时,数据已变更但SwiftUI视图无法可靠更新。
操作步骤:
- 创建新Schedule
- 点击该Schedule进入编辑页
- 修改其名称
- 返回列表页
此时列表视图不会更新,除非新增一个Schedule才会触发List刷新。请问如何实现名称修改后立即更新视图?
原代码
Views
import SwiftUI import CoreData struct ContentView: View { @ObservedObject var allSchedules:AllSchedules = AllSchedules.shared var body: some View { NavigationView { List { ForEach (allSchedules.schedules) { s in NavigationLink(destination: EditView(schedule: s), label: {Text(s.name)} ) } Button("Add a Schedule") { allSchedules.schedules.append(Schedule()) } } } } } struct EditView: View { @State var schedule:Schedule var body: some View { List { TextField("Change Name", text: $schedule.name, prompt: Text("Change Name")) } } }
Classes
class AllSchedules:ObservableObject { @Published var schedules:Array<Schedule> = [] static let shared = AllSchedules() private init() {} } class Schedule: ObservableObject, Identifiable { let id = UUID() @Published var name:String = "New Schedule" }
项目模板代码(无此代码无法运行)
import CoreData @main struct Timer3App: App { let persistenceController = PersistenceController.shared var body: some Scene { WindowGroup { ContentView() .environment(\.managedObjectContext, persistenceController.container.viewContext) } } } struct PersistenceController { static let shared = PersistenceController() static var preview: PersistenceController = { let result = PersistenceController(inMemory: true) let viewContext = result.container.viewContext for _ in 0..<10 { let newItem = Item(context: viewContext) newItem.timestamp = Date() } do { try viewContext.save() } catch { let nsError = error as NSError fatalError("Unresolved error \(nsError), \(nsError.userInfo)") } return result }() let container: NSPersistentContainer init(inMemory: Bool = false) { container = NSPersistentContainer(name: "Timer3") if inMemory { container.persistentStoreDescriptions.first!.url = URL(fileURLWithPath: "/dev/null") } container.loadPersistentStores(completionHandler: { (storeDescription, error) in if let error = error as NSError? { fatalError("Unresolved error \(error), \(error.userInfo)") } }) container.viewContext.automaticallyMergesChangesFromParent = true } }
解决方案
核心问题:AllSchedules里的@Published var schedules只会在数组本身增删元素时发送更新通知,但数组内的Schedule对象是引用类型,其内部属性变化不会触发AllSchedules的更新信号,导致ContentView无法刷新。
下面提供两种实用解决方法:
方法一:将Schedule改为值类型(struct)
值类型的属性修改会生成新实例,数组能检测到元素变化,触发@Published更新,是最简洁的方案。
修改后的代码
Classes部分
class AllSchedules: ObservableObject { @Published var schedules: Array<Schedule> = [] static let shared = AllSchedules() private init() {} } struct Schedule: Identifiable { let id = UUID() var name: String = "New Schedule" }
Views部分
调整EditView使用@Binding传递值类型,ContentView的ForEach绑定数组元素:
struct ContentView: View { @ObservedObject var allSchedules: AllSchedules = AllSchedules.shared var body: some View { NavigationView { List { // 使用$绑定数组元素 ForEach ($allSchedules.schedules) { $s in NavigationLink(destination: EditView(schedule: $s), label: {Text(s.name)} ) } Button("Add a Schedule") { allSchedules.schedules.append(Schedule()) } } } } } struct EditView: View { // 使用@Binding接收值类型 @Binding var schedule: Schedule var body: some View { List { TextField("Change Name", text: $schedule.name, prompt: Text("Change Name")) } } }
方法二:手动监听子对象变化(保留类类型)
如果必须用引用类型的Schedule,可以在AllSchedules中监听每个Schedule的属性变化,手动触发父对象的更新。
修改后的代码
Classes部分
import Combine class AllSchedules: ObservableObject { @Published var schedules: Array<Schedule> = [] static let shared = AllSchedules() private init() {} // 封装添加方法,同时监听子对象变化 func addSchedule() { let newSchedule = Schedule() // 监听子对象的更新信号,触发当前对象的刷新 let cancellable = newSchedule.objectWillChange.sink { [weak self] _ in self?.objectWillChange.send() } // 保存监听对象,防止被提前释放 newSchedule.cancellable = cancellable schedules.append(newSchedule) } } class Schedule: ObservableObject, Identifiable { let id = UUID() @Published var name: String = "New Schedule" // 保存监听的生命周期对象 var cancellable: AnyCancellable? }
Views部分
修改添加按钮调用封装的方法:
Button("Add a Schedule") { allSchedules.addSchedule() }
内容的提问来源于stack exchange,提问作者user1933275
相关产品推荐
相关产品推荐

