BrickKit 是 iOS 和 tvOS 的一款愉快布局库。它完全使用 Swift 编写!
使用 BrickKit,您可以通过简单的方式创建复杂且响应灵敏的布局。易于使用和扩展。创建自己的可重用积木和行为。
使用描述高级行为的对象定义您的布局
let section = BrickSection(bricks: [
LabelBrick(width: .ratio(ratio: 1), text: "BRICK 1"),
LabelBrick(width: .ratio(ratio: 1), text: "MULTI-LINE BRICK MULTI-LINE BRICK MULTI-LINE BRICK MULTI-LINE BRICK MULTI-LINE BRICK MULTI-LINE BRICK MULTI-LINE BRICK MULTI-LINE BRICK MULTI-LINE BRICK MULTI-LINE BRICK MULTI-LINE BRICK MULTI-LINE BRICK MULTI-LINE BRICK MULTI-LINE BRICK MULTI-LINE BRICK MULTI-LINE BRICK MULTI-LINE BRICK MULTI-LINE BRICK"),
LabelBrick(width: .ratio(ratio: 1/2), text: "1/2 BRICK"),
LabelBrick(width: .ratio(ratio: 1/2), text: "1/2 BRICK"),
LabelBrick(width: .ratio(ratio: 1/3), text: "1/3 BRICK"),
LabelBrick(width: .ratio(ratio: 1/3), text: "1/3 BRICK"),
LabelBrick(width: .ratio(ratio: 1/3), text: "1/3 BRICK"),
], inset: 10, edgeInsets: UIEdgeInsets(top: 20, left: 20, bottom: 20, right: 20))
self.setSection(section)
纵向布局
横向布局
基于规则定义高度
// Calculate height using auto-layout
LabelBrick(height: .auto(estimate: .fixed(size: 50)), text: "BRICK"),
// Fixed Height
LabelBrick(height: .fixed(size: 50), text: "BRICK"),
// Calculate height based on ratio of the width
LabelBrick(height: .ratio(ratio: 1), text: "BRICK"),
// Complex Rule based on size classes and/or orientation
let height: BrickDimension =
.horizontalSizeClass(
regular: .orientation(
landscape: .fixed(size: 200),
portrait: .fixed(size: 100)
),
compact: .orientation(
landscape: .fixed(size: 100),
portrait: .fixed(size: 50)
)
)
肖像模式下的高度比率
横向模式下的高度比率
使用Section中的Section进行复杂布局
let section = BrickSection(bricks: [
LabelBrick(width: .ratio(ratio: 0.5), text: "BRICK"),
BrickSection(width: .ratio(ratio: 0.5), bricks: [
LabelBrick(text: "BRICK\nBRICK"),
LabelBrick(text: "BRICK"),
LabelBrick(text: "BRICK"),
], inset: 10),
BrickSection(bricks: [
BrickSection(width: .ratio(ratio: 1/3), bricks: [
LabelBrick(text: "BRICK"),
LabelBrick(text: "BRICK"),
], inset: 5),
BrickSection(width: .ratio(ratio: 2/3), backgroundColor: .brickGray3, bricks: [
LabelBrick(text: "BRICK"),
LabelBrick(text: "BRICK"),
LabelBrick(text: "BRICK"),
], inset: 15),
], inset: 5, edgeInsets: UIEdgeInsets(top: 5, left: 5, bottom: 5, right: 5)),
BrickSection(width: .ratio(ratio: 0.5), bricks: [
LabelBrick(text: "BRICK"),
LabelBrick(text: "BRICK"),
], inset: 10),
LabelBrick(width: .ratio(ratio: 0.5), text: "BRICK"),
LabelBrick(text: "BRICK"),
], inset: 10, edgeInsets: UIEdgeInsets(top: 20, left: 20, bottom: 20, right: 20))
self.setSection(section)
Section中的Section示例
创建您自己的可重用砖块
registerBrickClass(NiblessBrick.self)
let section = BrickSection(bricks: [
NiblessBrick(text: "BRICK", image: UIImage(named: "logo_splash")!),
NiblessBrick(width: .ratio(ratio: 1/2), text: "BRICK", image: UIImage(named: "logo_inapp")!),
NiblessBrick(width: .ratio(ratio: 1/2), text: "BRICK", image: UIImage(named: "logo_inapp")!)
], inset: 10)
setSection(section)
BrickKit支持带Nib或无Nib的砖块
在砖块内使用不同的滚动方向
self.registerBrickClass(CollectionBrick.self)
self.registerBrickClass(LabelBrick.self)
let section1 = BrickSection(bricks: [
ImageBrick(width: .ratio(ratio: 1/4), height: .ratio(ratio: 1), dataSource: self),
] , inset: 10, edgeInsets: UIEdgeInsets(top: 5, left: 5, bottom: 5, right: 5))
section1.repeatCountDataSource = self
let section2 = BrickSection(bricks: [
ImageBrick(width: .ratio(ratio: 1/2), height: .ratio(ratio: 1), dataSource: self),
], inset: 10, edgeInsets: UIEdgeInsets(top: 5, left: 5, bottom: 5, right: 5))
section2.repeatCountDataSource = self
let section3 = BrickSection(bricks: [
ImageBrick(width: .fixed(size: 100), height: .ratio(ratio: 1), dataSource: self),
], inset: 10, edgeInsets: UIEdgeInsets(top: 5, left: 5, bottom: 5, right: 5))
section3.repeatCountDataSource = self
let section = BrickSection(bricks: [
LabelBrick(text: "1/4 Ratio"),
CollectionBrick(scrollDirection: .horizontal, dataSource: CollectionBrickCellModel(section: section1), brickTypes: [ImageBrick.self]),
LabelBrick(text: "1/2 Ratio"),
CollectionBrick(scrollDirection: .horizontal, dataSource: CollectionBrickCellModel(section: section2), brickTypes: [ImageBrick.self]),
LabelBrick(text: "100px Fixed"),
CollectionBrick(scrollDirection: .horizontal, dataSource: CollectionBrickCellModel(section: section3), brickTypes: [ImageBrick.self]),
])
setSection(section)
动态图片高度
图片会根据其内容自动调整大小
registerBrickClass(ImageBrick.self)
registerBrickClass(LabelBrick.self)
let imageURL = NSURL(string:"https://secure.img2.wfrcdn.com/lf/8/hash/2664/10628031/1/custom_image.jpg")!
let section = BrickSection("RootSection", bricks: [
LabelBrick(text: "Below is an image brick with fixed height"),
ImageBrick(height: .fixed(size: 50)dataSource: ImageURLBrickModel(url: imageURL, contentMode: .ScaleAspectFit)),
LabelBrick(text: "Below is an image brick loaded dynamically"),
ImageBrick(height: .auto(estimate: .fixed(size: 50)), dataSource: ImageURLBrickModel(url: imageURL, contentMode: .ScaleAspectFit)),
LabelBrick(text: "Below is an image brick with fixed height"),
ImageBrick(height: .fixed(size: 50), dataSource: ImageURLBrickModel(url: imageURL, contentMode: .scaleAspectFill)),
], inset: 10)
self.setSection(section)
高级粘性条
...
behavior = StickyLayoutBehavior(dataSource: self)
...
func stickyLayoutBehavior(stickyLayoutBehavior: StickyLayoutBehavior, shouldStickItemAtIndexPath indexPath: NSIndexPath, withIdentifier identifier: String, inCollectionViewLayout collectionViewLayout: UICollectionViewLayout) -> Bool {
return identifier == BrickIdentifiers.titleLabel
}
Coverflow
layout.scrollDirection = .horizontal
let snapToBehavior = SnapToPointLayoutBehavior(scrollDirection: .horizontal(.Center))
self.brickCollectionView.layout.behaviors.insert(snapToBehavior)
self.brickCollectionView.layout.behaviors.insert(CoverFlowLayoutBehavior(minimumScaleFactor: 0.75))
出现行为(无,底部,顶部)
layout.appearBehavior = BrickAppearTopBehavior() // Insert from the top
layout.appearBehavior = BrickAppearBottomBehavior() // Insert from the bottom
聚光灯
...
behavior = SpotlightLayoutBehavior(dataSource: self)
...
func spotlightLayoutBehavior(behavior: SpotlightLayoutBehavior, smallHeightForItemAtIndexPath indexPath: NSIndexPath, withIdentifier identifier: String, inCollectionViewLayout collectionViewLayout: UICollectionViewLayout) -> CGFloat? {
return identifier == BrickIdentifiers.repeatLabel ? 50 : nil
}
特点
- 设置简单,只需几行代码即可创建视图。
- 是UCollectionViewController的另一种实现。
- 高级宽度和高度设置,初始化时视图可以大小根据iPad/屏幕尺寸调整!
- 允许多重复用单元。
- 支持每个BrickCell:UICollectionViewCell类多个nib。
- 有“行为”,这是可以应用在任何视图控制器上的复杂布局交互。
- 粘性标题/页脚
- 隐藏视图
- 滚动行为
- 吸附到点上
- 以上所有功能都可以用不高于十行的代码实现!
- 异步改变高度
- 允许在单元格出现在屏幕上后使内容流动。
- 支持单元高度在图片或视频加载后调整大小。
安装说明
安装简单。您只需要 Cocoapods,只需要将其添加到您的 podfile 中。
pod 'BrickKit'
参见[安装详情](#安装详情)
术语
砖
Brick
对象表示需要在屏幕上布局的模型。例如,在 LabelBrick
的情况下,模型应包含要放在标签内的文本。
我们强烈建议每个
Brick
都有一个datasource
和可选的delegate
。这是一种已被证明成功的模式,允许您的砖块重复使用。对于简单的砖块,建议创建一个基本的BrickModel
,该模型继承自数据源,这样您就不需要在 viewcontroller 中实现数据源。关于如何实现Brick
的示例,请参考 BrickKit pod 中的示例,例如LabelBrick
或ButtonBrick
。
砖区域
BrickSection
为 BrickCollectionView
如何在屏幕上布局单元格提供信息。特别是,它提供了 identifiers
、BrickSection 内的砖块以及 UIEdgeInsets
和 inset
。
BrickSection
是砖块的子类。这允许在砖区域内嵌套多个砖区域。BrickCollectionView 只接受单个统一的砖区域,因此如果您想要复杂的布局,您将需要将砖区域嵌套在一起。
BrickCell
BrickCell
对象表示实际的视图。当视图出现在屏幕上时,将会实例化单元格,并调用其dataSource来获取如何显示自己的信息。
由于BrickKit基于
UICollectionView
,因此每个BrickCell
都是UICollectionViewCell
的子类。
BrickCollectionView
BrickCollectionView是一个基于UICollectionView
的子类,负责管理砖块的部分/部分。它还负责为给定的砖块加载正确的BrickCell
。
您需要将BrickCollectionView中使用的每个砖块进行注册。(如:`brickCollectionView.registerBrickClass(LabelBrick.self)`)如果您未能这样做,则应用程序将崩溃(并带有描述性消息)。注意,CollectionBrick是一个包含BrickCollectionView的砖块。如果您其中之一实现,则必须在CollectionBrick的BrickCollectionView中注册brickClass,否则会因为找不到nib而崩溃。
BrickLayout
实现BrickLayout
协议的对象负责在BrickCollectionView上显示BrickCells。
BrickFlowLayout
BrickFlowLayout是BrickLayout协议的一个实现。
BrickFlowLayout是UICollectionViewLayout的子类。
BrickDimension
在初始化砖块时提供BrickDimension
,这里发生了神奇的事情。BrickDimensions允许宽度 και η ύψος βασίζονται σε μέγεθος οθόνης ή σταθελή τιμή. Μπορούν επίσης να εξαρτάνται από το οριζόντιο ή ηpotenύ责任制 σας, την κατεύθυνσή σας, ή μπορείτε απλώς να αφήσετε το αυτόματο και να αφήσετε το αυτόματο ράουτ να το κάνει τη δουλειά του.
BrickRepeatCountDataSource
BrickRepeatCountDataSource
允许您为某个砖指定重复次数。这将给您的 BrickCell
分配一个索引,类似于数组设置的方式。
实现细节
理想情况下,砖块有一个标识符。这个标识符可以用于引用某个特定的砖块。与使用 NSIndexPath
相比较,这种做法的优势在于它将模型与布局解耦。这使得您可以为一个单元格使用相同的砖块、数据源和委托。
BrickKit 包含一些有用的默认砖块实现。
在 BrickKit 中使用的根对象是 BrickSection。
以下代码将创建一个标签,这个标签是
- 视图的全程宽度。
- 它的高度将根据标签内的文本动态调整。
- 上述两个条件取决于 nib 的约束是否正确设置,没有任何特定的高度或宽度设置。
--------------------------------
| LABEL BRICK |
--------------------------------
import UIKit
import BrickKit
class ViewController: BrickViewController {
override func viewDidLoad() {
super.viewDidLoad()
let section = BrickSection(bricks: [
LabelBrick(text: "LABEL BRICK")
])
self.setSection(section)
}
}
砖块的大小使用自动布局。如果约束设置为垂直增长,砖块将根据需要获得适当的高度。所以在一个标签的情况下,当文本大于宽度时,它将使用下一行。
--------------------------------
| LABEL BRICK LABEL BRICK LABEL |
| BRICK LABEL BRICK |
--------------------------------
let section = BrickSection(bricks: [
LabelBrick(text: "LABEL BRICK LABEL BRICK LABEL BRICK LABEL BRICK")
])
在 LabelBrickPlayground 中试用。
砖块尺寸
要设置砖块的尺寸,请参阅BRICK_DIMENSION.md。
BrickRepeatCountDataSource
BrickRepeatCountDataSources 是基于部分设置的。它们可以设置在任何 BrickSection
上。
import UIKit
import BrickKit
class ViewController: BrickViewController {
var fruits: [Fruit] = []
override func viewDidLoad() {
super.viewDidLoad()
fruits.append(Fruit(name: "Apple"))
fruits.append(Fruit(name: "Banana"))
fruits.append(Fruit(name: "Cherry"))
let section = BrickSection(bricks: [
LabelBrick("FRUIT", text: "BRICK 1")
])
section.repeatCountDataSource = self
self.setSection(section)
}
}
extension ViewController: BrickRepeatCountDataSource {
func repeatCount(for identifier: String, with collectionIndex: Int, collectionIdentifier: String) -> Int {
return identifier == "FRUIT" ? fruits.count : 1
}
}
在 BrickRepeatCountPlayground 中试用。
布局
隐藏行为
此行为允许您隐藏积木和部分。
积木出现行为
定义积木出现和消失方向的行为
BrickAppearBehavior
可扩展,因此您可以创建自己的行为
积木布局Z轴索引行为
确定积木在Z轴上的布局行为。在处理允许积木交叉的行为时非常重要。
行为 | 描述 |
---|---|
自上而下 | 顶部的单元拥有最高的z索引。对于需要粘性 单元的布局来说很理想,其中积木需要显示在其他所有积木之上。 |
自下而上 | 底部的单元拥有最高的z索引。对于较低单元位于较高单元之上的布局来说很理想。 |
行为
行为改变了积木在屏幕上的显示方式。这可能会依赖于滚动、刷新等。
BrickKit带有几个内建行为
行为 | 描述 |
---|---|
StickyLayoutBehavior | 允许积木和部分粘附到屏幕顶部。积木会粘附,直到其容器部分被通过。 |
MinimumStickyLayoutBehavior | 与StickyLayoutBehavior 相同,但积木的高度会先缩小到一个最小值,然后开始粘附。 |
StickyFooterLayoutBehavior | 允许积木和部分粘附到屏幕底部。积木会粘附,直到其容器部分被通过。 |
OnScrollDownStickyLayoutBehavior | 与StickyLayoutBehavior 相同,但砖块仅在向上滚动时才会粘附。向下滚动时,砖块将与其他砖块一起流动。 |
SnapToPointLayoutBehavior | 在滚动后,此行为会将 ScrollView 粘附到指定位置。如果在用户完成滚动后想使砖块粘附到一个点,这很有用。 |
SpotlightLayoutBehavior | 在滚动过程中,位于焦点中的砖块会变大。同一时间只有一个砖块在焦点中。 |
CardLayoutBehavior | 砖块将像卡片堆一样滚动。ScrollView 不会滚动,直到一张卡片完全位于另一张卡片之上。 |
CoverFlowLayoutBehavior | 在滚动过程中,屏幕中心靠近的砖块会变大,而其他砖块则会缩小。 |
OffsetLayoutBehavior | 允许对砖块的起始位置或大小进行偏移。 |
MaxZIndexLayoutBehavior | 允许砖块设置为最大 ZIndex,这意味着它们将始终位于任何其他砖块之上。 |
SetZIndexLayoutBehavior | 允许布局将任何砖块的 ZIndex 设置为任何值。 |
BrickLayoutBehavior
是可扩展的,因此可以自由创建自己的行为。
需求
- iOS 9.1+ / tvOS 9.0+
- Xcode 8.0+
- Swift 3.0 (Swift 3.2 兼容)
通讯
- 如果您发现了错误,请打开问题。
- 如果您有功能请求,请打开问题。
- 如果您想贡献,提交拉取请求。
安装详情
CocoaPods
CocoaPods是Cocoa项目的依赖项管理器。您可以使用以下命令安装它
$ gem install cocoapods
要使用CocoaPods将BrickKit集成到您的Xcode项目中,在您的Podfile
中指定它
source 'https://github.com/CocoaPods/Specs.git'
platform :ios, '10.0'
use_frameworks!
target '<Your Target Name>' do
pod 'BrickKit'
end
然后,运行以下命令
$ pod install
Carthage
Carthage 是一个去中心化的依赖管理器,用于构建您的依赖项并为您提供二进制框架。
您可以使用以下命令通过 Homebrew 安装 Carthage
$ brew update
$ brew install carthage
要使用 Carthage 将 BrickKit 集成到您的 Xcode 项目中,请在您的 Cartfile
中指定它
github "wayfair/brickkit-ios"
运行 carthage update
以构建框架,并将构建的 BrickKit.framework
拖动到您的 Xcode 项目。
致谢
BrickKit 由 Wayfair 所拥有和维护。
行为守则
贡献
许可
BrickKit 采用 Apache 许可证发布。有关详细信息,请参阅 LICENSE。