BrickKit 2.5.0

BrickKit 2.5.0

测试已测试
Lang语言 SwiftSwift
许可证 Apache-2.0
发布最后发布2018年11月
SPM支持 SPM

Maintained by Wayfair iOS Team, Justin Anderson, Kevin Lundberg.



BrickKit 2.5.0

  • Ruben Cagnie

BrickKit

Build Status codecov.io Pod Version Pod Platform Pod License Carthage compatible

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)

纵向布局

Simple Portrait

横向布局

Simple Landscape

基于规则定义高度

// 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)
        )
)

肖像模式下的高度比率

Height Ratio Portrait

横向模式下的高度比率

Height Ratio Landscape

使用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示例

Multi Sections

创建您自己的可重用砖块

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的砖块

Dynamic Sizing

在砖块内使用不同的滚动方向

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)

horizontal

动态图片高度

图片会根据其内容自动调整大小

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)

images

高级粘性条

...
behavior = StickyLayoutBehavior(dataSource: self)
...

func stickyLayoutBehavior(stickyLayoutBehavior: StickyLayoutBehavior, shouldStickItemAtIndexPath indexPath: NSIndexPath, withIdentifier identifier: String, inCollectionViewLayout collectionViewLayout: UICollectionViewLayout) -> Bool {
    return identifier == BrickIdentifiers.titleLabel
}

sticky

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))

coverflow

出现行为(无,底部,顶部)

layout.appearBehavior = BrickAppearTopBehavior() // Insert from the top
layout.appearBehavior = BrickAppearBottomBehavior() // Insert from the bottom

insert

聚光灯

...
behavior = SpotlightLayoutBehavior(dataSource: self)
...

func spotlightLayoutBehavior(behavior: SpotlightLayoutBehavior, smallHeightForItemAtIndexPath indexPath: NSIndexPath, withIdentifier identifier: String, inCollectionViewLayout collectionViewLayout: UICollectionViewLayout) -> CGFloat? {
    return identifier == BrickIdentifiers.repeatLabel ? 50 : nil
}

spotlight

特点

  • 设置简单,只需几行代码即可创建视图。
  • 是UCollectionViewController的另一种实现。
  • 高级宽度和高度设置,初始化时视图可以大小根据iPad/屏幕尺寸调整!
  • 允许多重复用单元。
    • 支持每个BrickCell:UICollectionViewCell类多个nib。
  • 有“行为”,这是可以应用在任何视图控制器上的复杂布局交互。
    • 粘性标题/页脚
    • 隐藏视图
    • 滚动行为
    • 吸附到点上
    • 以上所有功能都可以用不高于十行的代码实现!
  • 异步改变高度
    • 允许在单元格出现在屏幕上后使内容流动。
    • 支持单元高度在图片或视频加载后调整大小。

安装说明

安装简单。您只需要 Cocoapods,只需要将其添加到您的 podfile 中。

pod 'BrickKit'

参见[安装详情](#安装详情)

术语

Brick 对象表示需要在屏幕上布局的模型。例如,在 LabelBrick 的情况下,模型应包含要放在标签内的文本。

我们强烈建议每个 Brick 都有一个 datasource 和可选的 delegate。这是一种已被证明成功的模式,允许您的砖块重复使用。对于简单的砖块,建议创建一个基本的 BrickModel,该模型继承自数据源,这样您就不需要在 viewcontroller 中实现数据源。关于如何实现 Brick 的示例,请参考 BrickKit pod 中的示例,例如 LabelBrickButtonBrick

砖区域

BrickSectionBrickCollectionView 如何在屏幕上布局单元格提供信息。特别是,它提供了 identifiers、BrickSection 内的砖块以及 UIEdgeInsetsinset

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 所拥有和维护。

行为守则

CODE_OF_CONDUCT.md

贡献

CONTRIBUTING.md

许可

BrickKit 采用 Apache 许可证发布。有关详细信息,请参阅 LICENSE。