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

SwiftUI视图更新异常问题:Exercise对象currentSet属性变更时视图未自动刷新

解决SwiftUI中嵌套ObservableObject属性变化不触发视图更新的问题

这个问题我之前也碰到过,核心原因是你的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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.30 03:52:42