Epoxy是一套用于在Swift中构建< різнёнка href="https://developer.apple.com/documentation/uikit" rel="nofollow">UIKit应用程序的声明式UI API。Epoxy受到由Android上的Epoxy框架 以及Swift中的其他声明式UI框架(如SwiftUI )启发和影响。
Epoxy是在Airbnb 开发并由数十位贡献者开发和完善多年的。它为数千个屏幕提供了动力,这些屏幕被部署给数百万用户。
以下是我们使用Epoxy在Airbnb应用程序中构建的一些示例屏幕截图。我们对Epoxy的使用从我们最简单的表单和静态屏幕到我们最复杂和动态的功能。
目录
安装
Epoxy可以使用CocoaPods 或Swift Package Manager 进行安装。
CocoaPods
要开始使用Epoxy与Cocoapods,请将以下内容添加到您的Podfile中,然后按照集成说明 进行操作。
Epoxy被分成每个podspec 的模块,因此您只需包括您需要的部分。
Swift包管理器(SPM)
要使用Swift包管理器 安装Epoxy,请遵循Apple发布的教程 并使用Epoxy仓库的当前版本URL。
在Xcode中,选择“文件”→“Swift包”→“添加包依赖项”。
输入https://github.com/airbnb/epoxy-ios.git
Epoxy被分成每个模块 的库产品 ,因此您只需包括您需要的部分。
模块
Epoxy有模块化架构,因此您只需包含您用例所必需的部分。
文档和教程
请查看wiki 以获取完整文档和逐步教程。对于类型级文档,请参阅Epoxy DocC文档 ,该文档托管在Swift包索引 上。
还有一个包含许多示例的完整样例应用,您可以通过在 Epoxy.xcworkspace 中运行 EpoxyExample 方案,或者浏览其 源代码 来使用。
如果您还有疑问,请随时创建一个新的 问题 。
入门指引
EpoxyCollectionView
EpoxyCollectionView 提供了一个声明式 API,用于驱动 UICollectionView 的内容。重写 CollectionViewController 可无限期地创建含有声明式 API 的 UICollectionView 衍生视图控制器。
以下示例代码展示了如何在 UICollectionView 中渲染含有 TextRow 组件的单个单元格。 TextRow 是一个简单的 UIView,包含两个遵循 EpoxyableView 协议的标签。
您可以通过直接创建包含段落的 CollectionViewController 实例使用它,例如,这个视图控制器有一个可选的中继项。
来源 结果
enum DataID {
case row
}
let viewController = CollectionViewController (
layout : UICollectionViewCompositionalLayout
.list (using : .init (appearance : .plain )),
items : {
TextRow.itemModel (
dataID : DataID.row ,
content : .init (title : " Tap me!" ),
style : .small )
.didSelect { _ in
// Handle selection
}
})
或者,您可以通过对 CollectionViewController 进行子类化来处理更复杂的情况,比如这个跟踪运行计数的视图控制器。
来源 结果
class CounterViewController : CollectionViewController {
init () {
let layout = UICollectionViewCompositionalLayout
.list (using : .init (appearance : .plain ))
super .init (layout : layout)
setItems (items, animated : false )
}
enum DataID {
case row
}
var count = 0 {
didSet {
setItems (items, animated : true )
}
}
@ItemModelBuilder
var items: [ItemModeling] {
TextRow.itemModel (
dataID : DataID.row ,
content : .init (
title : " Count \( count ) " ,
body : " Tap to increment" ),
style : .large )
.didSelect { [weak self ] _ in
self ? .count += 1
}
}
}
您可以查看其 wiki 页面 或浏览 代码文档 来了解更多关于 EpoxyCollectionView 的信息。
EpoxyBars
EpoxyBars 提供了一个声明式 API,用于在 UIViewController 中渲染固定顶部、固定底部或输入栏堆栈。
以下代码示例将在 UIViewController 视图中固定渲染一个 ButtonRow 组件。 ButtonRow 是一个简单的 UIView 组件,包含单个 UIButton,该按钮按视图中父视图的外边距对齐,并遵循 EpoxyableView 协议。
来源 结果
class BottomButtonViewController : UIViewController {
override func viewDidLoad () {
super .viewDidLoad ()
bottomBarInstaller.install ()
}
lazy var bottomBarInstaller = BottomBarInstaller (
viewController : self ,
bars : bars)
@BarModelBuilder
var bars: [BarModeling] {
ButtonRow.barModel (
content : .init (text : " Click me!" ),
behaviors : .init (didTap : {
// Handle button selection
}))
}
}
您可以在其 wiki 页面 或浏览 代码文档 中了解更多关于 EpoxyBars 的信息。
EpoxyNavigationController
EpoxyNavigationController 提供了驱动 UINavigationController 导航堆栈的声明式 API。
以下代码示例展示了如何使用它轻松地驱动包含多个视图控制器流的功能。
来源 结果
class FormNavigationController : NavigationController {
init () {
super .init ()
setStack (stack, animated : false )
}
enum DataID {
case step1 , step2
}
var showStep2 = false {
didSet {
setStack (stack, animated : true )
}
}
@NavigationModelBuilder
var stack: [NavigationModel] {
.root (dataID : DataID.step1 ) { [weak self ] in
Step1ViewController (didTapNext : {
self ? .showStep2 = true
})
}
if showStep2 {
NavigationModel (
dataID : DataID.step2 ,
makeViewController : {
Step2ViewController (didTapNext : {
// Navigate away from this step.
})
},
remove : { [weak self ] in
self ? .showStep2 = false
})
}
}
}
您可以在其 维基百科 页面或通过浏览 代码文档 了解更多关于 EpoxyNavigationController 的信息。
EpoxyPresentations
EpoxyPresentations 提供了驱动 UIViewController 弹出显示的声明式 API。
以下代码示例展示了如何使用它轻松地驱动首次出现时显示模态的功能。
来源 结果
class PresentationViewController : UIViewController {
override func viewDidAppear (_ animated : Bool ) {
super .viewDidAppear (animated)
setPresentation (presentation, animated : true )
}
enum DataID {
case detail
}
var showDetail = true {
didSet {
setPresentation (presentation, animated : true )
}
}
@PresentationModelBuilder
var presentation: PresentationModel? {
if showDetail {
PresentationModel (
dataID : DataID.detail ,
presentation : .system ,
makeViewController : { [weak self ] in
DetailViewController (didTapDismiss : {
self ? .showDetail = false
})
},
dismiss : { [weak self ] in
self ? .showDetail = false
})
}
}
}
您可以在其 维基百科 页面或通过浏览 代码文档 了解更多关于 EpoxyPresentations 的信息。
EpoxyLayoutGroups
布局组是受 SwiftUI 的 Auto Layout 容器启发而创建的 UIKit,类似于 SwiftUI 的 HStack 和 VStack ,允许您轻松地将 UIKit 元素组合成水平和垂直组。
VGroup 允许您将组件垂直组合,创建如这样的堆叠组件
来源 结果
// Set of dataIDs to have consistent
// and unique IDs
enum DataID {
case title
case subtitle
case action
}
// Groups are created declaratively
// just like Epoxy ItemModels
let group = VGroup (
alignment : .leading ,
spacing : 8 )
{
Label.groupItem (
dataID : DataID.title ,
content : " Title text" ,
style : .title )
Label.groupItem (
dataID : DataID.subtitle ,
content : " Subtitle text" ,
style : .subtitle )
Button.groupItem (
dataID : DataID.action ,
content : " Perform action" ,
behaviors : .init { button in
print (" Button tapped! \( button ) " )
},
style : .standard )
}
// install your group in a view
group.install (in : view)
// constrain the group like you
// would a normal subview
group.constrainToMargins ()
如您所见,这与其他 Epoxy 中使用的 API 非常相似。需要注意的是底部的重要代码:`install(in: view)` 调用。`HGroup` 和 `VGroup` 都使用 UILayoutGuide 编写,这防止了大型嵌套视图层级。为了解决这一点,我们添加了此 install 方法,防止用户手动添加子视图和布局指南。
使用 HGroup 几乎与 VGroup 完全相同,但在此处组件水平排列而不是垂直排列
来源 结果
enum DataID {
case icon
case title
}
let group = HGroup (spacing : 8 ) {
ImageView.groupItem (
dataID : DataID.icon ,
content : UIImage (systemName : " person.fill" )! ,
style : .init (size : .init (width : 24 , height : 24 )))
Label.groupItem (
dataID : DataID.title ,
content : " This is an IconRow" )
}
group.install (in : view)
group.constrainToMargins ()
组也支持嵌套,因此您可以轻松创建由多个组组成的复杂布局。
来源 结果
enum DataID {
case checkbox
case titleSubtitleGroup
case title
case subtitle
}
HGroup (spacing : 8 ) {
Checkbox.groupItem (
dataID : DataID.checkbox ,
content : .init (isChecked : true ),
style : .standard )
VGroupItem (
dataID : DataID.titleSubtitleGroup ,
style : .init (spacing : 4 ))
{
Label.groupItem (
dataID : DataID.title ,
content : " Build iOS App" ,
style : .title )
Label.groupItem (
dataID : DataID.subtitle ,
content : " Use EpoxyLayoutGroups" ,
style : .subtitle )
}
}
您可以在其wiki条目 中了解更多关于EpoxyLayoutGroups的信息,或者通过浏览代码文档 。
常见问题解答(FAQ)
贡献
欢迎Pull requests!我们很乐意看到这个库的改进。欢迎浏览开放问题 ,看看哪些地方需要工作。如果您有功能请求或bug,请创建一个新的问题,以便我们可以跟踪。贡献者应遵循代码行为指南 。
许可
Epoxy在Apache License 2.0下发布。有关详细信息,请参阅LICENSE文件。
致谢
Logo设计由Alana Hanada 和Jonard La Rosa 完成。