ストリーミングはほとんどのブラウザと
Developerアプリで視聴できます。
-
Diffable Data Sourcesの応用
Diffable Data Sourcesは、CollectionやTable Viewの管理および更新に必要な作業を劇的に簡素化し、ダイナミックでレスポンシブルなUXを実現します。 Section Snapshotを使って、iOSやiPadOSでリストやアウトラインのCollection Viewを効率的に構築し、iPadのサイドバーの実装をサポートします。また、インターフェイスの作成をより迅速に効率化するために、UICollectionViewDiffableDataSourceを使ってセルの並び替えを簡素化する方法をご説明します。 このセッションは、2019年の "Advances in UI Data Sources" を基に作成されました。
リソース
関連ビデオ
WWDC21
WWDC20
WWDC19
-
ダウンロード
こんにちは WWDCへようこそ “Diffable Data Sourcesの進化” ようこそ 私の名前はスティーブ・ブリーン UIKitチームのエンジニアです iOS 14のDiffableDataSourceの進歩について お話します コンテンツに入る前に 「Emoji Explorer」というこの companion sample projectの一部について話します このサンプルには多くの興味深い コンポーネントがあります 最初のセクションには水平方向にスクロールする 絵文字のグリッドがあります 今日の多くのアプリで 一般的なデザイン要素です Emoji Explorerの中央にある このセクションは特に斬新です iOS 14で初めて搭載された expandable collapsible outline styled UIです この最後のセクションは UI Collection Viewの真ん中で UI Table Viewのように見えるデザインです それがEmoji Explorerなのです 詳しくは のちほど述べます iOS 13で導入されたDiffableDataSourceは 新しいスナップショットデータタイプの 追加により UIステートの管理を大幅に簡素化しました スナップショットは一意のセクション およびアイテム識別子を使用して UIステート全体をカプセル化します したがって UI Collection Viewを 更新するときは 現在のUIステートでそれを入力して データソースに適用します DiffableDataSourceは アプリケーション開発者の追加作業を必要とせず 差異を計算し 自動的にアニメーション化します このAPIについてはWWDC 2019の 「Advances in UI Data Sources」で取り上げました 詳細については そちらを確認ください iOS 14では2つの新機能を備えた DiffableDataSourceを基盤として構築しました セクションスナップショット それにファーストクラスの リオーダリングサポートです セクションスナップショットについて お話しましょう
iOS 14では既存のスナップショットのすぐ横に 新しいセクションスナップショットが 追加されます 名前が示すように セクションスナップショットは UICollectionViewのsingleセクションデータを カプセル化します この機能強化には2つの理由があります まず データソースをセクションサイズの チャンクに構成できるようになること 2番目に outline style UIの レンダリングに必要な― 階層データのモデリングを 可能にすることです これはiOS 14全体に共通する ビジュアルデザインです では Emoji Explorerに戻ります セクションスナップショットを使用して これをサンプルアプリで構築する方法を示します まず水平方向にスクロールするセクションで singleセクションスナップショットを使用して コンテンツを完全にモデル化しています 次に expandable collapsible outline style sectionが表示される2番目のセクションで 2番目のセクションスナップショットは 階層データをモデル化するために使用されます 最後にリストセクションで このセクションのコンテンツを 3番目のセクションスナップショットで モデル化します Emoji Explorerでは それぞれがsingleセクションのコンテンツを表す 3つの異なるセクションスナップショットから DiffableDataSourceを作成します いくつかのAPIを見てみましょう ここに新しいSection スナップショット APIの スニペットを示しています API全体のSDKを確認してください
セクションとアイテム識別子を含むiOS 13で 導入された元のスナップショットを指すときは 「スナップショット」という用語を 使用することに注意してください 「セクションスナップショット」という用語は iOS 14の新しいタイプを指すために使用します この新しいセクションスナップショットタイプを 見ると アイテムタイプよりも 汎用的であることがわかります
あらゆる種類のセクション識別子タイプが ないことに注意してください セクションスナップショットは どのセクションを表しているのかを知りません
セクションスナップショットにコンテンツを 追加するにはappend APIを使用します
ここでオプションの 親パラメーターに注目してください 挿入すると階層データのモデル化に必要な― セクションスナップショットに 親子関係を作成できます
ここで新しいセクションスナップショット タイプに対応するために UICollectionViewDiffableDataSourceに 2つの新しいAPIを追加しました まず セクションスナップショットと セクション識別子を取得する 新しい「apply」メソッドがあります
2つ目の新しいAPIは 特定のセクションのコンテンツを表す セクションスナップショットを取得できます 次にスナップショットと セクションスナップショットを一緒に使用して Collection Viewのコンテンツを構築する方法を 示すコードスニペットを具体化しましょう 最初にスナップショットを DiffableDataSourceに適用することにより セクションを希望の順序で追加します ここではセクションが 特定の順序で表示されています 「recent」「top」「suggested」 の順になっています ここで目的のセクションの順序が整ったら セクションスナップショットを 各セクションに直接 適用して これらの各セクションのアイテムを設定します このセクションスナップショットAPIを使用して 階層データを作成する方法を見てみましょう まず ルートアイテムを セクションに追加します append APIを使用した 「smileys」「Nature」「Food」など
appendメソッドにはオプションの appendパラメータがあります それが提供されていない場合 これらのアイテムを ルートに適用することを意味します したがって 親子関係を構成するために いくつかの子アイテムを 親アイテムに追加します この例では 親アイテムは「Food」です アプリケーションの 階層データをモデル化した― セクションスナップショットを作成しました もうお分かりでしょうが セクションスナップショットは 階層データを表すことができます この階層データのほんの一部を 推論することは非常に便利です したがって このコードスニペットでは 特定の親アイテムに関連する すべての子アイテムを取得します 親アイテムには必要に応じて 結果のセクションスナップショットの 親も含めます
次は拡張ステートについて 少しお話しましょう 拡張ステートはセクションスナップショットの 一部として管理されます 表示用スナップショットを作成する場合 そのアイテムの親の 展開ステートを設定することにより 子コンテンツが最初に表示されるかを 簡単に判断できます スナップショットをクエリしてアイテムが 展開されているか否かを確認できます セクションスナップショットの拡張ステートを ミューテートした場合 実際にそれをDiffableDataSourceに適用するまで 適用されないことに注意してください ユーザが新しいCell Outline Disclosure Accessoryで構成された― outline styled UIを操作すると フレームワークはセクションスナップショットを 新しい展開ステートで自動的に更新し そのセクションスナップショットが データソースに適用されます これらのユーザインタラクションによって 引き起こされた― 拡張ステートの変化に関して 通知を受けると大変 便利です たとえば 特定の親が決して折りたたまれない 必要があるデザインがあるとします これをサポートするため DiffableDataSourceには ユーザインタラクションによる 拡張ステートの変更を プログラムで制御する新しいAPIがあります では これらの新しいAPIを 紹介します まず我々はDiffableDataSourceに― 「SectionSnapshotHandlers」という 新しいプロパティがあることに気付きます 新しいSectionSnapshotHandlers型は 構造体であり これはアイテムが包括されていて 5つのオプションのクロージャーを含みます 先ほど述べた要件を処理するには アウトラインの折りたたみを回避するため 特定の親に対して「false」を返す― shouldCollapseItemハンドラーを提供できます API snapshotForExpandingParentを 使用して 高価なコンテンツの遅延読み込みのサポートも 提供しています そのためコンテンツを取得するのに コストがかかる場合に 「初期セクションスナップショット」に読まれる コンテンツの量を最小限に抑えるのに役立ちます そのため 現在の子スナップショットの ステートに応じて 必要に応じてそのコンテンツをロードできます これでセクションスナップショットは終了です
次に DiffableDataSourceに追加された ReorderingSupport APIです
DiffableDataSourceがもたらす進歩の1つは CollectionViewのデータを 一意のアイテム識別子でモデル化する機能です これらの一意のアイテム識別子を使用すると フレームワークはユーザの操作に基づいて アプリケーションに代わって 自動的に並べ替えの変更をコミットできます しかし これだけでは十分ではありません 私たちのアプリケーションは― ユーザが開始した並べ替え操作が 行われたことを通知する必要があります これにより新しい表示順が アプリケーションのバッキングストアに残り これが最終のsource of truthです そのため 並べ替えをサポートするために DiffableDataSourceに新しいプロパティ ReorderingHandlersが追加されました
これは3つのオプションのクロージャーを含む 構造体です それは canReorderItem と willReorder didReorderです この新しいAPIを介して 並べ替えを有効にするには 最初にcanReorderItemクロージャーを 提供する必要があります これはユーザが並べ替え操作を 開始しようとしたときに呼び出されます 「true」が返された場合 並べ替え操作を開始できます ユーザが並べ替え操作を完了すると didReorderクロージャーが呼び出され アプリケーションが新しい並び替えステートを source of truthにコミットできるようになります 並べ替え機能を有効にするには 「canReorderItem」と 「didReorderクロージャー」の両方を 提供する必要があることに注意してください didReorderクロージャーは― アプリケーションに新しいタイプ NSDiffableDataSourceTransactionを 渡します そのためトランザクションは DiffableDataSourceに対して 実行されている更新について推論するのに必要な すべての情報を提供します まず NSDiffableDataSourceTransactionを 確認してみましょう このタイプは 4つの基本的な情報を提供します まず初期スナップショットがあります これは更新が適用される前の DiffableDataSourceのステートです
次に最終スナップショットがあります これが更新が適用された後の DiffableDataSourceのステートです このスナップショットから これらのアイテム識別子を直接使用して アプリケーションのsource of truthにコミットする新しい順序を決定できます さらにSwift標準ライブラリCollection Differenceも提供されていることがわかります そしてアプリケーションにsource of truthとして 「Array」などのデータ型がある場合は そのCollectionDifferenceを それに直接 適用できます そして最後に この並べ替え更新に関係する すべてのセクションについて セクションごとの詳細を提供する トランザクションのリストが表示されます セクショントランザクションは 非常に似ています それらは並べ替え更新に関係する セクションごとに 1つのセクショントランザクションが 用意されています まずこのセクショントランザクションが 適用された― セクションアイデンティファイヤーを 調査できます またこのセクションの更新に固有の 「CollectionDifference」とともに 初期および最終セクションスナップショットが あることもわかります 例を見てみましょう ここでバッキングストアはsingleセクション― collectionViewのsource of truthを提供する アイテムの配列です
トランザクションで提供されるSwift標準 ライブラリCollection Differenceを使用して 新しいバッキングストアを作成し source of truthを直接 更新します 以上となります iOS 14のDiffableDataSourceにもたらした すべての進歩を ご説明しました
サンプルApp.をダウンロードください 新しいAPIの多くの使用例をご覧頂けます そして このコードを出発点として 使用して これらの新しいAPIを活かせるよう App.を更新します 関連動画をご覧頂くと より深くご理解頂けます ご視聴ありがとうございました
-
-
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 } }
-
-
特定のトピックをお探しの場合は、上にトピックを入力すると、関連するトピックにすばやく移動できます。
クエリの送信中にエラーが発生しました。インターネット接続を確認して、もう一度お試しください。