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

SwiftUI如何使用ObservedObject或EnvironmentObject存储GeometryReader缩放数据

问题判断说明

你提到的「视图加载完成后不能再声明ObservedObject或EnvironmentObject」的判断是错误的。你可以提前在App根节点声明ObservableObject对象,在GeometryReader拿到尺寸后更新对象内的属性,通过响应式更新同步到所有子视图。

实现方案

步骤1:定义可观测的Scale存储类

首先创建继承自ObservableObject的存储类,用来存储全局的Scale数据,支持响应式更新:

import SwiftUI

class ScaleStore: ObservableObject {
    // 全局屏幕尺寸对应的Scale
    @Published var globalScale: Scale? = nil
    // 可按需新增字段存储不同嵌套场景的局部Scale数据
    @Published var nestedCardScale: Scale? = nil
}

原有Scale结构体无需修改,可以直接复用。

步骤2:在App入口注入ScaleStore

在App根节点初始化ScaleStore并通过环境变量注入到整棵视图树:

@main
struct ScaleDemoApp: App {
    @StateObject private var scaleStore = ScaleStore()
    var body: some Scene {
        WindowGroup {
            GeometryReaderSampleView()
                .environmentObject(scaleStore)
        }
    }
}

步骤3:在GeometryReader中更新Scale数据

修改根视图的GeometryReader逻辑,拿到尺寸后更新ScaleStore的对应属性,同时适配屏幕旋转、分屏等尺寸变化场景:

struct GeometryReaderSampleView: View {
    @EnvironmentObject var scaleStore: ScaleStore
    var body: some View {
        NavigationView {
            GeometryReader { metrics in
                ZStack {
                    VStack {
                        LoginMainDecorationView()
                        Spacer()
                    }
                    
                    VStack {
                        HStack {
                            GreenSquareView()
                            Spacer()
                        }
                        .offset(x: 29, y: scaleStore.globalScale?.vertical(300.0) ?? 0)
                        Spacer()
                    }
                }
                .onAppear {
                    scaleStore.globalScale = Scale(geometry: metrics)
                }
                .onChange(of: metrics.size) { newSize in
                    scaleStore.globalScale = Scale(screenWidth: newSize.width, screenHeight: newSize.height)
                }
            }
        }
    }
}

步骤4:子视图直接获取Scale,无需传参

子视图可以删除原来的Scale属性和init传参逻辑,直接通过环境变量获取Scale数据:

struct GreenSquareView: View {
    @EnvironmentObject var scaleStore: ScaleStore
    // 增加默认值兼容加载阶段、预览场景的空值问题
    private var scale: Scale { scaleStore.globalScale ?? Scale(screenWidth: 375, screenHeight: 734) }
    
    var body: some View {
        ZStack(alignment: .topLeading) {
            RoundedRectangle(cornerRadius: scale.horizontal(30))
                .fill(Color.green)
                .frame(width: scale.horizontal(157), height: scale.horizontal(146))
            
            Text("Here goes\nsome text")
                .font(.custom("TimesNewRomanPS-ItalicMT", size: scale.horizontal(20)))
                .padding(.top, scale.horizontal(29))
                .padding(.leading, scale.horizontal(19))
            
            VStack {
                Spacer()
                HStack {
                    Spacer()
                    Image(systemName: "heart.circle")
                        .resizable()
                        .frame(width: scale.horizontal(20), height: scale.horizontal(20))
                        .offset(x: scale.horizontal(-20), y: scale.vertical(-17.0))
                }
            }.frame(width: scale.horizontal(157), height: scale.horizontal(146))
        }
    }
}
嵌套子视图尺寸获取方案

如果需要获取嵌套在父视图内部的子视图尺寸,更推荐使用自定义环境键的方案,无需额外维护ObservableObject,性能更优:

// 定义自定义环境键
extension EnvironmentValues {
    var localScale: Scale {
        get { self[LocalScaleKey.self] }
        set { self[LocalScaleKey.self] = newValue }
    }
}

private struct LocalScaleKey: EnvironmentKey {
    static let defaultValue: Scale = Scale(screenWidth: 375, screenHeight: 734)
}

// 父视图内获取局部尺寸并注入
struct CardContainerView: View {
    var body: some View {
        GeometryReader { metrics in
            VStack {
                CardChildView1()
                CardChildView2()
            }
            // 局部注入该视图树专属的Scale
            .environment(\.localScale, Scale(geometry: metrics))
        }
        .frame(width: 300, height: 200)
    }
}

// 子视图直接获取局部Scale
struct CardChildView1: View {
    @Environment(\.localScale) var scale
    var body: some View {
        Text("局部缩放文本")
            .font(.system(size: scale.horizontal(16)))
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.04 01:09:02