Compose是一个数据驱动的库,将帮助你组合复杂的动态视图。
它帮助您使用简单和易于组合的数据结构来创建复杂的动态视图。它很容易使用和扩展。
而且,在现有项目中实现它也轻而易举。
首先,你需要创建包含将要显示的数据的units
let info = "Info to be displayed"
let infoUnit = LabelUnit(text: info, traits: [.height(40)])
然后,将这个unit添加到容器中
container.state = [infoUnit]
你也可以有条件地添加这个unit到容器中,例如
var units: [ComposingUnit] = [....]
units.add(if: someCondition) {
return LabelUnit(text: info, traits: [.height(40)])
}
container.state = units
所以,你不需要处理许多dataSource/delegate方法,只需创建一个ComposingUnit
数组并将其分配给容器的state
属性。
那么,什么是ComposingUnits呢?
协议ComposingUnit是这个框架的核心。
您可以让任何类或结构遵从它,根据您的需要选择使用`Value Type`或`Reference Type`。此外,此类/结构应**包含所有将被显示的数据**。采用这种方案,我们不需要保留生成这些单位的模型的引用。
假设我们想显示我们拥有的所有feed项的列表。
let feedItems: [FeedItem] = ... //You can grab an array from CoreData, JSON, Realm, anywhere...
var feedUnits: [ComposingUnit] = feedItems.map { FeedUnit(id: $0.uniqueId, title: $0.title, image: $0.image, likeCount:Int) }
container.state = feedUnits
因此,创建`feedUnits`数组后,我们不再需要`feedItems`,并可以轻松地在任何线程中使用我们的feedUnits。**并且我们可以向该数组添加任何其他ComposingUnit
,使我们能够显示与同一列表中的feed项完全不同的视图**
feedUnits.add(if: feedUnits.count > 4) {
return SeeMoreFeedsUnit(feedsCount: feedUnits.count)
}
处理选择和其他代理回调
要处理**单元格选择**或其他代理回调,所有您要做的只是实现一个适用的**协议**。对于单元格选择,它是`SelectableUnit`协议。此协议定义了一个在单元格被选择时将调用的方法。
您可以在这里查看所有扩展协议
将units分组以表示单个单元
您也可以使用一个CollectionStackUnit来将一些单元组合在一起作为一个单独的单元
var units: [ComposingUnit] = [...] //create somewhere
units.add(ifLet: data.optionalFeedItem) { feedItem in
var innerUnits: [ComposingUnit] = [HeaderUnit(text: feedItem.title)]
innerUnits.add(ifLet: feedItem.image) { image in
return FeedImageUnit(image: image)
}
innerUnits.add(ifLet: feedItem.video) { video in
return FeedVideoUnit(video: video)
}
innerUnits.append(ActionBarUnit(likes: feedItem.likes, comments: feedItem.comments))
let unit = CollectionStackUnit(identifier: feedItem.uniqueIdentifier, direction: .vertical, traits: [], units: innerUnits)
return unit
}
也许您也可以创建一个函数,根据FeedItem返回此项目
func FeedUnit(from: FeedItem)-> CollectionStackUnit {
var innerUnits: [ComposingUnit] = [HeaderUnit(text: feedItem.title)]
innerUnits.add(ifLet: feedItem.image) { image in
return FeedImageUnit(image: image)
}
innerUnits.add(ifLet: feedItem.video) { video in
return FeedVideoUnit(video: video)
}
innerUnits.append(ActionBarUnit(likes: feedItem.likes, comments: feedItem.comments))
let unit = CollectionStackUnit(identifier: feedItem.uniqueIdentifier, direction: .vertical, traits: [], units: innerUnits)
return unit
}
var units: [ComposingUnit] = [...] //create somewhere
units.add(ifLet: data.optionalFeedItem) { feedItem in
return FeedUnit(from: feedItem)
}
动态单元大小计算
我们使用一个名为DimensionUnit的结构来表示单元格宽度和/或高度的计算。一个DimensionUnit
可以通过以下方式计算尺寸:
- 静态值:它将忽略其容器大小并且总是返回此静态值
- 基于百分比值:它将返回其容器尺寸的百分比
- 基于总值:它将返回其容器尺寸减去一个静态值
- 基于自定义:它将执行一个闭包,并将容器大小作为参数传递。
使用DimensionUnit
,我们可以轻松地表达我们的单位高度和宽度。
ComposingContainer
为了显示一个ComposingUnit
数组,您需要一个符合ComposingContainer
的UIView。
框架提供了两个默认容器:ComposingCollectionView
和ComposingTableView
。两者都能够自动检测其显示状态中的插入/更新/删除操作。
测试
测试界面非常简单,因为您可以测试某些特定单元的存在,并且不需要渲染界面。
let dummyItem = FeedItem(...)
var feedUnit = FeedUnit(from: dummyItem)
XCTAssert(feedUnit.units.count == 3)
dummyItem.image = nil
dummyItem.video = nil
feedUnit = FeedUnit(from: dummyItem)
XCTAssert(feedUnit.units.count == 2)
示例
在我们的示例项目中提供了一些有趣的示例。
要运行,请克隆此回购库,然后打开Example/Compose_Example.xcodeproj
。**您不需要执行任何pod安装或配置才能运行此项目**。
安装
Cocoapods
Compose可以通过CocoaPods获取。要安装它,请简单地将在您的Podfile
中添加以下行
pod "Compose"
Carthage
Carthage是一个去中心化的依赖关系管理器,它构建您的依赖关系,并提供二进制框架。
您可以使用Homebrew安装Carthage,以下命令可以安装:
$ brew update
$ brew install carthage
要使用Carthage将Compose集成到Xcode项目中,请指定它在您的Cartfile
中。
github "VivaReal/Compose" ~> 1.0
运行carthage update
来构建框架,并将构建好的Compose.framework
拖放到您的Xcode项目中。
手动
您可以从此下载库,将Compose.xcodeproj
拖拽到您的项目中并链接Compose
框架。
文档
您可以在以下位置找到有关Compose的所有文档:文档
作者
布鲁诺·比莱斯基克,[email protected]
许可证
Compose在MIT许可证下提供。有关更多信息,请参阅LICENSE文件。