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

SwiftUI使用EnvironmentObject时出现运行时问题的原因咨询

问题成因解析:@ScaledMetric在ObservableObject中触发运行时警告

核心原因:@ScaledMetric的运行依赖View上下文

@ScaledMetric是SwiftUI专为View设计的属性包装器,它的核心功能(响应系统动态字体大小、屏幕缩放变化)完全依赖当前View所在的Environment环境。只有当它被定义在View结构体内部时,SwiftUI才会自动将该View的环境上下文绑定给它,让它能实时感知系统设置变化并更新值。

你的代码里,@ScaledMetric被放在了普通结构体Size中,而Size又被ObservableObject类Style持有。ObservableObject是独立于View生命周期的对象,它不属于任何View的上下文环境,所以当你在TestView的body中访问style.size.inputPadding时,@ScaledMetric找不到它需要的View环境支持,就会抛出警告,并且此时它只能返回默认值,完全无法响应系统设置的变化。

为什么会被判定为「在View外部访问」?

虽然你是在TestView的body里写的代码,但实际读取的是ObservableObject实例中的Size结构体属性。Size不是View,它的@ScaledMetric属性没有被SwiftUI的View环境托管,相当于脱离了View的上下文去访问依赖环境的属性,因此系统判定为「在View外部访问」。

旧版本无异常的原因

Xcode 14.1之前的SwiftUI版本没有严格校验@ScaledMetric的使用上下文,所以没有抛出这个警告,但底层逻辑一直如此——当时inputPadding同样不会响应系统动态字体/缩放的变化,只是问题没有被显性暴露出来。

修复方案参考

方案1:将@ScaledMetric移至View内部

把缩放相关的属性直接放在View里,让它处于正确的上下文环境中:

struct TestView: View {
    @EnvironmentObject private var style: Style
    @ScaledMetric(relativeTo: .body) private var inputPadding: CGFloat = 8

    public var body: some View {
        Text("Hello World")
            .padding(inputPadding)
    }
}

方案2:让Style绑定View环境动态更新

如果需要在全局Style中统一管理缩放值,可以在View中监听环境变化并同步给Style:

class Style: ObservableObject {
    @Published var inputPadding: CGFloat = 8
}

struct TestView: View {
    @EnvironmentObject private var style: Style
    @Environment(\.dynamicTypeSize) private var dynamicTypeSize

    public var body: some View {
        Text("Hello World")
            .padding(style.inputPadding)
            .onChange(of: dynamicTypeSize) { newSize in
                // 根据动态类型大小计算对应的padding值
                style.inputPadding = 8 * newSize.scalingFactor
            }
    }
}

内容的提问来源于stack exchange,提问作者Mojtaba Hosseini

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.09 08:35:20