【问题标题】:SwiftUI ForEach with Array of SubClasses带有子类数组的 SwiftUI ForEach
【发布时间】:2019-11-07 05:18:12
【问题描述】:

我发现 SwiftUIForEach(和 List)有一个奇怪的问题,如果您使用子类类型的数组,其中父类实现 BindableObjectForEach 循环坚持每个项目都是基类类型而不是您正在使用的子类,请参阅下面的示例代码。一个小实验发现如果子类实现 BindableObject 然后问题就消失了,在我展示的示例中是可以的,但通常不是很合适。

任何人看到这个知道你应该如何处理这个,或者这可能是一个错误,我应该向 Apple 提出它?

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
      }
    }
  }
}

【问题讨论】:

    标签: swift inheritance foreach swiftui


    【解决方案1】:

    在 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
                }
            }
        }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-11
      • 2012-12-26
      • 1970-01-01
      • 2020-10-04
      • 2022-11-15
      • 1970-01-01
      相关资源
      最近更新 更多