首页 > 解决方案 > 一般如何使用 iOS 14 单元格内容配置?

问题描述

我正在观看并重新观看 WWDC 2020“现代单元配置”,但启发性并不大。

我知道内置的 ListContentConfiguration 具有类似于文本标签和图像视图等内置单元格样式组件的属性。但我从不使用内置的单元格样式;我总是创建一个单元子类,并在 xib 或故事板原型单元中从头开始设计单元子视图,甚至在代码中构造它们。

那么如何使用 Apple 的配置来填充我的单元格的子视图?

视频说:

除了列表内容配置之外,我们还允许您访问实现所有呈现的关联列表内容视图。您只需使用配置创建或更新此视图,然后您可以将其作为子视图添加到您自己的自定义视图旁边。这使您可以利用所有内容配置功能并将 ListContentView 与您自己的附加自定义视图(例如额外的图像视图或标签)结合起来。

好吧,不,这不是我想做的。我不想要任何内置的单元格样式子视图。

然后视频说:

即使您在单元格内构建完全自定义的视图层次结构,您仍然可以使用系统配置来提供帮助。由于配置非常轻量,您可以将它们用作您复制到自定义视图的字体、颜色和边距等默认值的来源,即使您从未直接应用配置本身。对于更高级的用例,您可以创建一个完全自定义的内容配置类型,其中包含呈现它的配对内容视图类,然后将自定义配置与任何单元格一起使用,就像使用列表内容配置一样。

我的斜体,斜体是我要问的部分。我问:怎么做?

那么如何将它与我的自定义单元子类结合使用,将信息从数据源传递到单元并填充单元的子视图?

像往常一样,感觉就像 Apple 向我们展示了玩具示例,并完全省略了有关如何在现实世界中工作的细节。有没有人弄清楚这一点?

标签: iosuitableviewios14

解决方案


编辑我现在已经发表了一系列关于这个主题的文章,从https://www.biteinteractive.com/cell-content-configuration-in-ios-14/开始。


这里的关键——我认为 Apple 在视频中根本没有明确说明这一点——是这些单元配置的工作方式是从字面上撕掉单元contentView并将其替换为配置提供的视图作为输出它的makeContentView

因此,您所要做的就是手动构建整个内容视图,运行时会为您将其放入单元格中。

这是一个例子。我们需要提供自己的采用 UIContentConfiguration 的配置类型,这样我们就可以定义自己的属性;它还必须实现makeContentView()and updated(for:)。所以假设我们有四个文本要显示在单元格中:

struct Configuration : UIContentConfiguration {
    let text1: String
    let text2: String
    let text3: String
    let text4: String
    func makeContentView() -> UIView & UIContentView {
        let c = MyContentView(configuration: self)
        return c
    }
    func updated(for state: UIConfigurationState) -> MyCell.Configuration {
        return self
    }
}

在现实生活中,我们可能会通过更改返回此配置的某些属性更改的版本来响应状态更改,但在这种情况下,没有什么可做的,所以我们只是 return self

我们假设 MyContentView 的存在,这是一个采用 UIContentView 的 UIView 子类,意味着它有一个configuration属性。这是我们配置视图的子视图并应用配置的地方。在这种情况下,应用配置意味着只需设置四个标签的文本。我将这两个任务分开:

class MyContentView: UIView, UIContentView {
    var configuration: UIContentConfiguration {
        didSet {
            self.configure()
        }
    }
    private let lab1 = UILabel()
    private let lab2 = UILabel()
    private let lab3 = UILabel()
    private let lab4 = UILabel()
    init(configuration: UIContentConfiguration) {
        self.configuration = configuration
        super.init(frame: .zero)
        // ... configure the subviews ...
        // ... and add them as subviews to self ...
        self.configure()
    }
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    private func configure() {
        guard let config = self.configuration as? Configuration else { return }
        self.lab1.text = config.text1
        self.lab2.text = config.text2
        self.lab3.text = config.text3
        self.lab4.text = config.text4
    }
}

您可以看到该架构的重点。如果在未来某个时刻我们被分配了一个新的configuration,我们只需调用configure再次设置标签的文本,而无需重建子视图本身。在现实生活中,我们可以通过检查传入的配置来获得更高的效率;如果与当前配置相同,则无需self.configure()再次调用。

结果是我们现在可以在我们的tableView(_:cellForRowAt:)实现中这样说:

override func tableView(_ tableView: UITableView, 
    cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(
            withIdentifier: self.cellID, for: indexPath) as! MyCell
        let config = MyCell.Configuration(
            text1: "Harpo",
            text2: "Groucho",
            text3: "Chico",
            text4: "Zeppo"
        )
        cell.contentConfiguration = config
        return cell
}

所有这些都非常聪明,但不幸的是,内容视图界面似乎必须在代码中创建——我们无法从 nib 加载现成的单元格,因为从 nib 加载的内容视图及其所有子视图, 将被我们的makeContentView实现返回的内容视图替换。因此,Apple 的配置架构不能与您在情节提要或.xib文件中设计的单元格一起使用。很遗憾,但我看不到任何解决方法。


推荐阅读