AloeStackView 1.2.0

AloeStackView 1.2.0

Arthur PangDan FedermanFan CoxMarli Oshlack 维护。



  • Airbnb

AloeStackView

一个简单的类,用于使用方便的 API 布局一组视图,同时发挥 Auto Layout 的强大功能。

Carthage compatible Version License Platform Build status

介绍

AloeStackView 是一个类,允许一组视图按照垂直或水平列表布局。在广义上,它与 UITableView 类似,但它的实现完全不同,并且做出了一组不同的权衡。

我们首次在 2016 年的 Airbnb iOS 应用中使用 AloeStackView。我们后来用它来实现应用中近 200 个屏幕。用例相当多样:从设置屏幕到创建新列表的表单,再到列表共享页面。

Airbnb app 1 Airbnb app 2 Airbnb app 3 Airbnb app 11 Airbnb app 4 Airbnb app 8
Airbnb app 7 Airbnb app 6 Airbnb app 5 Airbnb app 9 Airbnb app 10 Airbnb app 12

AloeStackView 首先专注于使 UI 非常快速、简单和直观地实现。它通过两种方式来实现这一点:

  • 它利用 Auto Layout 的功能来自动更新 UI,当对其视图进行更改时。

  • 它放弃了如视图回收等 UITableView 的一些功能,以便实现更简单且更安全的 API。

我们发现 AloeStackView 是一件有用的基础设施,希望您也会发现它很有用!

目录

功能

  • 允许您保持对视图的强引用并动态更改它们的属性,同时自动布局会自动使 UI 保持最新状态。

  • 允许视图动态添加、删除、隐藏和显示,可选的动画效果。

  • 包括对可自定义视图之间分隔符的内置支持。

  • 提供可扩展的 API,允许在不修改 AloeStackView 本身的情况下添加特殊功能。

  • 在高度访问量的 iOS 应用中被广泛使用并经过验证。

  • 小型、易于理解的代码库(小于 600 行代码),无外部依赖,将二进制大小增加保持在最低,并使代码贡献和调试变得容易。

系统要求

  • 目标设备 iOS 9.0+
  • Xcode 10.0+
  • Swift 4.0+

示例应用

库中包含一个简单的 示例 iOS 应用

您可以通过克隆库,打开 AloeStackViewExample.xcworkspace 并运行应用来尝试它。

示例应用展示了 AloeStackView 可用于实现 iOS 应用中屏幕的几种方式。

Example app

使用方法

创建 AloeStackView 实例

主要 API 通过 AloeStackView 类访问。

您可以在代码中轻松创建 AloeStackView 的实例。

import AloeStackView

let stackView = AloeStackView()

AloeStackView 是一个 UIView(具体为 UIScrollView),因此可以像使用应用中的任何其他视图一样使用它。

另外,如果您想使用 AloeStackView 构建整个 UIViewController,则可以使用方便的 AloeStackViewController

import AloeStackView

public class MyViewController: AloeStackViewController {

  public override func viewDidLoad() {
    super.viewDidLoad()
    stackView.addRow(...)
  }

}

AloeStackViewController 与如 UITableViewControllerUICollectionViewController 等类非常相似,因为它为您创建并管理一个 AloeStackView。您可以通过 stackView 属性访问 AloeStackView。使用 AloeStackViewController 而不是在 UIViewController 中创建自己的 AloeStackView 只需少写一些代码。

添加、删除和管理行

AloeStackView 的 API 主要处理“行”。行可以是您希望在 UI 中使用的任何 UIView

默认情况下,行垂直排列在一个列中,并且每行都占据 AloeStackView 的全部宽度。

可以通过 AloeStackView 上的 axis 属性来更改方向。当 axis 设置为 .horizontal 时,行并排排列,从左到右顺序排列,并且 AloeStackView 水平滚动,每行占据 AloeStackView 的全部高度。

使用 AloeStackView 构建 UI 时,一般首先添加组成您 UI 的行

for i in 1...3 {
  let label = UILabel()
  label.text = "Label \(i)"
  stackView.addRow(label)
}

Add rows

如果 AloeStackView 的长度超过可用屏幕空间,内容会自动变为可滚动的。

Add rows

AloeStackView 提供了一套全面的方法来管理行,包括在开头和结尾插入行、在其它行的上方或下方插入行、隐藏和显示行、删除行以及检索行。

您可以使用 rowInset 属性自定义行周围的间距,以及 setInset(forRow:)setInset(forRows:) 方法。

AloeStackView.swift 中的类文档提供了所有可用 API 的完整详细信息。

处理用户交互

AloeStackView 提供了对行上的点击手势的支持

stackView.setTapHandler(
  forRow: label,
  handler: { [weak self] label in
    self?.showAlert(title: "Row Tapped", message: "Tapped on: \(label.text ?? "")")
  })

label.isUserInteractionEnabled = true

Add rows

只有在行的 isUserInteractionEnabledtrue 时,点击处理程序才会触发。

处理点击手势的另一种方法是遵循 Tappable 协议

public class ToggleLabel: UILabel, Tappable {

  public func didTapView() {
    textColor = textColor == .red ? .black : .red
  }

}

for i in 1...3 {
  let label = ToggleLabel()
  label.text = "Label \(i)"
  label.isUserInteractionEnabled = true
  stackView.addRow(label)
}

Add rows

遵循 Tappable 允许常见的点击手势处理行为封装在视图内部。这样,您就可以在 AloeStackView 中多次重用视图,而无需每次都编写相同的点击手势处理代码。

动态更改行内容

使用AloeStackView的一个优点是可以即使在将视图添加到AloeStackView之后,依然持有其视图的强引用。

如果你更改视图的属性,这些属性会影响整个UI的布局,AloeStackView将自动重新布局其所有行

stackView.setTapHandler(forRow: label, handler: { label in
  label.text = (label.text ?? "") + "\n\nSome more text!"
})

Add rows

如你所见,在更改视图之前或之后无需通知AloeStackView。自动布局将确保UI保持最新状态。

样式和分隔符控制

默认情况下,AloeStackView会在行之间添加分隔符。

Add rows

开启和关闭分隔符

你可以轻松隐藏添加到AloeStackView中的任何行的分隔符。

stackView.hidesSeparatorsByDefault = true

Add rows

属性hidesSeparatorsByDefault仅适用于新添加的行。已存在于AloeStackView中的行不会受到影响。

你可以使用方法hideSeparator(forRow:)hideSeparators(forRows:)showSeparator(forRow:)showSeparators(forRows:)来隐藏或显示现有行的分隔符。

AloeStackView还提供了一个方便的属性来自动隐藏最后一个分隔符。

stackView.automaticallyHidesLastSeparator = true

Add rows

自定义分隔符

你可以更改分隔符左右两侧的间距。

stackView.separatorInset = .zero

Add rows

在垂直方向上,只使用separatorInset的左右属性。

在水平方向上,分隔符在行之间垂直显示。在这种情况下,只使用separatorInset的上下属性,它们控制分隔符的上下间距。

hidesSeparatorsByDefault一样,属性separatorInset仅适用于新添加的行。已存在于AloeStackView中的行不会受到影响。

你可以使用方法setSeparatorInset(forRow:)setSeparatorInset(forRows:)更改现有行的分隔符间距。

AloeStackView还提供了自定义分隔符颜色和宽度(或厚度)的属性。

stackView.separatorColor = .blue
stackView.separatorWidth = 2

Add rows

这些属性会影响所有AloeStackView中的分隔符。

扩展AloeStackView

AloeStackView是一个公开类,因此很容易通过子类化添加自定义功能而无需更改原始源代码。此外,AloeStackView还提供两种方法,可用于扩展其功能。

configureCell(_:)

AloeStackView中的每一行都包装在一个名为StackViewCellUIView子类中。这个视图用于每行的簿记并管理UI,如分隔符和内边距。

每当向AloeStackView添加或插入行时,就会调用configureCell(_:)方法。这个方法传递给行的新的StackViewCell

您可以重写此方法以根据需要对单元格进行任何自定义,例如支持您添加到AloeStackView的自定义功能或控制屏幕上行的外观。

此方法总是在设置单元格的任何默认值之后调用,因此在方法中进行的任何更改都不会被系统覆盖。

cellForRow(_:)

每当向AloeStackView插入行时,都会调用cellForRow(_:)方法来获取行的单元格。默认情况下,cellForRow(_:)简单地返回一个包含传入行的新的StackViewCell

然而,StackViewCell是一个公开类,可以子类化以根据需要添加自定义行为和功能。若要使AloeStackView使用您的自定义单元格,请重写cellForRow(_:)并返回您自定义子类的实例。

提供自定义的StackViewCell子类可以更细致地控制行显示方式。它还可以与每行一起存储自定义数据,这在支持您添加到AloeStackView的任何功能时可能很有用。

要记住的一件事是,AloeStackView将在从cellForRow(_:)返回单元格后为其应用默认值。因此,如果您需要进一步自定义单元格,请考虑在configureCell(_:)中执行此操作。

当何时扩展AloeStackView

这些方法共同为扩展AloeStackView以添加自定义行为和功能提供了相当大的灵活性。

