在 returns 某些视图 (SwiftUI) 的闭包内声明一个临时变量或常量
Declare a temporary variable or constant inside a closure that returns some View (SwiftUI)
我正在构建一个基于 SwiftUI 的视图,我想在 returns some View
的闭包中存储一个临时值(以便它可以多次使用)。编译器给我以下错误:
Unable to infer complex closure return type; add explicit type to disambiguate
struct GameView: View {
@State private var cards = [
Card(value: 100),
Card(value: 20),
Card(value: 80),
]
var body: some View {
MyListView(items: cards) { card in // Error is on this line, at the starting curly brace
let label = label(for: card)
return Text(label)
}
}
func label(for card: Card) -> String {
return "Card with value \(card.value)"
}
}
struct MyListView<Item: Identifiable, ItemView: View>: View {
let items: [Item]
let content: (Item) -> ItemView
var body: some View {
List {
ForEach(items) { item in
content(item)
}
}
}
}
struct Card: Identifiable {
let value: Int
let id = UUID()
}
如果我内联对 label(for:)
方法的调用,构建就会成功。显然,上面的例子是我对该问题的简化再现。在我的实际应用程序中,我试图存储该方法的 return 值,因为在为单个项目创建视图时它被多次使用,并且该操作需要在我的模型中进行可能昂贵的评估。多次调用该方法很浪费。
几个注意事项:
- 传递给
MyListView
的 content
闭包是 而不是 一个 @ViewBuilder
,但即使是,我想使用 let
像我做的应该没问题。
- 当闭包包含单个表达式时,我不依赖于隐式
return
- 我添加了自己的显式 return
.
- 我是否使用声明的变量并不重要。仅仅语句的存在就会扰乱编译器。
如何编写此代码,以便我不必多次调用可能昂贵的方法?有人可以解释 language/syntax 级别发生了什么导致错误吗?
不幸的是,这对 Swift 来说太复杂了,但有几个解决方案:
首先,您可以手动声明它是什么功能:
MyListView(items: cards) { (card: Card) -> Text in
let label = label(for: card)
return Text(label)
}
或者您需要使用@ViewBuilder 的强大功能来使其工作。因此,我有2个同等质量的工作建议
- 使用
Group
:
var body: some View {
MyListView(items: cards) { card in
Group {
let label = label(for: card)
Text(label)
}
}
}
- 将 func 与
@ViewBuilder
标签一起使用
@ViewBuilder func cardView(card: Card) -> some View {
let label = label(for: card)
Text(label)
}
var body: some View {
MyListView(items: cards, content: cardView)
}
此外,您可以简化第二个示例而不使用 ViewBuilder,因为您可以手动说出您将 return 文本,例如:
func cardView(card: Card) -> Text {
let label = label(for: card)
return Text(label)
}
这是由于 Swift 编译器仅尝试推断闭包的 return 类型(如果它是单个表达式)的限制。由结果生成器处理的闭包,例如 @ViewBuilder
,不受此限制。 重要的是,此限制也不影响功能(仅影响闭包)。
我能够通过将闭包移动到结构内部的方法来完成这项工作。注意:这与@cluelessCoder 的第二个解决方案相同,只是排除了 @ViewBuilder
属性。
struct GameView: View {
@State private var cards = [
Card(value: 100),
Card(value: 20),
Card(value: 80),
]
var body: some View {
MyListView(items: cards, content: cardView)
}
func cardView(for card: Card) -> some View {
let label = label(for: card) // only called once, and can be reused.
return Text(label)
}
func label(for card: Card) -> String {
return "Card with value \(card.value)"
}
}
感谢@cluelessCoder。如果没有他们的意见和有用的答案,我永远不会偶然发现这个发现。
我正在构建一个基于 SwiftUI 的视图,我想在 returns some View
的闭包中存储一个临时值(以便它可以多次使用)。编译器给我以下错误:
Unable to infer complex closure return type; add explicit type to disambiguate
struct GameView: View {
@State private var cards = [
Card(value: 100),
Card(value: 20),
Card(value: 80),
]
var body: some View {
MyListView(items: cards) { card in // Error is on this line, at the starting curly brace
let label = label(for: card)
return Text(label)
}
}
func label(for card: Card) -> String {
return "Card with value \(card.value)"
}
}
struct MyListView<Item: Identifiable, ItemView: View>: View {
let items: [Item]
let content: (Item) -> ItemView
var body: some View {
List {
ForEach(items) { item in
content(item)
}
}
}
}
struct Card: Identifiable {
let value: Int
let id = UUID()
}
如果我内联对 label(for:)
方法的调用,构建就会成功。显然,上面的例子是我对该问题的简化再现。在我的实际应用程序中,我试图存储该方法的 return 值,因为在为单个项目创建视图时它被多次使用,并且该操作需要在我的模型中进行可能昂贵的评估。多次调用该方法很浪费。
几个注意事项:
- 传递给
MyListView
的content
闭包是 而不是 一个@ViewBuilder
,但即使是,我想使用let
像我做的应该没问题。 - 当闭包包含单个表达式时,我不依赖于隐式
return
- 我添加了自己的显式return
. - 我是否使用声明的变量并不重要。仅仅语句的存在就会扰乱编译器。
如何编写此代码,以便我不必多次调用可能昂贵的方法?有人可以解释 language/syntax 级别发生了什么导致错误吗?
不幸的是,这对 Swift 来说太复杂了,但有几个解决方案:
首先,您可以手动声明它是什么功能:
MyListView(items: cards) { (card: Card) -> Text in
let label = label(for: card)
return Text(label)
}
或者您需要使用@ViewBuilder 的强大功能来使其工作。因此,我有2个同等质量的工作建议
- 使用
Group
:
var body: some View {
MyListView(items: cards) { card in
Group {
let label = label(for: card)
Text(label)
}
}
}
- 将 func 与
@ViewBuilder
标签一起使用
@ViewBuilder func cardView(card: Card) -> some View {
let label = label(for: card)
Text(label)
}
var body: some View {
MyListView(items: cards, content: cardView)
}
此外,您可以简化第二个示例而不使用 ViewBuilder,因为您可以手动说出您将 return 文本,例如:
func cardView(card: Card) -> Text {
let label = label(for: card)
return Text(label)
}
这是由于 Swift 编译器仅尝试推断闭包的 return 类型(如果它是单个表达式)的限制。由结果生成器处理的闭包,例如 @ViewBuilder
,不受此限制。 重要的是,此限制也不影响功能(仅影响闭包)。
我能够通过将闭包移动到结构内部的方法来完成这项工作。注意:这与@cluelessCoder 的第二个解决方案相同,只是排除了 @ViewBuilder
属性。
struct GameView: View {
@State private var cards = [
Card(value: 100),
Card(value: 20),
Card(value: 80),
]
var body: some View {
MyListView(items: cards, content: cardView)
}
func cardView(for card: Card) -> some View {
let label = label(for: card) // only called once, and can be reused.
return Text(label)
}
func label(for card: Card) -> String {
return "Card with value \(card.value)"
}
}
感谢@cluelessCoder。如果没有他们的意见和有用的答案,我永远不会偶然发现这个发现。