[英]SwiftUI ForEach with Array of SubClasses
我发现SwiftUI的ForEach (和List )有一个奇怪的问题,如果您使用父类实现BindableObject的子类类型数组,则ForEach循环坚持每个项目都是基类类型,而不是您正在使用的子类,请参阅下面的示例代码。 一些实验发现如果子类实现了BindableObject那么问题就会消失,在我展示的例子中是可以的,但通常并不合适。
看到这个的人知道你应该如何处理这个问题,或者这是一个错误,我应该向苹果提出这个问题?
class Bar: BindableObject {
let didChange = PassthroughSubject<Bar, Never>()
let name: String
init(name aName: String) {
name = aName
}
}
class Foo: Bar {
let value: Int
init(name aName: String, value aValue: Int) {
value = aValue
super.init(name:aName)
}
}
let arrayOfFoos: Array<Foo> = [ Foo(name:"Alpha",value:12), Foo(name:"Beta",value:13)]
struct ContentView : View {
var body: some View {
VStack {
ForEach(arrayOfFoos) { aFoo in
Text("\(aFoo.name) = \(aFoo.value)") // error aFoo is a Bar not a Foo
}
}
}
}
在 Xcode Beta 2 上试过这个
我认为这不是错误,而是 Swift 类型系统和 SwiftUI API 的“功能”。
如果您查看ForEach
的签名(只需 Cmd + 单击ForEach
)
public init(_ data: Data, content: @escaping (Data.Element.IdentifiedValue) -> Content)
你可以注意到它接受Data.Element.IdentifiedValue
类型
所以,从你的例子
struct ContentView : View {
var body: some View {
VStack {
ForEach(arrayOfFoos) { aFoo in
Text("\(aFoo.name) = \(aFoo.value)") // error aFoo is a Bar not a Foo
}
}
}
}
aFoo
本地值的类型为Foo.IdentifiedValue
让我们问问 Swift 对这种类型的看法:
Foo.IdentifiedValue.self == Bar.IdentifiedValue.self // true
Foo.IdentifiedValue.self == Foo.self // false
Foo.IdentifiedValue.self == Bar.self // true
如您所见, Foo.IdentifiedValue
实际上是Bar
。
为了绕过这个,我们可以使用 Swift 5.1 的一个新特性——'Key Path Member Lookup' 创建一个包装器! :D
我更新了你的例子。 新增AnyBindable
类的映射元素arrayOfFoos
它。
class Bar: BindableObject {
let didChange = PassthroughSubject<Void, Never>()
let name: String
init(name aName: String) {
name = aName
}
}
class Foo: Bar {
let value: Int
init(name aName: String, value aValue: Int) {
value = aValue
super.init(name:aName)
}
}
@dynamicMemberLookup
class AnyBindable<T: BindableObject>: BindableObject {
let didChange: T.PublisherType
let wrapped: T
init(wrapped: T) {
self.wrapped = wrapped
self.didChange = wrapped.didChange
}
subscript<U>(dynamicMember keyPath: KeyPath<T, U>) -> U {
return wrapped[keyPath: keyPath]
}
}
let arrayOfFoos = [ Foo(name:"Alpha",value:12), Foo(name:"Beta",value:13)]
.map(AnyBindable.init)
struct ContentView : View {
var body: some View {
VStack {
ForEach(arrayOfFoos) { aFoo in
Text("\(aFoo.name) = \(aFoo.value)") // it compiles now
}
}
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.