SwiftUI 清理 ContentView

SwiftUI clean up ContentView

我正在尝试简化项目中的 ContentView,并且我正在努力了解如何将基于 @State 的逻辑移动到它自己的文件中并让 ContentView 适应任何更改。目前我有基于 @Binding 操作显示自己的动态视图,我将 $binding 向下传递到视图层次结构以使按钮切换 bool 值。

这是我目前的尝试。我不确定在 SwiftUI 中如何从嵌套视图更改 SheetPresenter 的视图状态,而不将 $binding 一直传递到视图堆栈。理想情况下,我希望它看起来像 ContentView.overlay(sheetPresenter($isOpen, $present).

另外,我正在学习 SwiftUI,所以如果这不是最好的方法,请提供指导。

class SheetPresenter: ObservableObject {

@Published var present: Present = .none
@State var isOpen: Bool = false

enum Present {
    case none, login, register
}

@ViewBuilder
func makeView(with presenter: Present) -> some View {
    switch presenter {
    case .none:
        EmptyView()
    case .login:
        BottomSheetView(isOpen: $isOpen, maxHeight: UIConfig.Utils.screenHeight * 0.75) {
            LoginScreen()
        }
    case .register:
        BottomSheetView(isOpen: $isOpen, maxHeight: UIConfig.Utils.screenHeight * 0.75) {
            RegisterScreen()
        }
    }
}

}

如果您不想在视图中一直传递 $binding,您可以在顶部视图中创建一个 StateObject 变量并使用 .environmentObject() 传递它。并使用 EnvironmentObject

从任何视图访问它
struct testApp: App {
        @StateObject var s1:  sViewModel = sViewModel()
        var body: some Scene {
            WindowGroup {
                ContentView()               
                    .environmentObject(s1)
            }
        }
    }

你是对的,这不是最好的方法,但却是一个常见的错误。在 SwiftUI 中,我们实际上使用 @State 来表示视图拥有的瞬态数据。这意味着使用像结构这样的值类型,而不是 类。 Data Essentials in SwiftUI from WWDC 2020.

中的 4:18 对此进行了解释

EditorConfig can maintain invariants on its properties and be tested independently. And because EditorConfig is a value type, any change to a property of EditorConfig, like its progress, is visible as a change to EditorConfig itself.

struct EditorConfig {
    var isEditorPresented = false
    var note = ""
    var progress: Double = 0
    mutating func present(initialProgress: Double) {
        progress = initialProgress
        note = ""
        isEditorPresented = true
    }
}
struct BookView: View {
    @State private var editorConfig = EditorConfig()
    func presentEditor() { editorConfig.present(…) }
    var body: some View {
        …
        Button(action: presentEditor) { … }
        …
    }
}

然后您只需使用 $editorConfig.isEditorPresented 作为 .sheet.overlay 中的布尔绑定。

还值得一看 sheet(item:onDismiss:content:),它使显示项目变得更加简单,因为它不需要布尔值,它使用可选的 @State,您可以将其设置为 nil 以关闭。