代码之家  ›  专栏  ›  技术社区  ›  tech_human

将View作为视图修饰符内容传递给函数参数

  •  2
  • tech_human  · 技术社区  · 3 年前

    我正在创建一个可重复使用的自下而上面板,其中自下而上面板内部显示的视图将有所不同。我还希望这个面板成为视图修改器。我在过去创建过视图修改器,但从未将视图作为视图修改器内容传递。当我试图通过视图时,我会遇到下面描述的错误。

    查看修改后的代码:

    struct BottomPanel: ViewModifier {
        @Binding var isPresented: Bool
        let panelContent: Content
    
        init(isPresented: Binding<Bool>, @ViewBuilder panelContent: @escaping () -> Content) {
            self.panelContent = panelContent()
            self._isPresented = isPresented
        }
    
        func body(content: Content) -> some View {
            content.overlay(self.$isPresented.wrappedValue ? bottomPanelContent() : nil)
        }
    
        @ViewBuilder
        private func bottomPanelContent() -> some View {
            GeometryReader { geometry in
                VStack(spacing: 0) {
                    self.panelContent
                }
                // some modifiers to change the color or height of the panel.
            }
        }
    }
    

    视图扩展:

    extension View {
        func bottomPanel(isPresented: Binding<Bool>, @ViewBuilder panelContent: @escaping () -> BottomPanel.Content) -> some View {
            return modifier(BottomPanel(isPresented: isPresented, panelContent: panelContent)
        }
    }
    

    我希望在自下而上的面板中打开的内容视图和子视图:

    struct ContentView: View {
        @State var showBottomPanel: Bool = false
    
        var body: some View {
            VStack {
                Button(action: { self.showBottomPanel = true}) {
                    Text("Click me to open bottom panel")
                }
            }
            .bottomPanel(isPresented: $self.showBottomPanel, panelContent: { ChildView() })
        }
    }
    
    struct ChildView: View {
        var body: some View {
            VStack {
                Button("Click Me 1", action: {}).foregroundColor(.blue)
                Button("Click Me 2", action: {}).foregroundColor(.red)
            }
        }
    }
    

    错误: Cannot convert value of type 'ChildView' to closure result type 'BottomPanel.Content' (aka '_ViewModifier_Content<BottomPanel>')

    我做错了什么?如何将视图传递给BottomPanel?

    注意:我已经从底部面板中删除了很多代码,以保持代码发布的简短性,但如果需要,请告诉我,我可以分享。

    感谢阅读!

    1 回复  |  直到 3 年前
        1
  •  2
  •   rob mayoff    3 年前

    您写道:

        let panelContent: Content
    

    问题是 Content here is a special type defined by SwiftUI .这是一个类型擦除的容器,有点像 AnyView ,除非您无法创建自己的 所容纳之物 类型

    您需要为面板内容引入自己的泛型类型参数。您还应该存储 ViewBuilder 回调,创建面板内容,而不是在面板未显示时计算它。

    struct BottomPanel<PanelContent: View>: ViewModifier {
        @Binding var isPresented: Bool
        let panelContent: () -> PanelContent
    
        init(
            isPresented: Binding<Bool>,
            panelContent: @escaping () -> PanelContent
        ) {
            self.panelContent = panelContent
            self._isPresented = isPresented
        }
    
        func body(content: Content) -> some View {
            content.overlay(self.$isPresented.wrappedValue ? bottomPanelContent() : nil)
        }
    
        @ViewBuilder
        private func bottomPanelContent() -> some View {
            GeometryReader { geometry in
                VStack(spacing: 0) {
                    panelContent()
                }
                // some modifiers to change the color or height of the panel.
            }
        }
    }
    

    然后你需要更新你的 bottomPanel 修饰符也是通用的。在这种情况下,您可以使用不透明的参数语法,而不是添加显式 PanelContent 通用参数:

    extension View {
        func bottomPanel(
            isPresented: Binding<Bool>,
            @ViewBuilder panelContent: @escaping () -> some View
        ) -> some View {
            return modifier(BottomPanel(isPresented: isPresented, panelContent: panelContent))
        }
    }
    
        2
  •  0
  •   nickreps    3 年前

    我能够通过做一些更改来获得要呈现的内容。在不知情的情况下 怎样 内容是要看的,我不能确切地说这是否真的按照要求呈现了内容- 但它仍然提供了内容,避免了您试图绕过的错误

    import Foundation
    import SwiftUI
    
    struct ContentViewTest: View {
        @State var showBottomPanel: Bool = false
        
        var body: some View {
            VStack {
                Button(action: { self.showBottomPanel = true}) {
                    Text("Click me to open bottom panel")
                }
            }
            .bottomPanel(isPresented: $showBottomPanel, panelContent: { AnyView(ChildView()) }())
        }
    }
    
    struct ChildView: View {
        var body: some View {
            VStack {
                Button("Click Me 1", action: {}).foregroundColor(.blue)
                Button("Click Me 2", action: {}).foregroundColor(.red)
            }
        }
    }
    
    
    
    extension View {
        func bottomPanel(isPresented: Binding<Bool>, panelContent: AnyView) -> some View {
            return modifier(BottomPanel(isPresented: isPresented, panelContent: AnyView(panelContent)))
        }
    }
    
    
    struct BottomPanel: ViewModifier {
        @Binding var isPresented: Bool
        let panelContent: AnyView
        
        init(isPresented: Binding<Bool>, panelContent: AnyView) {
            self.panelContent = AnyView(panelContent)
            self._isPresented = isPresented
        }
        
        func body(content: Content) -> some View {
            content.overlay(self.$isPresented.wrappedValue ? bottomPanelContent() : nil)
        }
        
        @ViewBuilder
        private func bottomPanelContent() -> some View {
            GeometryReader { geometry in
                VStack(spacing: 0) {
                    self.panelContent
                }
                // some modifiers to change the color or height of the panel.
            }
        }
    }