一个能够以垂直滚动网格和列表形式布局视图的集合视图布局。
MagazineLayout
是一个 UICollectionViewLayout
子类,用于以垂直滚动网格和列表的形式布局项目。与 UICollectionViewFlowLayout
相比,MagazineLayout
支持许多附加功能。
UITableView
)其他功能
这些功能使我们能够在 Airbnb 应用程序中构建各种各样的屏幕,其中许多是我们流量最高的屏幕。以下是一些使用 MagazineLayout
布局的屏幕示例:
房源搜索 | 体验搜索 | 愿望清单 | 首页 |
---|---|---|---|
![]() |
![]() |
![]() |
![]() |
Plus 房源 | Plus 房源参观 | 旅程 | 旅程详情 |
---|---|---|---|
![]() |
![]() |
![]() |
![]() |
提供了一个示例应用程序来展示并使您能够测试 MagazineLayout
的一些功能。它可以在 ./Example/MagazineLayoutExample.xcworkspace
中找到。
注意:请确保使用 .xcworkspace
文件,而不是 .xcodeproj
文件,因为后者无法访问 MagazineLayout.framework
。
首次打开示例应用程序时,您将看到许多预先填充的项目和 section。大多数项目都配置为根据它们显示的文本进行自适应大小。
如果您想删除示例内容并从空白集合视图开始,您可以点击导航栏中的重新加载图标。
重新加载菜单 | 没有项目 |
---|---|
![]() |
![]() |
从此菜单中,您还可以将应用程序重置为原始示例数据。
要添加新项目,请点击导航栏中的添加图标。
从添加屏幕中,您可以配置一个新项目以插入到 UICollectionView
中。点击导航栏中的完成按钮后,该项目将以动画形式插入。
项目配置选项
.dynamic
高度模式,这将更改项目的高度)要删除项目,只需在集合视图中点击该项目即可。该项目将以动画形式删除。
要使用 Carthage 安装 MagazineLayout
,请将 github "airbnb/MagazineLayout"
添加到您的 Cartfile,然后按照 此处的 集成教程进行操作。
要使用 CocoaPods 安装 MagazineLayout
,请将 pod 'MagazineLayout'
添加到您的 Podfile,然后按照 此处的 集成教程进行操作。
一旦您将 MagazineLayout
集成到您的项目中,就可以轻松地将其与集合视图一起使用。
由于 UIKit
中的缺陷,MagazineLayout
需要其自己的 UICollectionViewCell
和 UICollectionReusableView
子类。
MagazineLayoutCollectionViewCell
MagazineLayoutCollectionReusableView
这两种类型使单元格和补充视图在使用 MagazineLayout
时可以正确地自适应大小。**请确保您的应用程序中的自定义单元格和可重用视图类型分别继承自 MagazineLayoutCollectionViewCell
和 MagazineLayoutCollectionReusableView
。**
或者,您可以复制 preferredLayoutAttributesFitting(_:)
的实现以在您的自定义单元格和可重用视图类型中使用,而无需从 MagazineLayout
提供的类型继承。
在您想要使用 MagazineLayout
的文件的顶部(可能是 UIView
或 UIViewController
子类),导入 MagazineLayout
。
import MagazineLayout
创建您的 UICollectionView
实例,并传入 MagazineLayout
实例作为布局参数。
let layout = MagazineLayout()
let collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout)
确保将 collectionView
添加为子视图,然后使用自动布局正确约束它,或者手动设置其 frame
属性。
view.addSubview(collectionView)
collectionView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
collectionView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
collectionView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
collectionView.topAnchor.constraint(equalTo: view.topAnchor),
collectionView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
])
向您的集合视图注册您的单元格和可重用视图类型。
collectionView.register(MyCustomCell.self, forCellWithReuseIdentifier: "MyCustomCellReuseIdentifier")
// Only necessary if you want section headers
collectionView.register(MyCustomHeader.self, forSupplementaryViewOfKind: MagazineLayout.SupplementaryViewKind.sectionHeader, withReuseIdentifier: "MyCustomHeaderReuseIdentifier")
// Only necessary if you want section footers
collectionView.register(MyCustomFooter.self, forSupplementaryViewOfKind: MagazineLayout.SupplementaryViewKind.sectionFooter, withReuseIdentifier: "MyCustomFooterReuseIdentifier")
// Only necessary if you want section backgrounds
collectionView.register(MyCustomBackground.self, forSupplementaryViewOfKind: MagazineLayout.SupplementaryViewKind.sectionBackground, withReuseIdentifier: "MyCustomBackgroundReuseIdentifier")
因为单元格、页眉和页脚可以自适应大小(背景不自适应大小),所以在本例中,MyCustomCell
、MyCustomHeader
和 MyCustomFooter
**必须**具有 preferredLayoutAttributesFitting(_:)
的正确实现。 请参阅设置单元格和页眉。
现在您已经向您的集合视图注册了视图类型,现在是连接数据源的时候了。与任何集合视图集成一样,您的数据源需要符合 UICollectionViewDataSource
。如果拥有您的集合视图的同一对象也是您的数据源,您可以简单地这样做:
collectionView.dataSource = self
最后,现在是配置布局以满足您的需求的时候了。与 UICollectionViewFlowLayout
和 UICollectionViewDelegateFlowLayout
一样,MagazineLayout
通过其 UICollectionViewDelegateMagazineLayout
配置其布局。
要开始配置 MagazineLayout
,请将集合视图的 delegate
属性设置为符合 UICollectionViewDelegateMagazineLayout
的对象。 如果拥有您的集合视图的同一对象也是您的代理,您可以简单地这样做:
collectionView.delegate = self
这是一个代理实现的示例:
extension ViewController: UICollectionViewDelegateMagazineLayout {
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeModeForItemAt indexPath: IndexPath) -> MagazineLayoutItemSizeMode {
let widthMode = MagazineLayoutItemWidthMode.halfWidth
let heightMode = MagazineLayoutItemHeightMode.dynamic
return MagazineLayoutItemSizeMode(widthMode: widthMode, heightMode: heightMode)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, visibilityModeForHeaderInSectionAtIndex index: Int) -> MagazineLayoutSupplementaryViewVisibilityMode {
return .visible(heightMode: .dynamic, pinToVisibleBounds: true)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, visibilityModeForFooterInSectionAtIndex index: Int) -> MagazineLayoutSupplementaryViewVisibilityMode {
return .visible(heightMode: .dynamic, pinToVisibleBounds: false)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, visibilityModeForBackgroundInSectionAtIndex index: Int) -> MagazineLayoutBackgroundVisibilityMode {
return .hidden
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, horizontalSpacingForItemsInSectionAtIndex index: Int) -> CGFloat {
return 12
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, verticalSpacingForElementsInSectionAtIndex index: Int) -> CGFloat {
return 12
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetsForSectionAtIndex index: Int) -> UIEdgeInsets {
return UIEdgeInsets(top: 0, left: 8, bottom: 24, right: 8)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetsForItemsInSectionAtIndex index: Int) -> UIEdgeInsets {
return UIEdgeInsets(top: 24, left: 0, bottom: 24, right: 0)
}
}
如果您按照上述步骤操作,您应该有一个使用 MagazineLayout
的工作正常的 UICollectionView
!如果您想使用预先制作的示例,请查看包含的示例项目,以及使用它的说明。
MagazineLayout
欢迎修复、改进和功能添加。如果您想贡献,请打开一个包含您的更改的详细描述的拉取请求。
作为经验法则,如果您要提出一个 API 破坏性更改或对现有功能的更改,请考虑通过打开一个 issue 来提出它,而不是一个拉取请求;我们将使用该 issue 作为公共论坛来讨论该提案是否有意义。
Bryan Keller
Bryn Bodayle
如果您或您的公司发现 MagazineLayout
有用,请告诉我们!
如果没有我在 Airbnb 的几位同事的贡献和支持,MagazineLayout
是不可能实现的。特别是 Bryn Bodayle,自 MagazineLayout
成立以来,审查了每个 PR,并帮助讨论和解决了无数棘手的 UICollectionView
和 UIKit
问题。
我也要感谢以下人员,他们都为 MagazineLayout
的成功铺平了道路:
MagazineLayout
在 Apache License 2.0 下发布。 有关详细信息,请参阅 LICENSE。