Bento 0.4

Bento 0.4

sergdortDavid RodriguesYasuhiro InamiAnders HaAdam Borek 维护。



Bento 0.4

  • Babylon iOS

Bento🍱弁当

是一种常见的日式食盒食品,通常是将米饭或面条、鱼或肉、腌菜和熟蔬菜放在一起。

Bento 是一个 Swift 库,用于在 UITableView 上构建基于组件的界面。

  • 声明式:提供一种简单的方法来构建 UITableView 接口
  • Diffing:当数据更改时,通过漂亮的动画重新加载您的 UI
  • 基于组件的:设计可重用组件,并在应用程序的多个屏幕之间共享您自定义 UI

在我们的经验中,这使得 UI 相关代码更容易构建和维护。我们的目标是使 UI 成为状态的函数(即:UI = f(state)),这使得 Bento 成为响应式编程的完美选择。

内容📋

安装💾

  • Cocoapods
target 'MyApp' do
    pod 'Bento'
end
  • Carthage
github "Babylonpartners/Bento"

它是怎样的?🧐

当构建一个Box时,你只需要关心SectionsNodes

let box = Box<SectionId, RowId>.empty
            |-+ Section(id: SectionId.user,header: EmptySpaceComponent(height: 24, color: .clear))
            |---+ Node(id: RowID.user, component: IconTitleDetailsComponent(icon: image, title: patient.name))
            |-+ Section(id: SectionId.consultantDate, header: EmptySpaceComponent(height: 24, color: .clear))
            |---+ Node(id: RowID.loading, component: LoadingIndicatorComponent(isLoading: true))

        tableView.render(box)

它是如何工作的?🤔

设置

Bento会自动在第一次请求UITableViewUICollectionView渲染Bento Box时执行数据源和代理的设置。

换句话说,为了使用Bento,不能使用你自己的数据源和代理。如果你想响应当地代理的消息,Bento不支持作为功能,你可以考虑使用prepareForBoxRendering(_:)提供自定义适配器。

集合视图 适配器基类 必需的协议实现
UITableView TableViewAdapterBase UITableViewDataSource & UITableViewDelegate
UICollectionView CollectionViewAdapterBase UITableViewDataSource & UITableViewDelegate

Box📦

Box 是该库的基本组件,本质上是对UITableView内容的虚拟表示。它有两个泛型参数 - SectionIdRowId -,这些是用于Section<SectionId, RowId>Node<RowId>的唯一标识符,由差异引擎使用以执行UITableView内容的动画更改。Box只是一个包含多个section数组的容器。

章节和节点🏗

SectionNodeBox的构建块

  • Section是对UITableView中section的抽象,它定义了是否应显示头部或尾部。
  • Node是对UITableView中row的抽象,它定义了数据的渲染方式。
struct Section<SectionId: Hashable, RowId: Hashable> {
    let id: SectionId
    let header: AnyRenderable?
    let footer: AnyRenderable?
    let rows: [Node<RowId>]
}

public struct Node<Identifier: Hashable> {
    let id: Identifier
    let component: AnyRenderable
}

身份标识🎫

身份标识是差异算法执行更改的关键概念之一。

对于一般业务问题,两个实例的完全不等性并不一定意味着在身份上的不等—它仅仅意味着如果两个实例的身份相同,所持有数据已经更改。

(更多信息请参阅此处.)

SectionIDItemID分别定义了章节及其项目的身份标识。

可渲染项🖼

Renderable类似于React组件。它是即将显示的真实UITableViewCell的抽象。这个想法是使其能够创建小的独立组件,这些组件可以在您的应用的许多部分中重用。

public protocol Renderable: class {
    associatedtype View: UIView

    func render(in view: View)
}

class IconTextComponent: Renderable {
    private let title: String
    private let image: UIImage

    init(image: UIImage,
         title: String) {
        self.image = image
        self.title = title
    }

