SwiftUI - 可重用组件,带有指向其他视图的链接作为参数

SwiftUI - reusable components with links to other views as parameters

我想在我的应用程序中创建可重用的组件。

我搜索过类似的问题。但我只找到了更复杂的例子。

让我们试试这个简单的例子——一个可以根据传递的参数打开不同视图的按钮。 我有 2 个视图,我将以 sheet:

打开

FirstView.swift

import SwiftUI

struct FirstView: View {
    var body: some View {
        Text("First view")
    }
}

SecondView.swift

struct SecondView: View {
    var body: some View {
        Text("Second view")
    }
}

ButtonView.swift 这是我想在我的设计系统中用作可重用组件的视图。

import SwiftUI

struct ButtonView: View {

    @State private var showModal: Bool = false
    
    // This works
    var text: String
    // Here I am getting an error:
    // Protocol 'View' can only be used as a generic constraint because it has Self or associated type requirements
    var link: View
    
    var body: some View {
        VStack {
            Spacer()
            Button(action: {
                self.showModal = true
            }) {
                Text(text)
                    .padding(20)
                    .foregroundColor(Color.white)
            }.sheet(isPresented: self.$showModal) {
                link
            }
            .background(Color.blue)
        }
    }
}

struct ButtonView_Previews: PreviewProvider {
    static var previews: some View {
        ButtonView(text: "TEST", link: FirstView())
    }
}

ContentView.swift 这里我尝试使用相同的按钮组件,但标签和链接不同。

import SwiftUI

struct ContentView: View {
    var body: some View {
        HStack {
            ButtonView(text: "first", link: FirstView())
                .padding()
            ButtonView(text: "second", link: SecondView())
                .padding()
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

传递字符串参数有效。标签不同。但我不能让它与不同视图的链接一起使用。我收到一个错误:

Protocol 'View' can only be used as a generic constraint because it has Self or associated type requirements

保持第一个视图和第二个视图相同,对 ButtonView 使用以下内容:

struct ButtonView<Content : View>: View {

    @State private var showModal: Bool = false
    
    var text: String
 
    // This is the generic content parameter
    let content: Content
    
    init(text: String, @ViewBuilder contentBuilder: () -> Content){
        self.text = text
        self.content = contentBuilder()
    }
    
    var body: some View {
        VStack {
            Spacer()
            Button(action: {
                self.showModal = true
            }) {
                Text(text)
                    .padding(20)
                    .foregroundColor(Color.white)
            }.sheet(isPresented: self.$showModal) {
                content
            }
            .background(Color.blue)
        }
    }
}

此处名为 content 的通用参数用于接收任何视图,初始化程序与 @ViewBuilder 属性 包装器一起使用以构建 view.Now 在 ContentView 结构中按以下方式使用它:

struct ContentView: View {
    var body: some View {
        HStack {
            ButtonView(text: "First") {
                FirstView()
            }
            ButtonView(text: "Second") {
                SecondView()
            }
        }
    }
}

它会很有魅力:)

此外,如果您想保留 ButtonView 的预览并且不希望它崩溃,请将预览添加为:

struct ButtonView_Previews: PreviewProvider {     
    static var previews: some View {         
       ButtonView(text: "First") {             
              FirstView()
        }
     }
 }