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

Swift结构体中Property Wrapper值无法更新视图问题

问题原因分析

你遇到的UI不更新问题,核心在于值类型的可变性检测逻辑和自定义Property Wrapper的nonmutating setter:

  1. 当@Uppercased直接放在View中时,SwiftUI会将Property Wrapper内部的@State与View生命周期绑定,即便setter用了nonmutating,View的状态系统仍能捕捉到内部@State的变化,触发更新。
  2. 但将@Uppercased嵌套在自定义结构体中,并用@State修饰整个结构体实例时,情况发生了变化:
    • Uppercased的nonmutating setter对于值类型(你的嵌套结构体)来说,不会修改整个结构体实例的内存标识——修改结构体内部的@Uppercased属性后,外部的@State会认为整个结构体实例没有变化,因此不会触发UI更新。
    • 嵌套结构体中的@State(属于Uppercased)无法正确绑定到父View的生命周期,状态变化无法被SwiftUI的视图更新系统感知。
解决方案

方案1:移除nonmutating修饰符,让setter修改整个值类型实例

修改你的Uppercased Property Wrapper,去掉wrappedValue setter的nonmutating:

@propertyWrapper
struct Uppercased: DynamicProperty {
    @State private var value: String
    
    var wrappedValue: String {
        get {
            value
        }
        set { // 移除nonmutating修饰符
            value = newValue.uppercased()
        }
    }
    
    init(wrappedValue: String) {
        self.value = wrappedValue.uppercased()
    }
}

此时,修改嵌套结构体中的@Uppercased属性时,setter会改变整个结构体实例的状态,@State能检测到这个值类型的变更,触发UI更新。示例代码如下:

struct TextSettings {
    @Uppercased var content: String = "pork on the fork"
}

struct ContentView: View {
    @State private var settings = TextSettings()
    
    var body: some View {
        VStack {
            Text(settings.content)
            Button("New string") {
                settings.content = "chicken on the knife"
            }
        }
        .padding()
    }
}

方案2:将嵌套结构体改为ObservableObject,用@Published监听变化

如果希望保持nonmutating setter,或者嵌套结构体有多个需要监听的属性,可以将其改为引用类型的ObservableObject,并用@Published修饰@Uppercased属性:

class TextSettings: ObservableObject {
    @Published @Uppercased var content: String = "pork on the fork"
}

struct ContentView: View {
    @StateObject private var settings = TextSettings()
    
    var body: some View {
        VStack {
            Text(settings.content)
            Button("New string") {
                settings.content = "chicken on the knife"
            }
        }
        .padding()
    }
}

@Published会自动监听@Uppercased属性的变化,发送objectWillChange事件,SwiftUI收到事件后会更新视图。

额外说明
  • 当你在View中直接使用@Uppercased时,SwiftUI会将Property Wrapper视为View的一部分状态,内部的@State能被正确管理;但嵌套在值类型结构体中时,@State的生命周期脱离了View的管控,这也是状态变化无法被感知的次要原因。
  • 选择方案1还是方案2,取决于业务场景:如果嵌套结构体只是简单的状态容器,方案1更轻量;如果结构体需要复杂的状态逻辑或多个监听属性,方案2更适合。

内容的提问来源于stack exchange,提问作者Daniel Crompton

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.17 01:20:23