大多数浏览器和
Developer App 均支持流媒体播放。
-
差量数据源的改进
Diffable 数据源极大幅度简化了管理及升级 collection 和 table view 的相关工作,从而为你的app创造动态化的高响应体验。了解如何使用局部快照来高效创建 iOS 及 iPadOS 的 collection view 列表及大纲,并为在 iPad app 中设置侧边栏提供帮助。我们还将向你展示如何通过 UICollectionViewDiffableDataSource 简化元件重新排序,从而提升编程效率并且更快速地构建 app 界面。 本次分享的内容基于 2019 年“UI数据源提升”,建议优先查看。
资源
相关视频
WWDC21
WWDC20
WWDC19
-
下载
(你好 WWDC 2020) 大家好 欢迎参加 WWDC (差量数据源的改进) 欢迎大家 我叫 Steve Breen 是 UIKit 团队的工程师 在这段视频中 我们要谈谈 iOS 14 差量数据源的改进 在深入讨论内容之前 我想先重点解释一下 伴随样例项目的部分示例项目 一段名为“表情符号管理器”的视频 这个示例在设计中加入了 许多有趣的组件 (水平滚动网格) 在第一段可以看到 有一个水平滚动的表情符号网格 在如今众多 app 当中 这是一个相当常见的设计元素 (框架) 好吧 在表情符号管理器中间的 这一段尤其新颖 这是可展开 可折叠边框风格的用户界面 属于 iOS14 中的新功能 (列表) 最后 在末端一节 有一个看起来很像 UITableView 的设计 就在用 UICollectionView 的中间 好 这就是表情符号管理器 这次视频主要是围绕它而展开的 (差量数据源) 好 来回顾一下 iOS13 中引入的 差量数据源 通过添加新的 snapshot 数据类型 可高度简化用户界面状态的管理 (简化用户界面状态:自动化动画 无需批次更新) snapshot 封装整个用户界面状态的方法 就是通过使用唯一的片段和项标识符 因此 在更新 UICollectionView 时 首先要创建一个新 snapshot 用当前用户界面状态填充它 并将其应用于数据源 差量数据源计算差异 并自动使事物动画化 而无需 app 开发人员 进行任何额外的工作 所以我们在 WWDC 2019 视频中 详细介绍了这款 API 《用户界面数据源的改进 》 我鼓励你们去看看那个视频 来了解更多内容 因此 针对 iOS14 我们在差量数据源的基地上 构建了两个新特性 片段 snapshot 以及一级重新排序支持 让我们聊聊片段 snapshot (片段 snapshot) (新单个片段数据) (可组合数据源、分层数据) 对于 iOS14 我们将在现有的 snapshot 类型 旁边添加新的片段 snapshot 并称之为“片段 snapshot ” 顾名思义 片段 snapshot 将单个片段的数据 封装在 UICollectionView 之中 (单个片段数据) 这种优化源自两个出发点 首先 允许数据源更易于组合成 片段大小的语块 (可组合数据源) 其次 允许分层数据建模 (分层数据) 这是支持渲染框架样式用户界面 所必需的内容 也是 iOS14 中常见的可视化设计 让我们回到表情符号管理器 并看看如何在示例 app 中 使用片段 snapshot 来构建此功能 (片段 snapshot) 首先 在水平滚动片段中 我们使用的是单片段 snapshot 来对这里的内容完全建模 接下来 在第二片段中可以看到 可展开-可折叠的边框样式片段 第二个片段 snapshot 用于对该分层数据建模 最后 在列表片段中 我们再次使用第三个片段 snapshot 对这一节的内容进行建模 对于表情符号管理器来说 我们用三个不同的片段 snapshot 组成差量数据源 每个 snapshot 表示单节的内容 让我们来看看其中一些 API (新片段 snapshot) 这里的一小片段属于 新的片段 snapshot API 然后查看整个 API 的 SDK
不过请注意 在我们提到 iOS13 中 所引入的原始 snapshot 类型中 使用的术语“ snapshot ” 其中包括片段和项标识符 我们还将使用术语“片段 snapshot ” 来指代新的 IOS14 类型 来看一下这个新的片段 snapshot 类型 我们可以发现它通用于项类型
注意 这里没有任何类型的 片段标识符类型 片段 snapshot 本身就不知道 它们所表示的片段
所以要想向片段 snapshot 中添加内容 就要使用 “附加” API
现在则要注意可选的父参数 当父参数可用时 则允许在分层数据 所需的部分 snapshot 中创建父-子关系
现在我们向用 UICollectionView DiffableDataSource 添加了两个新 API 以适应新片段 snapshot 类型 首先 我们有一个新的“应用”方法 需要片段 snapshot 和片段标识符
第二个新 API 允许检索 表示特定片段内容的片段 snapshot 接下来 让我们填充一个代码片段 来展示如何使用 snapshot 以及片段 snapshot 来构建 CollectionView 的内容 首先 我们将通过将 snapshot 应用到 差量数据源 以按所需的顺序添加片段 在这里可以看到特定顺序的片段 “最近” “首要”和“建议” 在拥有所需的片段顺序之后 直接将片段 snapshot 应用于每个片段 来为每个片段填充项目 让我们来看看 如何使用此片段 snapshot API 来创建分层数据 首先 向片段添加一些根项 使用 “附加” API 的 笑脸、自然、食物等等
回想一下 “附加”方法 其中有可选的“父”参数 要是不可用时 则意味着 要将这些项应用到根目录 因此想要配置父-子关系 就要把一些子项追加到父项中 在本例中 父项是“食物” 就这样 我们创建了一个片段 snapshot 它对 app 的分层数据进行建模 好吧 现在应该弄清楚了 片段 snapshot 能够表示分层数据 其实很多时候 都很容易理解 这些只是分层数据的一部分 (分层数据的子 snapshot 推理) 在这个代码片段中 我们想要知道的是 与特定父项相关的所有子项 有选择性地 将父级包括在结果片段 snapshot 中 (展开和折叠项目的展开状态保持不变) 那么 接下来 让我们聊聊展开状态 (调用 APPLY(_:TO) 来提交更改自动动画) 展开状态是作为片段 snapshot 状态的 一部分来进行管理的 在建立 snapshot 以供显示时 你可以轻松通过设置 该项的父级展开状态以确定 最初的子内容是否可见 还可以查询 snapshot 以确定项是否处于展开或折叠状态 请注意 要想更改片段 snapshot 的展开状态 除非真正将其应用到差量数据源中 否则不可适用 (展开状态变化) (用户交互将展开/折叠) (展开/折叠通知) 当用户与配置有新单元框架 显示附件的框架样式用户界面交互时 框架将自动使用新的展开状态 更新片段 snapshot 并将该片段 snapshot 应用于数据源 通知这些因用户交互 而导致的展开状态更改 通常是很管用的 例如 你的设计可能需要 一个永不崩溃的特定父元素 (支持延迟加载) 为了支持这一功能 差量数据源提供了新的 API 用于为 app 提供编程控制 以控制由用户交互导致的 展开状态更改 (片段 snapshot 处理程序) 好了 这些就是新的 API 首先 我们注意到差量数据源 有一个新属性 称为 SectionSnapshotHandlers 新的 SectionSnapshotHandlers 是一个通用于项的结构 包含五个可选闭包 为了处理前面提到的需求 我们可以提供 一个 shouldCollapseItem 处理程序 它将为特定父级返回到“假” 以避免折叠框架 然后我们还通过使用 API snapshotForExpandingParen 以支持对珍贵内容的延迟加载 因此在获取初始片段 snapshot 中的 珍贵内容时 对于最小化所加载的内容是很有用的 让你可以根据当前子 snapshot 的状态 按需加载该内容 片段 snapshot 就到此为止 (重新排序支持) 接下来 我们来讨论一下 添加到差量数据源的 API 重新排序 (重新排序支持、 自动化 snapshot 更新、事务) 由差量数据源带来的其中一项改进 是能够使用唯一的项标识符 为 CollectionView 的数据建模 有了这些唯一的项目标识符 就可以让框架基于用户交互 代表 app 自动提交重新排序更改 但这还不够 需要通知我们的 app 发生了用户发起的重新排序交互 以便使它能够将新可视化顺序 保持在 app 的备份存储中 也就是它的最终真实源 (可选闭包) 为了支持重新排序 差量数据源现在有了一个新属性 ReorderingHandlers (自动化重新排序:实现 canReorderItem / didReorder) 此结构将包含三个可选闭包 canReorderItem willReorder 和 didReorder 要通过这个新 API 启用重新排序 首先需要提供一个 canReorderItem 闭包 当用户尝试启动重新排序交互时 将调用此函数 如果返回到“真” 则允许开始重新排序交互 当用户完成重新排序交互时 将会调用 didReorder 闭包 以允许 app 提交 新排序状态到 app 的真实源内 注意 必须同时提供 canReorderItem 和 didReorder 闭包 以启用重新排序功能 好 didReorder 闭包 将向 app 传递一个新类型 NSDiffableDataSourceTransaction (重新排序事务) 因此 事务提供了 对差量数据源执行更新 所需的全部信息 首先 让我们看一下 NSDiffableDataSourceTransaction 这种类型提供四个基本信息 首先是 initialSnapshot 这是在应用更新之前 差量数据源的状态
接下来就是 finalSnapshot 这是在应用更新之后 差异数据状态 我们可以直接使用这个 snapshot 中的 这些项目标识符 来确定新的排序 并将其提交给 app 的真实源 另外 我们还可以看到 提供了 Swift 标准资料库 CollectionDifference 如果你的 app 有这样的数据类型 比如以“数组”作为真实源 就可以将 CollectionDifference 直接应用到该数据类型 最后 我们看到一个片段事务列表 里面提供涉及到重新排序更新中 所有片段的单独详细信息 片段事务相当相似 要为重新排序更新中 涉及的每个片段提供事务 首先 我们可以检查已应用于 sectionTransaction 的 sectionIdentifier 而且还看到了初始和最终的 片段 snapshot 以及特定于此片段更新的 CollectionDifference 好吧 我们来看一个示例 (更新我们的真实源) 在这里 我们的备份存储 是一个项目数组 为单个片段专题视图提供真实源
使用随事务提供的 Swift 标准资料库 CollectionDifference 我们将创建一个新的备份存储 并直接更新真实源 好了 今天的视频就到此结束 涵盖了我们为 iOS14 差量数据源 带来的所有改进 (下一步 下载示例 app) 请下载示例 app 其中有很多关于 如何使用这些新 API 的示例 (更新你的 app) 并将此代码作为更新 app 的起点 以利用这些新 API (观看相关视频) 并请观看本次谈话中提到的 相关视频 以加深理解 感谢收看
-
-
3:24 - NSDiffableDataSourceSectionSnapshot
// NSDiffableDataSourceSectionSnapshot public struct NSDiffableDataSourceSectionSnapshot<ItemIdentifierType> where ItemIdentifierType : Hashable { public init() public init(_ snapshot: NSDiffableDataSourceSectionSnapshot<ItemIdentifierType>) public mutating func append(_ items: [ItemIdentifierType], to parent: ItemIdentifierType? = nil) public mutating func insert(_ items: [ItemIdentifierType], before item: ItemIdentifierType) public mutating func insert(_ items: [ItemIdentifierType], after item: ItemIdentifierType) public mutating func delete(_ items: [ItemIdentifierType]) public mutating func deleteAll() public mutating func expand(_ items: [ItemIdentifierType]) public mutating func collapse(_ items: [ItemIdentifierType]) public mutating func replace(childrenOf parent: ItemIdentifierType, using snapshot: NSDiffableDataSourceSectionSnapshot<ItemIdentifierType>) public mutating func insert(_ snapshot: NSDiffableDataSourceSectionSnapshot<ItemIdentifierType>, before item: (ItemIdentifierType)) public mutating func insert(_ snapshot: NSDiffableDataSourceSectionSnapshot<ItemIdentifierType>, after item: (ItemIdentifierType)) public func isExpanded(_ item: ItemIdentifierType) -> Bool public func isVisible(_ item: ItemIdentifierType) -> Bool public func contains(_ item: ItemIdentifierType) -> Bool public func level(of item: ItemIdentifierType) -> Int public func index(of item: ItemIdentifierType) -> Int? public func parent(of child: ItemIdentifierType) -> ItemIdentifierType? public func snapshot(of parent: ItemIdentifierType, includingParent: Bool = false) -> NSDiffableDataSourceSectionSnapshot<ItemIdentifierType> public var items: [ItemIdentifierType] { get } public var rootItems: [ItemIdentifierType] { get } public var visibleItems: [ItemIdentifierType] { get } }
-
4:20 - UICollectionViewDiffableDataSource Additions for Section Snapshots
// UICollectionViewDiffableDataSource additions for iOS 14 extension UICollectionViewDiffableDataSource<Item, Section> { func apply(_ snapshot: NSDiffableDataSourceSectionSnapshot<Item>, to section: Section, animatingDifferences: Bool = true, completion: (() -> Void)? = nil) func snapshot(for section: Section) -> NSDiffableDataSourceSectionSnapshot<Item> }
-
4:43 - Using Snapshots and Section Snapshots together
// Example of using snapshots and section snapshots together func update(animated: Bool=true) { // Add our sections in a specific order let sections: [Section] = [.recent, .top, .suggested] var snapshot = NSDiffableDataSourceSnapshot<Section, Item>() snapshot.appendSections(sections) dataSource.apply(snapshot, animatingDifferences: animated) // update each section's data via section snapshots in the existing position for section in sections { let sectionItems = items(for: section) var sectionSnapshot = NSDiffableDataSourceSectionSnapshot<Item>() sectionSnapshot.append(sectionItems) dataSource.apply(sectionSnapshot, to: section, animatingDifferences:animated) } }
-
5:18 - Creating hierarchical data with NSDiffableDataSourceSectionSnapshot
// Create hierarchical data for our Outline var sectionSnapshot = ... sectionSnapshot.append(["Smileys", "Nature", "Food", "Activities", "Travel", "Objects", "Symbols"]) sectionSnapshot.append(["🥃", "🍎", "🍑"], to: "Food")
-
6:01 - Child Section Snapshots
let childSnapshot = sectionSnapshot.snapshot(for: parent, includingParent: false)
-
6:11 - Section Snapshot Expand / Collapse API
struct NSDiffableDataSourceSectionSnapshot<Item: Hashable> { func expand(_ items: [Item]) func collapse(_ items: [Item]) func isExpanded(_ item: Item) -> Bool }
-
7:21 - Section Snapshot Handlers
// Section Snapshot Handlers: handling user interactions for expand / collapse state changes extension UICollectionViewDiffableDataSource { struct SectionSnapshotHandlers<Item> { var shouldExpandItem: ((Item) -> Bool)? var willExpandItem: ((Item) -> Void)? var shouldCollapseItem: ((Item) -> Bool)? var willCollapseItem: ((Item) -> Void)? var snapshotForExpandingParent: ((Item, NSDiffableDataSourceSectionSnapshot<Item>) -> NSDiffableDataSourceSectionSnapshot<Item>)? } var sectionSnapshotHandlers: SectionSnapshotHandlers<Item> }
-
8:52 - Reordering Handlers
// Diffable Data Source Reordering Handlers extension UICollectionViewDiffableDataSource { struct ReorderingHandlers { var canReorderItem: ((Item) -> Bool)? var willReorder: ((NSDiffableDataSourceTransaction<Section, Item>) -> Void)? var didReorder: ((NSDiffableDataSourceTransaction<Section, Item>) -> Void)? } var reorderingHandlers: ReorderingHandlers }
-
9:45 - Diffable Data Source Transactions
// NSDiffableDataSourceTransaction struct NSDiffableDataSourceTransaction<Section, Item> { var initialSnapshot: NSDiffableDataSourceSnapshot<Section, Item> { get } var finalSnapshot: NSDiffableDataSourceSnapshot<Section, Item> { get } var difference: CollectionDifference<Item> { get } var sectionTransactions: [NSDiffableDataSourceSectionTransaction<Section, Item>] { get } } struct NSDiffableDataSourceSectionTransaction<Section, Item> { var sectionIdentifier: Section { get } var initialSnapshot: NSDiffableDataSourceSectionSnapshot<Item> { get } var finalSnapshot: NSDiffableDataSourceSectionSnapshot<Item> { get } var difference: CollectionDifference<Item> { get } }
-
11:07 - Diffable Data Source Reordering Example
dataSource.reorderingHandlers.didReorder = { [weak self] transaction in guard let self = self else { return } if let updateBackingStore = self.backingStore.applying(transaction.difference) { self.backingStore = updatedBackingStore } }
-
-
正在查找特定内容?在上方输入一个主题,就能直接跳转到相应的精彩内容。
提交你查询的内容时出现错误。请检查互联网连接,然后再试一次。