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)
}
}
}
我有这个简单的 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 notLabel
because I need to use the SwiftUILabel
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)
}
}
}