繁体   English   中英

带有子类数组的 SwiftUI ForEach

[英]SwiftUI ForEach with Array of SubClasses

我发现SwiftUIForEach (和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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM