【问题标题】:SwiftUI - Selecting the correct View type based on API fieldSwiftUI - 根据 API 字段选择正确的视图类型
【发布时间】:2020-07-23 05:58:47
【问题描述】:

抱歉,如果这个问题有点抽象,但在这里:

API 返回由几个简单字段组成的资源列表。其中一个字段(示例中的“view-id”)告诉客户端在呈现数据时使用哪个 SwiftUI View

[
  {
    "title": "Example Title"
    "image": "https://www.example.com/some/image.png"
    "view-id": "text-row" // tells the client to use, say, `TextRowView`
  },
  {
    "title": "A Second Title"
    "image": "https://www.example.com/some/other/image.png"
    "view-id": "image-row" // tells the client to use, say, `ImageRowView`
  }
]

我的目标是找到一种可扩展的方式,以允许在运行时根据 API 中的 view-id 选择 View 类型。

为了解决这个问题,我定义了以下协议来描述可以创建视图的类型

protocol MyViewBuilder {
    
    associatedtype Content: View
    
    func buildView(data: Data) -> Content
}

/// concrete implementation of a `MyViewBuilder`
struct MyExampleViewBuilder {
    
    func buildView(data: Data) -> Text {
        Text(data.string)
    }
}

我已经定义了以下协议来将 API 中的“view-id”映射到特定类型的视图:

protocol MyViewDescriptor {

    associatedtype Content: View
    
    var id: String { get }
}

/// concrete implementation for a `MyViewDescriptor`
struct MyExampleDescriptor {

    associatedtype Content: Text
    
    var id: String = "text-row"
} 

现在,我构建了一个允许注册和访问这些片段的类型,如下所示:

class MyGlueCode: MyViewBuilder {
    func register<D, B>(_ descriptor: D, viewBuilder: B) where D: MyViewDescriptor,
                                                                   B: MyViewBuilder,
                                                                   D.Content == B.Content {...}


    func buildView<D, Content>(descriptor: D, data: Data) -> Content where D: MyViewDescriptor, Content == D.Content { ... }

    
}

现在上面的代码可以通过一点类型擦除来工作,但我正在努力的是如何为buildView(descriptor:data:) 保留/生成“描述符”参数。但我无法设计出一种方法让这个位直接在呼叫站点,理想情况下我可以做一些简单的事情,比如

ForEach(data) { 
   SomeClass().view(for: $0)
}

我被卡住了:(救命!

【问题讨论】:

    标签: ios swift generics swiftui


    【解决方案1】:

    这是可能的方法。使用 Xcode 11.4 / iOS 13.4 测试

    enum ViewType: String {
        case text = "text-row"
        case image = "image-row"
    
        static func view(for data: Data) -> some View {
            let type = Self(rawValue: data.viewId)
    
            return Group {
                if type == nil {
                    Text("Unsupported View Type")
                } else if type == .text {
                    TexDataView(data: data)
                } else if type == .image {
                    ImageDataView(data: data)
                }
            }
        }
    }
    
    struct DemoDataView: View {
        let data: Data
        var body: some View {
            ViewType.view(for: data)
        }
    }
    

    假设复制

    struct Data {
        let title: String
        let image: String
        let viewId: String
    }
    

    【讨论】:

      猜你喜欢
      • 2019-10-23
      • 1970-01-01
      • 2021-08-14
      • 1970-01-01
      • 2014-08-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多