簡體   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