例如,您可以向AloeStackView添加新方法来控制行的方式,或者支持新的用户交互类型。您可以自定义StackViewCell的属性来控制每一行的单独外观。您可以通过子类化StackViewCell来存储每行的新数据和属性,以便支持您添加的自定义功能。子类化StackViewCell还可以提供对如何显示行的更精细控制。

然而,这种灵活性不可避免地与复杂性和维护成本相交换。 AloeStackView有一个全面且易于使用API,可以支持各种用例。因此,在尝试扩展类以添加新功能之前,最好先看看您需要的特性是否可以通过现有的API实现。这通常可以节省时间和精力,既包括开发自定义功能的开销,也包括持续维护的代价。

当何时使用AloeStackView

简短回答

AloeStackView最适合较短屏幕,内容不超过一屏或两屏的情况。它特别适合接受用户输入、实现表单或由异质视图组成的屏幕。

但是,深入研究AloeStackView的技术细节也很有用,因为这可以帮助更好地理解适当的使用场景。

更多详情

AloeStackView是工具箱中的一个非常有用的工具。其简单灵活的API使您能够快速轻松地构建UI。

UITableViewUICollectionView不同,您可以在AloeStackView中保留视图的强引用,并在任何时间对其进行更改。由于自动布局,这将自动更新整个UI,无需通知AloeStackView更改。

这使得AloeStackView非常适合用于表单和需要用户输入屏幕的用例。在这种情况下,保留对用户正在编辑的字段的强引用,并直接用验证反馈更新UI通常很方便。

AloeStackView没有reloadData方法,也没有任何通知它关于视图更改的方式。这使得它比像UITableView这样的类更不容易出错,更容易调试。例如,如果未通知AloeStackView其所管理视图的基础数据发生更改,它不会崩溃。

由于AloeStackView在内部使用UIStackView,在滚动时不会回收视图。这消除了由不正确回收视图引起的一些常见错误。当用户与之交互时,您也不需要独立维护视图的状态,这使得实现某些类型的UI变得简单。

但是,AloeStackView并不适合所有情况。AloeStackView在屏幕加载时单次遍历整个UI布局。因此,较长的屏幕在第一次显示UI之前会出现明显的延迟。这对用户来说不是很好的体验,并且可以使应用程序响应导航操作的感觉迟钝。因此,AloeStackView不适用于实现包含多于几屏幕内容的应用UI。

放弃视图回收也是一种权衡:虽然AloeStackView编写UI更快,错误更少,但对于较长的屏幕,它的性能会比像UITableView这样的类更差,占用更多内存。因此,AloeStackView通常不适用于包含许多相同类型视图的屏幕,这些视图显示类似的数据。在那种情况下,类如UITableViewUICollectionView通常表现更好。

尽管AloeStackView并不是我们在Airbnb构建iOS UI时使用的唯一基础设施组件,但它对我们很多情况下的工作都非常有价值。我们希望你也觉得它很有用!

安装

AloeStackView可以使用Carthage进行安装。只需将 github "airbnb/AloeStackView" 添加到您的Cartfile。

AloeStackView可以使用CocoaPods进行安装。只需在您的Podfile中添加 pod 'AloeStackView'

贡献

AloeStackView已针对它最初设计的用例完成了所有功能。然而,iOS上的UI开发永远不会是一个已解决的问题,我们预计新的用例将出现,旧的错误将被发现。

因此我们完全欢迎贡献,包括新的功能、功能请求、错误报告和修复。如果您想做出贡献,只需推送一个带有您更改描述的PR。您还可以创建一个GitHub Issue以提交任何错误报告或功能请求。

如果您想联系项目负责人,请随时发送电子邮件。如果您或您的公司发现此库有用,我们非常乐意听听您的意见!

维护者

AloeStackView 由以下人员开发与维护:

Marli Oshlack ([email protected])

Fan Cox ([email protected])

Arthur Pang ([email protected])

贡献者

AloeStackView 还得到了许多 Airbnb 工程师的帮助。

他们包括:Daniel Crampton, Francisco Diaz, David He, Jeff Hodnett, Eric Horacek, Garrett Larson, Jasmine Lee, Isaac Lim, Jacky Lu, Noah Martin, Phil Nachum, Gonzalo Nuñez, Laura Skelton, Cal Stephens, 和 Ortal Yahdav

此外,如果没有 Jordan Harband, Tyler Hedrick, Michael Bachand, Laura Skelton, Dan Federman 和 John Pottebaum 的帮助和支持,开源此项目将无法实现。

许可证

AloeStackView 根据 Apache License 2.0 发布。有关详情,请参阅 LICENSE 文件。

为什么命名为 AloeStackView?

我们喜欢多肉植物,觉得这个名字很舒缓。😉