简体   繁体   中英

SwiftUI clean up ContentView

I'm trying to simplify the ContentView within a project and I'm struggling to understand how to move @State based logic into its own file and have ContentView adapt to any changes. Currently I have dynamic views that display themselves based on @Binding actions which I'm passing the $binding down the view hierarchy to have buttons toggle the bool values.

Here's my current attempt. I'm not sure how in SwiftUI to change the view state of SheetPresenter from a nested view without passing the $binding all the way down the view stack. Ideally I'd like it to look like ContentView.overlay(sheetPresenter($isOpen, $present) .

Also, I'm learning SwiftUI so if this isn't the best approach please provide guidance.

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()
        }
    }
}

}

if you don't want to pass $binding all the way down the view you can create a StateObject variable in the top view and pass it with.environmentObject(). and access it from any view with EnvironmentObject

struct testApp: App {
        @StateObject var s1:  sViewModel = sViewModel()
        var body: some Scene {
            WindowGroup {
                ContentView()               
                    .environmentObject(s1)
            }
        }
    }

You are correct this is not the best approach, however it is a common mistake. In SwiftUI we actually use @State for transient data owned by the view. This means using a value type like a struct, not classes. This is explained at 4:18 in 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) { … }
        …
    }
}

Then you just use $editorConfig.isEditorPresented as the boolean binding in .sheet or .overlay .

Worth also taking a look at sheet(item:onDismiss:content:) which makes it much simpler to show an item because no boolean is required it uses an optional @State which you can set to nil to dismiss.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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