简体   繁体   中英

How to show popup from any view in SwiftUI

I'm using swiftui and I'm working on big app. I have too many views, but I want to show a view that can pop up from any views within the app. It's almost impossible for me to put ".sheet" or "@State isPopUpShowing" on every view.swift.

Is there any way to code my popup once/in one file without messing with every single view file?

You could inject an ObservableObject that manages the sheets in the root of your view hierarchy ( ContentView in my example) and propagate it through using .environmentObject . Then, child views will be able to access the sheet manager and put up a sheet from anywhere.


class SheetManager : ObservableObject {
    struct SheetItem : Identifiable {
        var id = UUID()
        var message : String
    }
    
    @Published var sheetItem : SheetItem?
    
    @ViewBuilder func sheetForItem(item: SheetItem) -> some View {
        Text(item.message)
    }
    
    func buildAndShowSheet(message : String) {
        sheetItem = SheetItem(message: message)
    }
}

struct ContentView: View {
    @StateObject private var sheetManager = SheetManager()
    
    var body: some View {
        VStack {
            Text("Hello, world!")
            ChildView()
        }
        .sheet(item: $sheetManager.sheetItem) { item in
            sheetManager.sheetForItem(item: item)
        }
        .environmentObject(sheetManager)
    }
}

struct ChildView: View {
    @EnvironmentObject private var sheetManager : SheetManager
    
    var body: some View {
        VStack {
            Text("Child view that triggers sheet")
            Button("Open sheet") {
                sheetManager.buildAndShowSheet(message: "Sheet is open")
            }
        }
    }
}

To get slightly more advanced, because the child view doesn't need to respond to updates from the sheet manager, you could actually make it just a custom environment key:

private struct SheetManagerKey: EnvironmentKey {
    static let defaultValue = SheetManager()
}

extension EnvironmentValues {
    var sheetManager: SheetManager {
        get { self[SheetManagerKey.self] }
        set { self[SheetManagerKey.self] = newValue }
    }
}

class SheetManager : ObservableObject {
    struct SheetItem : Identifiable {
        var id = UUID()
        var message : String
    }
    
    @Published var sheetItem : SheetItem?
    
    @ViewBuilder func sheetForItem(item: SheetItem) -> some View {
        Text(item.message)
    }
    
    func buildAndShowSheet(message : String) {
        sheetItem = SheetItem(message: message)
    }
}

struct ContentView: View {
    @StateObject private var sheetManager = SheetManager()
    
    var body: some View {
        VStack {
            Text("Hello, world!")
            ChildView()
        }
        .sheet(item: $sheetManager.sheetItem) { item in
            sheetManager.sheetForItem(item: item)
        }
        .environment(\.sheetManager, sheetManager)
    }
}

struct ChildView: View {
    @Environment(\.sheetManager) private var sheetManager
    
    var body: some View {
        VStack {
            Text("Child view that triggers sheet")
            Button("Open sheet") {
                sheetManager.buildAndShowSheet(message: "Sheet is open")
            }
        }
    }
}

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