[英]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.