SwiftUI视图更新异常问题:Exercise对象currentSet属性变更时视图未自动刷新
这个问题我之前也碰到过,核心原因是你的Neck视图只监听了ExerciseProgram对象的变化——也就是当currentExercise的引用被替换时才会触发更新,但Exercise内部的currentSet变化并不会让ExerciseProgram发出变更通知,所以视图自然不会刷新。既然不能把Exercise改成struct,这里有两个实用的解决方案:
方案一:让ExerciseProgram转发子对象的更新事件
我们可以让ExerciseProgram订阅当前currentExercise的变更信号,当Exercise内部的属性变化时,手动触发ExerciseProgram的objectWillChange通知,这样上层视图就能感知到变化了。
修改后的ExerciseProgram代码如下:
import Combine class ExerciseProgram: ObservableObject { @Published var currentExercise: Exercise? = nil private var cancellables = Set<AnyCancellable>() // 在currentExercise变更时自动订阅其变化 @Published var currentExercise: Exercise? = nil { didSet { // 清空之前的订阅,避免内存泄漏 cancellables.removeAll() // 订阅新的currentExercise的变更信号 currentExercise?.objectWillChange .sink { [weak self] _ in // 转发变更通知给ExerciseProgram的订阅者 self?.objectWillChange.send() } .store(in: &cancellables) } } func start() { self.currentExercise = self.exercises[exerciseIndex + 1] } }
这样一来,不管是currentExercise被替换,还是它内部的currentSet变化,ExerciseProgram都会发出通知,Neck视图也就会随之刷新了。记得要导入Combine框架来使用订阅相关的API,并且用[weak self]避免循环引用。
方案二:用子视图单独监听Exercise对象
另一种更模块化的方式是把显示currentSet的部分拆成一个子视图,让子视图直接持有Exercise作为@ObservedObject,这样子视图会自动监听Exercise内部的属性变化。
修改后的视图代码如下:
struct Neck: View { @ObservedObject var program: ExerciseProgram = ExerciseProgram(exercises: neckExercises) var body: some View { if let currentExercise = program.currentExercise { // 子视图负责监听Exercise的变化 ExerciseSetDisplay(exercise: currentExercise) } else { Text("No active exercise") } } } // 子视图专门处理Exercise的显示 struct ExerciseSetDisplay: View { @ObservedObject var exercise: Exercise var body: some View { Text("\(exercise.currentSet)") } }
这个方案的好处是职责分离:Neck视图只关心currentExercise的存在与否,而ExerciseSetDisplay专门负责监听Exercise的内部变化,代码逻辑更清晰,也不需要修改ExerciseProgram的实现。
两种方案都能解决你的问题,你可以根据自己的代码结构选择更合适的一种。
内容的提问来源于stack exchange,提问作者Loppik

