首页 > 解决方案 > 如何在 SwiftUI 中的任何视图中显示弹出窗口

问题描述

我正在使用 swiftui,并且正在开发大型应用程序。我有太多视图,但我想显示一个可以从应用程序内的任何视图中弹出的视图。我几乎不可能在每个 view.swift 上放置“.sheet”或“@State isPopUpShowing”。

有没有办法在一个文件中对我的弹出窗口进行一次编码,而不会弄乱每个视图文件?

标签: swiftswiftui

解决方案


ObservableObject您可以在视图层次结构的根目录中注入一个管理工作表(ContentView在我的示例中)并通过 using 传播它.environmentObject。然后,子视图将能够访问工作表管理器并从任何地方放置工作表。


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

为了稍微高级一点,因为子视图不需要响应工作表管理器的更新,您实际上可以将其设置为自定义环境键:

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

推荐阅读