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 以关闭。
我正在尝试简化项目中的 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.
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 以关闭。