我在使用 新的“存在
any
”更改来使 Swift 5.7+ 中的存在变量保持一致时遇到了一些麻烦。
由于这里的Animal
总是符合
Hashable
,我想
any Animal
也会have符合
Hashable
。但是,我看到的错误让我产生了不同的想法。有人可以帮助解释为什么会发生这种情况吗?如果可能的话,请帮助我解决它?
import SwiftUI
import PlaygroundSupport
protocol Animal: Hashable {
var name: String { get }
}
struct Cow: Animal {
let name: String
}
struct Chicken: Animal {
let name: String
}
let anAnimalList: [any Animal] = [Cow(name: "Aaron"), Chicken(name: "Billy"), Cow(name: "Charlie"), Chicken(name: "Delilah")]
struct myView: View {
@State private var anAnimal: (any Animal)?
var body: some View {
VStack {
List(anAnimalList, id: \.self, selection: $anAnimal) { animal in // ERROR: Type 'any Animal' cannot conform to 'Hashable'
Text("Animal")
}
Text("Animal is \(anAnimal?.name ?? "Null")")
}
}
}
PlaygroundPage.current.setLiveView(myView())
[any Animal]
,而不是“为什么会发生这种事”:)(请参阅相应的 WWDC22 会议以找到第二个会议的答案)使用 Xcode 14b3 / iOS 16 进行测试
我们跳进这个洞吧...
原代码有两个地方报错:
List(anAnimalList,
id: \.self, // << 1st place
selection: $anAnimal // << 2nd place
) { animal in
Text("Animal")
}
第一名 - 因为Animal
不可识别,并且
self
涉及具体类型的实例,所以我们不应该使用
\.self
(因为编译器将无法推断具体类型),而是直接通过进行识别
\.id
因此,这是第一个问题的可能解决方案:
protocol Animal: Hashable, Identifiable {
var id: UUID { get }
var name: String { get }
}
struct Cow: Animal {
let id = UUID()
let name: String
}
struct Chicken: Animal {
let id = UUID()
let name: String
}
var body: some View {
VStack {
List(anAnimalList, id: \.id) { animal in // << no error !!
Text("Animal: \(animal.name)")
}
}
}
第二名 - 现在进行选择;故事是一样的,Swift 应该知道“具体的选择类型”或者能够“从代码中推断出来”,这两者在编译类型上都是不可能的,所以不要试图用头打破墙壁,让我们找到一扇门。 .
一个可能的解决方案是使用上下文中已知且具体的东西来进行选择...我们刚刚在上面添加了它 - 'id'所以这是第二部分的修复
@State private var anAnimalID: UUID? // << here !!
var body: some View {
VStack {
List(anAnimalList, id: \.id, selection: $anAnimalID) { animal in
Text("Animal: \(animal.name)")
}
if let animal = anAnimalList.first { $0.id == anAnimalID } {
Text("Animal is \(animal.name)")
}
}
}