AloeStackView

一个简单的类,用于方便地布局视图集合,同时利用 Auto Layout 的强大功能。

Carthage compatible Version License Platform

简介

AloeStackView 是一个类,允许以垂直或水平列表形式布局视图集合。从广义上讲,它类似于 UITableView,但其实现方式截然不同,并且它做出了不同的权衡。

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

我们发现 AloeStackView 是一个有用的基础设施组件,希望您也觉得它有用!

目录

特性

系统要求

示例 App

该仓库包含一个简单的 示例 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(...)
  }

}

AloeStackViewControllerUITableViewControllerUICollectionViewController 等类非常相似,因为它为您创建和管理 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。Auto Layout 将确保 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 的 left 和 right 属性。

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

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 中保持对视图的强引用,并在任何时候对其进行更改。借助 Auto Layout,这将自动更新整个 UI - 无需将更改通知 AloeStackView

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

AloeStackView 没有 reloadData 方法,也没有任何方法可以将其更改通知您的视图。这使其比 UITableView 之类的类更不容易出错且更易于调试。例如,如果未将对其管理的视图的底层数据的更改通知 AloeStackView,则它不会崩溃。

由于 AloeStackView 在底层使用 UIStackView,因此它不会在您滚动时回收视图。这消除了由于未正确回收视图而引起的常见错误。您也不需要独立维护用户与视图交互时的视图状态,这使得实现某些类型的 UI 更简单。

但是,AloeStackView 并非在所有情况下都适用。当屏幕加载时,AloeStackView 会在一次遍历中布局整个 UI。因此,较长的屏幕会在首次显示 UI 之前开始出现明显的延迟。这对用户来说不是很好的体验,并且会使应用感觉对导航操作无响应。因此,在实现内容超过一两个屏幕的 UI 时,不应使用 AloeStackView

放弃视图回收也是一种权衡:虽然 AloeStackView 可以更快地编写 UI 并且不易出错,但对于较长的屏幕,它的性能会更差,并且比 UITableView 之类的类使用更多的内存。因此,AloeStackView 通常不适用于包含许多相同类型视图(所有视图都显示相似数据)的屏幕。在这些情况下,UITableViewUICollectionView 之类的类通常表现更好。

安装

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

可以使用 CocoaPods 安装 AloeStackView。只需将 pod 'AloeStackView' 添加到您的 Podfile。

贡献

对于最初旨在解决的用例,AloeStackView 的功能已经完备。但是,iOS 上的 UI 开发永远不是一个已解决的问题,我们预计会出现新的用例并发现旧的错误。

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

如果您想联系,请随时发送电子邮件给项目维护者。如果您或您的公司发现此库有用,我们很乐意收到您的来信!

维护者

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

Marli Oshlack (marli@oshlack.com)

Arthur Pang

贡献者

AloeStackView 受益于许多其他工程师的贡献:

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?

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