SwiftUI 扩展通用 where 子句不匹配

SwiftUI extension generic where clause not matching

我有这个简单的 ThemedNavigationButton 视图,它在创建 NavigationLink 的同时处理一些东西(内部工作并不重要):

struct ThemedNavigationButton<Destination, L>: View where Destination: View, L: View {
    var destination: () -> Destination
    var label: () -> L
    
    var body: some View {
        ...
    }
}

I use L here and not Label because I need to use the SwiftUI Label next

我是这样使用的:

ThemedNavigationButton {
    NextView()
} label: {
    Label {
        Text("Some text")
    } icon: {
        Image(systemName: "check")
            .foregroundColor(theme.tint)
    }
}

我想在以这种方式使用时创建一个更简单的初始化器,所以我想出了这个:

extension ThemedNavigationButton where L == Label<Text, Image> {
    
    init(text: String, systemImage: String, destination: @escaping () -> Destination) {
        self.destination = destination
        self.label = {
            Label {
                Text(text + text)
            } icon: {
                Image(systemName: systemImage)
            }
        }
    }
}

像这样效果很好:

ThemedNavigationButton(text: "Some text", systemImage: "check") { NextView() }

我遇到的问题是,一旦我将图像色调颜色添加到新的初始化程序中,我就会收到错误消息:

Cannot convert value of type 'some View' to closure result type 'Image'

我猜是因为我的 Image 不再是 Image。但它是什么以及我如何声明它。我不能使用 some View,这是编译器告诉我的。

要使用以下表示法:

ThemedNavigationButton(text: "Some text", systemImage: "check") { NextView() }

您可以为 Destination 创建一个只有一种通用类型的 View,因为 Label 将接收基本的 String 类型。

您可以设置ThemedNavigationButton如下:

// Only one generic type needed
struct ThemedNavigationButton<Destination: View>: View {
    
    // Constants for the label (make them appear before "destination")
    let text: String
    let systemImage: String
    
    // Destination view
    var destination: () -> Destination

    var body: some View {
        
        // Show the views the way you want
        VStack {
            destination()
            
            // Use the label this way
            Label {
                Text(text)
            } icon: {
                Image(systemName: systemImage)
            }
        }
    }
}

根据需要自定义 body。 您可以使用它调用:

ThemedNavigationButton(text: "Some text", systemImage: "check") { NextView() }

泛型专业化需要具体类型,因此这里有一种解决这种情况的可能方法 - 引入自定义 wrapper/proxy 类型并在扩展中使用它。

使用 Xcode 13.2

测试
struct MyLabel: View {     // new wrapper type
    let text: String
    let systemImage: String
    var tintColor = Color.green
    var body: some View {
        Label {
             Text(text + text)
        } icon: {
             Image(systemName: systemImage)
                .foregroundColor(tintColor)
        }
    }
}

extension ThemedNavigationButton where L == MyLabel {   // << here !!
    init(text: String, systemImage: String, destination: @escaping () -> Destination) {
        self.destination = destination
        self.label = {
            MyLabel(text: text, systemImage: systemImage)
        }
    }
}