SwiftUIのStack, Grid, Outline – WWDC2020

Session概要

改良されたStackと新しいList、Outline Viewを使って、SwiftUIアプリで詳細データをより素早く効率的に表示しましょう。iOS, iPadOSには新登場となるOutlineは、StackやListとともに動く階層データを表現できる新しいマルチプラットフォームツールです。
SwiftUIの新しく改良されたツールを使用し、テーブルビュー利用時により多くのコンテンツを表示する方法、スムーズなスクロールで応答性の高いStackを作成する方法、VStackで提供できる以上のものを必要とするコンテンツのList Viewの構築方法をお伝えします。
また、DisclosureGroup同様、Grid Viewでより多くのレイアウトオプションも利用可能です。 https://developer.apple.com/videos/play/wwdc2020/10031/

新しく追加されたAPI

Stacks

  • 画面表示で画像等を多く表示していると、スクリーンが反応するまでの時間が長くかかるようになる
    • そのため、アプローチとしては最初の画面に映る分だけの画像を表示する遅延読み込みを行う
  • LazyVStack/LazyHStack
    • Lazy Stackは表示する必要ができた分だけコンテンツを逐次表示する
    • Viewはメインスレッドによる全単一画像のローディングと測定をブロックしません
    • また、アプリのメモリフットプリントが不必要に大きくなることはない
    • ただし、不必要に遅延読み込みを使用すべきではなく、画像等のコンテンツの読み込みに時間がかかるViewにのみ使用する
      • より良い方法としては、計測機器を用いたプロファイリングの後で判明した性能のボトルネックを解決する手段としてLazyStackを採用するようにする
// Fetch sandwiches from the sandwich store
let sandwiches: [Sandwich] = …

ScrollView {
    LazyVStack(spacing: 0) {
        ForEach(sandwiches) { sandwich in
            HeroView(sandwich: sandwich)
        }
    }
}

Grids

  • LazyVGrid/LazyHGrid
    • Stackと同様にコンテンツのGridを逐次増やす
    • 単一カラムのGridをマルチカラムのGridに変更して、コンテンツの表示数を増やす
// Fetch sandwiches from the sandwich store
let sandwiches: [Sandwich] = …

// Define grid columns
// 最小のカラム幅を維持しながら、均等な幅のカラムを可能な数だけ生成する
// iPadやmacOS向けのアプリで有用な機能
var columns = [
    GridItem(.adaptive(minimum: 300), spacing: 0)
]

ScrollView {
    LazyVGrid(columns: columns, spacing: 0) {
        ForEach(sandwiches) { sandwich in
            HeroView(sandwich: sandwich)
        }
    }
}

Lists

  • Listで表示するコンテンツは常に遅延読み込みを行う
struct GraphicsList: View {
    var graphics: [Graphic]
    var body: some View {
        List(
            graphics,
            children: \.children // この設定を行うことで階層構造を持つことができる
        ) { graphic in
            GraphicRow(graphic)
        }
        .listStyle(SidebarListStyle())
    }
}

OutlineとDisclosureGroupについて

情報のプログレッシブ表示をアプリに合うように適切に調整する能力を提供する

Outline

  • ForEachに似ているが、ツリー構造データを横断する
  • ディスクロージャーインジケータを経由してグループを拡張したり、折りたたみが可能
// Customizing your outlines

List {
    ForEach(canvases) { canvas in
        Section(header: Text(canvas.name)) {
            OutlineGroup(canvas.graphics, children: \.children)
            { graphic in
                GraphicRow(graphic)
            }
        }
    }
}

DisclosureGroup

  • ディスクロージャーインジケータとラベル、およびコンテンツを提供する
    • outlineと同様にコンテンツの拡張・折りたたみが可能
    • ディスクロージャーインジケータの操作によりコンテンツが表示/非表示される
    • isExpanded 引数によりデフォルトで展開するかどうかを選択可能
// Progressive display of information
Form {
    DisclosureGroup(isExpanded: $areFillControlsShowing) {
       Toggle("Fill shape?", isOn: isFilled)
       ColorRow("Fill color", color: fillColor)
    } label: {
       Label("Fill", …)
    }
    …
}

補足

  • OutlineGroupのForEachにおけるbodyはDisclosureGroup
    • DisclosureGroupのラベルはオリジナルの集合の単一エレメントを用いて生成されるが、各DisclosureGroupのコンテンツは別のOutlineGroupであり、その単一エレメントのchildrenが対象であることに注意
最新情報をチェックしよう!