    func render(in view: IconTextCell) {
        view.titleLabel.text = title
        view.iconView.image = image
    }
}

Bento的算术💡

存在几个自定义操作符,它们提供语法糖来简化Bento的构建

precedencegroup ComposingPrecedence {
    associativity: left
    higherThan: NodeConcatenationPrecedence
}

precedencegroup NodeConcatenationPrecedence {
    associativity: left
    higherThan: SectionConcatenationPrecedence
}

precedencegroup SectionConcatenationPrecedence {
    associativity: left
    higherThan: AdditionPrecedence
}

infix operator |-+: SectionConcatenationPrecedence
infix operator |-?: SectionConcatenationPrecedence
infix operator |---+: NodeConcatenationPrecedence
infix operator |---?: NodeConcatenationPrecedence

let bento = Box.empty
	|-+ Section(id: SectionID.first) // 2
	|---+ Node(id: RowID.someId, Component()) // 1

如你所注意到的

  • 操作符|-+具有SectionConcatenationPrecedence
  • 操作符|---+具有NodeConcatenationPrecedence

NodeConcatenationPrecedence高于|-+ / SectionConcatenationPrecedence,这意味着首先计算节点。

上述表达式的顺序是

  1. Section() |---+ Node() => Section
  2. Box() |-+ Section() => Box

条件运算符

除了 |-+|---+ 连接运算符之外,Bento 还具备条件连接运算符

  • |-? 用于 Section
  • |---? 用于 Node

它们用于为闭包中的 SectionNode 提供在 BoolOptional 幸运路径中的条件,通过 .iff.some 函数实现。

以下是一些示例

let box = Box.empty
    |-? .iff(aBoolCondition) {
        Section()  // <-- Section only added if `boolCondition` is `true`
    }
let box = Box.empty
    |-? anOptional.map { unwrappedOptional in  // <-- the value of anOptional unwrapped
        Section()  // <-- Section only added if `anOptional` is not `nil`
    }

|---? 对于 Node 的工作方式完全相同。

组件 & 样式表🎨

Bento 包含一系列通用组件,如 `DescriptionTextInput`、`EmptySpace` 等。Bento 使用样式表为组件提供样式。

样式表是一种定义特定视图应该如何渲染的方式。组件的职责是提供应显示的内容,而样式表提供一种样式,用于如何呈现它。字体、颜色、对齐方式应纳入样式表中。

样式表支持键路径,以便于组合。

let styleSheet = LabelStyleSheet()
    .compose(\.numberOfLines, 3)
    .compose(\.font, UIFont.preferredFont(forTextStyle: .body))

样式表可以与 Bento 的组件一起使用。您需要做的只是使用正确的样式表。

return .empty
  |-+ Section(id: .first)
  |---+ Node(
         id: .componentId,
         component: Component.Description(
             text: "Text",
             styleSheet: Component.Description.StyleSheet()
                 .compose(\.text.font, UIFont.preferredFont(forTextStyle: .body))
         )
   )

示例😎

附加文档📙

开发安装🛠

如果您想克隆存储库以供贡献使用或运行示例应用,您需要安装其依赖项,这些依赖项存储为git子模块。

git submodule update --init --recursive

或者,如果您已经安装了Carthage,您可以使用它来完成同样的操作。

carthage checkout

项目现状🤷‍♂️

功能 状态
UITableView
UICollectionView
Carthage支持
免费函数作为替代操作符

开发资源

  • Bento组件契约

    定义必须由Bento组件遵守的要求,以及开发自定义组件的最佳实践。

贡献✍️

欢迎并高度重视贡献!❤️以下是操作方法

  • 如果您有任何问题,请创建一个带有问题标签的问题;
  • 如果您有功能请求,您可以创建一个带有功能请求标签的问题;
  • 如果您发现了错误,请自由创建一个带有错误标签的问题,或者提交一个修复的PR。

图像归属

咖啡 石榴 樱桃 草莓