繁体   English   中英

SwiftUI:根据环境初始化 ObservableObject

[英]SwiftUI: Init ObservableObject based on Environment

在此示例中,蓝色矩形最初应在 .regular 大小 class 的设备上可见,而在.regular大小 class 的设备上.compact隐藏。

我正在使用一个名为SettingsObservableObject@Published变量isVisible来管理矩形的可见性。 我的问题是我不知道如何从ContentView使用正确的horizontalSizeClass ntalSizeClass 初始化Settings 现在我正在使用.onAppear来更改isVisible的值,但这会触发.onReceive 在紧凑型设备上,这会导致矩形在视图呈现时可见并淡出,而不是立即不可见。

如何根据 Horizo horizontalSizeClass等环境值初始化Settings ,以便isVisible从一开始就正确?

struct ContentView: View {
    @Environment(\.horizontalSizeClass) var horizontalSizeClass
    @StateObject var settings = Settings()
    @State var opacity: CGFloat = 1

    var body: some View {
        VStack {
            Button("Toggle Visibility") {
                settings.isVisible.toggle()
            }
            .onReceive(settings.$isVisible) { _ in
                withAnimation(.linear(duration: 2.0)) {
                    opacity = settings.isVisible ? 1 : 0
                }
            }
            Rectangle()
                .frame(width: 100, height: 100)
                .foregroundColor(.blue)
                .opacity(opacity)
        }
        .onAppear {
            settings.isVisible = horizontalSizeClass == .regular // too late
        }
    }
}

class Settings: ObservableObject {
    @Published var isVisible: Bool = true // can't get size class here
}

矩形在开始时不应该是可见的:

在此处输入图像描述

withAnimation应该在更改 state 的按钮操作上完成,例如

import SwiftUI

struct RectangleTestView: View {

    @Environment(\.horizontalSizeClass) var horizontalSizeClass
    @State var settings = Settings()
    
    var body: some View {
        VStack {
            Button("Toggle Visibility") {
                withAnimation(.linear(duration: 2.0)) {
                    settings.isVisible.toggle()
                }
            }
            Rectangle()
                .frame(width: 100, height: 100)
                .foregroundColor(.blue)
                .opacity(settings.opacity)
        }
        .onAppear {
            settings.isVisible = horizontalSizeClass == .regular
        }
    }
}

struct Settings {
    var isVisible: Bool = true
    var opacity: CGFloat {
        isVisible ? 1 : 0
    }
}

仅供参考,我们不再使用onReceive ,因为他们添加了onChange 最好将视图数据保留在结构中,而不是将其移动到昂贵的对象中。 “视图非常便宜,我们鼓励您将它们作为主要封装机制” SwiftUI WWDC 2020 中的 Data Essentials 20:50。

我们只需要执行依赖注入(已知环境是父环境,因此可以很容易地注入子环境),并且使用内部视图很简单,例如

struct ContentView: View {
    @Environment(\.horizontalSizeClass) var horizontalSizeClass

    struct MainView: View {
        // later knonw injection
        @EnvironmentObject var settings: Settings

        var body: some View {
            VStack {
                Button("Toggle Visibility") {
                    settings.isVisible.toggle()
                }
                Rectangle()
                    .frame(width: 100, height: 100)
                    .foregroundColor(.blue)
                    .opacity(settings.isVisible ? 1 : 0) // << direct dependency !!
            }
            .animation(.linear(duration: 2.0), value: settings.isVisible) // << explicit animation
        }
    }

    var body: some View {
        MainView()          // << internal view
            .environmentObject(
                Settings(isVisible: horizontalSizeClass == .regular) // << initial injecttion !!
            )
    }
}

使用 Xcode 13.4 / iOS 15.5 测试

演示

测试代码在这里

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM