WWDC Quick Look 💓 By SwiftGGTeam
Advances in diffable data sources

Advances in diffable data sources

元の動画を見る

ハイライト

iOS 14 では、セクション スナップショットと並べ替えハンドラーが Diffable データ ソースに追加され、UICollectionView がセクションごとに階層データを結合し、アウトラインの展開と折りたたみを自動的に処理し、ユーザーがドラッグした順序をアプリケーション データ ソースに送信できるようになります。

主要内容

Diffable Data Source は、iOS 13 でコレクション ビューとテーブル ビューを更新する際の古い問題を解決します。開発者はまず現在の UI 状態を記述し、次にフレームワークに差異とアニメーションを計算させます。このモデルはフラット リストにはうまく機能しますが、Emoji Explorer のようなページはより複雑です。また、水平スクロールの絵文字グリッド、拡張可能なアウトライン領域、テーブル ビューのようなリスト領域もあります。

iOS 14 では、アップデートの粒度がセクションに分割されています。セクション スナップショットは単一のコンテンツを表します。 UICollectionView セクションであり、データ ソースは複数のセクション スナップショットから結合できます。水平グリッド、アウトライン、リスト セクションはそれぞれ独自のコンテンツを保持し、それらを同じコレクション ビューに入れることができます。

この講演の焦点は階層データです。セクションのスナップショット append(_:to:) オプションのparentパラメータを使用して親子関係を確立します。 expand(_:) そして collapse(_:) 展開状態を管理します。新しいアウトライン開示アクセサリを使用すると、ユーザーが項目をクリックするか折りたたむと、フレームワークがセクション スナップショットを更新し、それをデータ ソースに再適用します。

もう 1 つの新しい追加機能はドラッグ ソートです。 Diffable データ ソースはすでに一意のアイテム識別子を使用して UI 状態を表しており、iOS 14 では引き続き提供されます。 reorderingHandlers そして取引。ユーザーがドラッグした後、アプリケーションは変更前後のスナップショットを取得します。 CollectionDifference と各セクションのトランザクションを処理し、新しいシーケンスを独自のバッキング ストアに書き込みます。

詳細

セクション スナップショットを使用してセクションを表現する

(03:24) NSDiffableDataSourceSectionSnapshot セクション識別子ではなく項目識別子のみを汎用化します。どのセクションに属しているかは関係なく、項目のグループと項目間の階層関係のみを説明します。

public struct NSDiffableDataSourceSectionSnapshot<ItemIdentifierType> where ItemIdentifierType : Hashable {
    public init()
    public mutating func append(_ items: [ItemIdentifierType], to parent: ItemIdentifierType? = nil)
    public mutating func expand(_ items: [ItemIdentifierType])
    public mutating func collapse(_ items: [ItemIdentifierType])
    public func isExpanded(_ item: ItemIdentifierType) -> Bool
    public func parent(of child: ItemIdentifierType) -> ItemIdentifierType?
    public var rootItems: [ItemIdentifierType] { get }
    public var visibleItems: [ItemIdentifierType] { get }
}

キーポイント:

  • ItemIdentifierType に準拠する必要があります Hashable これは、比較可能なデータ ソースが一意のアイテム ID に依存しているためです。
  • append(_:to:) 親が渡されない場合、項目はルート レベルに配置されます。
  • expand(_:) そして collapse(_:) セクションスナップショットの展開状態を変更します。
  • visibleItems 現在の展開状態で表示される項目のみを返します。アウトライン表示結果のデバッグに適しています。

最初にセクションを配置し、次に各セクションの内容を入力します

(04:43) 通常のスナップショットはセクションの順序を担当し、セクション スナップショットは各セクションの内容を担当します。このスピーチは 3 つのセクションで構成されています。 recenttop、 そして suggested この役割分担を実証するために。

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)
   }
}

キーポイント:

  • NSDiffableDataSourceSnapshot<Section, Item> セクション全体の順序を決定するために今でも使用されています。
  • それぞれ section 独自のものを作成する NSDiffableDataSourceSectionSnapshot<Item>
  • dataSource.apply(sectionSnapshot, to: section, ...) 部分的なコンテンツを指定されたセクションに適用します。
  • 複数のセクションが干渉しにくく、グリッド、アウトライン、リストを同時に持つページに適した書き方です。

アウトラインの親子関係を確立する

(05:18) 階層データは同じものを通じて確立されます append API。まずルート ノードを追加し、次に指定された親に子ノードを追加します。

// Create hierarchical data for our Outline

var sectionSnapshot = ...

sectionSnapshot.append(["Smileys", "Nature",
                        "Food", "Activities",
                        "Travel", "Objects", "Symbols"])

sectionSnapshot.append(["🥃", "🍎", "🍑"], to: "Food")

キーポイント:

  • 最初のセクション append には親がないため、これらの文字列はルート ノードです。
  • 2 番目の段落 append 指定します to: "Food"、3 つの絵文字は の子ノードになります。 Food
  • アプリケーションレベルのデータを記述したセクションスナップショットを取得できるように、トランスクリプトが明示的に記述されています。

(06:01) 親の子を個別に処理する必要がある場合は、セクション スナップショットから子のスナップショットを取得できます。

let childSnapshot = sectionSnapshot.snapshot(for: parent, includingParent: false)

キーポイント:

  • snapshot(for:includingParent:) 指定された親の下の階層の一部のみを取得します。
  • includingParent: false これは、親自体が結果に含まれないことを意味します。 ・本APIは、アウトラインの部分データのみを更新または確認する場合に適しています。

展開、折りたたみ、遅延読み込みの制御

(06:11) 展開状態はセクション スナップショットの状態でもあります。変更後、インターフェイスが更新される前に、それをデータ ソースに再度適用する必要があります。

struct NSDiffableDataSourceSectionSnapshot<Item: Hashable> {
   func expand(_ items: [Item])
   func collapse(_ items: [Item])
   func isExpanded(_ item: Item) -> Bool
}

キーポイント:

  • expand(_:) 指定された項目の展開状態を設定します。
  • collapse(_:) 指定された項目の折りたたまれた状態を設定します。
  • isExpanded(_:) 現在の項目が展開されているかどうかを判断するために使用されます。
  • 逐語的リマインダー: セクション スナップショットのみを変更し、Diffable データ ソースに適用しない場合、変更はインターフェイスに表示されません。

(07:21) ユーザーがアウトライン開示アクセサリをクリックすると、フレームワークによってセクションのスナップショットが更新されます。インターセプト、監視、または遅延ロードが必要な場合は、次を使用します。 sectionSnapshotHandlers

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>

}

キーポイント:

  • shouldExpandItem そして shouldCollapseItem ユーザー操作の続行が許可されるかどうかを決定します。
  • willExpandItem そして willCollapseItem 状態の変化を記録するのに適しています。
  • snapshotForExpandingParent 親が展開しようとしているときに新しいセクションのスナップショットを返します。これは、高価な子をオンデマンドでロードするのに適しています。
  • トランスクリプトで示された例は、親が決して崩壊しないようにし、必要な場合にのみ高価なコンテンツをロードすることです。

トランザクションを使用してドラッグ アンド ドロップの並べ替えを送信する

(08:52) Diffable Data Source の並べ替えエントリは次のとおりです。 reorderingHandlers。この講演では、並べ替えを有効にするには次の両方が必要であると明示的に述べています。 canReorderItem そして didReorder 提供されること。

extension UICollectionViewDiffableDataSource {

  struct ReorderingHandlers {
    var canReorderItem: ((Item) -> Bool)?
    var willReorder: ((NSDiffableDataSourceTransaction<Section, Item>) -> Void)?
    var didReorder: ((NSDiffableDataSourceTransaction<Section, Item>) -> Void)?
  }

  var reorderingHandlers: ReorderingHandlers
}

キーポイント:

  • canReorderItem ユーザーがドラッグを開始しようとしたときに呼び出され、ドラッグは次の場合にのみ許可されます。 true が返されます。
  • didReorder ドラッグが完了した後に呼び出され、新しい順序がアプリケーションのデータ ソースに書き戻されます。
  • トランスクリプトでは、フレームワークは一意のアイテム識別子に基づいてインターフェイス層で並べ替えの変更を自動的に送信できるが、アプリケーションは依然としてバッキング ストアを更新する必要があると説明しています。

(09:45) 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 }
}

キーポイント:

  • initialSnapshot 更新前のデータ ソースの状態です。
  • finalSnapshot 更新されたデータ ソースの状態です。
  • difference Swiftの標準ライブラリです CollectionDifference
  • sectionTransactions 影響を受けるセクションごとにローカルな変更を提供します。
  • トランスクリプト 適用される信頼できる情報源が ArrayCollectionDifference この配列に直接適用できます。

重要ポイント

  • 展開可能なカテゴリ ブラウザを作成: フォルダ、ブックマーク カテゴリ、または製品カテゴリをアウトラインに配置します。実行する価値がある理由: このセッションは、セクション スナップショットが親子階層を表現できること、および展開された状態がアウトライン開示アクセサリによって駆動されることを明確に示しています。開始方法: 使用する append(rootItems) ルートノードを配置してから使用します append(children, to: parent) 子ノードを作成し、最後にセクション スナップショットを対応するセクションに適用します。

  • 複雑なホームページを複数の独立したセクションに分割: たとえば、最近使用したアイテム、人気のあるアイテム、および推奨コンテンツのセクション スナップショットを維持します。実行する価値がある理由: スピーチ内の Emoji Explorer は 3 つの異なるセクションのスナップショットで構成されており、各スナップショットは 1 つのセクションのコンテンツを表します。開始方法: 初めて使用する NSDiffableDataSourceSnapshot セクションシーケンスを追加し、ループして作成して適用します NSDiffableDataSourceSectionSnapshot 各セクションごとに。

  • アウトラインのオンデマンド読み込み: ユーザーが親ノードを展開したときにのみ子コンテンツを読み込みます。実行する価値がある理由: snapshotForExpandingParent は高価なコンテンツの提供に特化しており、最初のセクションのスナップショットのコンテンツの量を削減できます。開始方法: 設定する dataSource.sectionSnapshotHandlers.snapshotForExpandingParent そして、クロージャ内の親と現在の子のスナップショットに基づいて、更新されたセクションのスナップショットを返します。

  • ユーザーのドラッグ後に順序を保存: リストがユーザーの並べ替えをサポートできるようにし、結果をモデル配列に書き戻します。実行する価値がある理由: トランスクリプトでは、インターフェイス層は並べ替えの変更を自動的に送信できるが、アプリケーションのバッキング ストアが最終的な信頼できる情報源であると説明しています。開始方法: 両方を提供します canReorderItem そして didReorder、 読む NSDiffableDataSourceTransaction.difference または finalSnapshotdidReorder、ローカル配列を更新します。

関連セッション

  • UICollectionView の進歩 — セクションのスナップショット、リスト、セルの登録、最新の構成方法など、iOS 14 のコレクション ビューの更新を引き続き確認します。
  • UICollectionView のリスト — このセッションのアウトライン データの実装に適した、UICollectionView および構成レイアウト上でリストとサイドバーがどのように構築されるかについて説明します。
  • 最新のセル構成 - コレクション ビューとテーブル ビューのセルのコンテンツ/背景構成を完了して、リストとアウトラインのセルを構成します。
  • SwiftUI のスタック、グリッド、アウトライン — SwiftUI の観点から階層データ、アウトライン、リスト表示を見ると、UIKit のセクション スナップショットと照らし合わせることができます。

コメント

GitHub Issues · utterances