【SwiftUI】iOS16+のNavigationSplitView|分割カラムレイアウトの作成

iOS16+より、新しいナビゲーション構築方法として、SwiftUIビュー「NavigationSplitView」が登場しました。

主にiPadやMacOSにおいて、”分割カラム構成”のレイアウトを作成することができます。

3列(カラム)構成のレイアウト

Appleは、これまで使用されてきた「NavigationView」を非推奨とし、iOS16+の新機能である「NavigationStack」「NavigationSplitView」を新たなナビゲーション構築方法として推奨しています。

本記事では、iPadでの動作を中心に置いてNavigationSplitViewの基本機能を確認しながら、iPadとiPhoneそれぞれでどのような画面表示になるかについても触れていきます。

ソースコード

使用しているサンプルコードはGitHubにて公開されています。

コード内容の確認にぜひご利用ください🍎

サンプルコードを見る>>

[ 本記事はこんな人におすすめ ]

・SwiftUIでアプリ開発をしている

・主にiPadにおいて、複数カラムの分割ビューレイアウトを作成したい

・iPadとiPhoneでの画面表示の違いを知りたい

機能の概要

イニシャライザ(初期化子)

NavigationSplitViewのイニシャライザは、大きく2つの種類があります。

構築する画面レイアウトのセクション分割を2列/3列のどちらにするかで、適するイニシャライザを選択します。

				
					// 2カラムの分割レイアウト
init(sidebar: () -> Sidebar, detail: () -> Detail)

// 3カラムの分割レイアウト
init(sidebar: () -> Sidebar, content: () -> Content, detail: () -> Detail)
				
			

以下の図のように、

・2カラム構成の場合は「sidebar|detail」

・3カラム構成の場合は「sidebar|content|detail」

の並びでセクションが構成されます。

各カラムの配置と振る舞い

2列(カラム)のレイアウト

2列のレイアウト構成の場合、detailに配置したビューは画面に常時表示されます。

sidebarに配置したビューの表示は、左上のメニュー開閉ボタンによってコントロールします。

【2列レイアウトの横画面】

縦画面の場合は、サイドバーがdetailの前面に覆い被さるような振る舞いになります。

【2列レイアウトの縦画面】

3列(カラム)のレイアウト

3列のレイアウト構成の場合、横画面と縦画面で少し異なるナビゲーションの振る舞いを見せます。

横画面では、左から順にcontentdetailが画面に常時表示されます。

sidebarの表示は、2列レイアウトと同様に、左上の開閉ボタンによってコントロールします。

【3列レイアウトの横画面】

3列レイアウトの縦画面では、開閉ボタンによって段階的に content > sidebar が表示されていくようなナビゲーションの形をとります。

【3列レイアウトの縦画面】

上記で挙げたような各カラムの振る舞いは、モディファイアによって一定のコントロールが可能です。方法については後述で触れていきます。

iPhone側での表示について

NavigationSplitViewを使って画面構築をした時、iPhoneやApple Watchなどの狭いサイズのデバイスでは、ナビゲーションの分割ビューが 1 つのスタックに折りたたまれたコンパクトカラムとして構成されます。

そして、表示カラムの優先度はSwiftUIが自動選択し構築します。

デフォルトでは、スタックの先頭はsidebarが選択され、初期表示時にsidebarのビューを単一の画面として表示します。

以下は、2カラム構成の同じコードをビルドした場合の、iPhoneとiPadそれぞれの画面表示図です。

上記の例ではsidebarにリストが配置されていますが、iPhone側では単一の画面としてsidebarのリストビューが表示されていることがわかります。

この例の場合、リスト選択のナビゲーション遷移によって、detailに設定した詳細ビューを表示しています。

実装コードを以下に挙げておきます。

【実装コード】

				
					/// 選択可能なサイドバーによる詳細ビューの切り替え実装
struct BasicNavigationSplitView: View {

    let colors: [Color] = [.orange, .pink, .blue]
    @State private var selectionColor: Color?

    var body: some View {
        NavigationSplitView {
            // 選択可能なリストサイドバー
            List(colors, id: \.self, selection: $selectionColor) { color in
                Text(color.description)
            }
        } detail: {
            if let selectionColor {
                // リスト選択時に表示
                selectionColor
            } else {
                // 未選択時に表示
                Text("カラーを選択してください。")
            }
        }
    }
}
				
			

また、このようなコンパクトカラム下での初期表示画面は、iOS17+の新機能を用いることで一定の制御が可能です。これについては、後項目【優先コンパクト列(カラム)の指定】で解説します。

スタイルの指定

NavigationSplitViewStyle

NavigationSplitViewに対して「.navigationSplitViewStyle」修飾子を渡すことで、サイドカラムを表示/非表示した時の詳細ビューのサイズの振る舞いを制御できます。

主に以下、3つのスタイルが用意されています。

・prominentDetail   ➡️  サイドカラム表示時、詳細エリアのサイズを維持する

・balanced        ➡️  サイドカラム表示時、詳細エリアのサイズを縮小する

・automatic      ➡️  コンテキストに基づいてシステムが判断

例えば以下のコード例では、スタイルに「prominentDetail」を設定しています。

これにより、カラムの表示/非表示に関わらず、詳細ビューが自身のサイズを維持するようになります。

サイドカラム表示時も、詳細ビューが自身のサイズを維持する「prominentDetail」

NavigationSplitViewStyleによるスタイル指定は、一部のプラットフォームでのみ適用されます。

例えば、iPhoneなどの狭いビューサイズによって折りたたまれたコンパクトカラムの場合、これらの設定は無視されます。

カラム幅の指定

NavigationSplitViewColumnWidth

カラム要素のビューに対して「.navigationSplitViewColumnWidth」修飾子を渡すことで、カラムの幅を指定できます。

「固定幅」か「可変幅」かによって、2つのイニシャライザから選択します。

例えば以下のコード例では、sidebarのカラムを固定幅100、contentのカラムを可変幅150〜400に設定しています。

				
					/// 各カラムの表示幅を指定
struct ColumnWidth: View {
    var body: some View {

        NavigationSplitView {
            SideBarView()
                .navigationSplitViewColumnWidth(100) // 固定
        } content: {
            ContentView()
                // min: 最小, ideal: 理想, max: 最大
                .navigationSplitViewColumnWidth(
                    min: 150, ideal: 300, max: 400) // 可変
        } detail: {
            DetailView()
        }
    }
}
				
			
sidebar「100」 content「150〜400」でカラム幅を指定

NavigationSplitViewColumnWidthによる幅指定は、一部のプラットフォームでのみ適用されます。

例えば、iPhoneなどの狭いビューサイズによって折りたたまれたコンパクトカラムの場合、これらの設定は無視されます。

また、プレゼンテーション環境がサポートしていない幅を指定すると、SwiftUIはカラムに対して別の幅を使用する可能性があります。

カラム表示状態のコントロール

NavigationSplitViewVisibility

構造体「NavigationSplitViewvisibility」の値を参照として渡すことで、各カラムの表示ナビゲーションをプログラムで制御することが可能です。

NavigationSplitView(columnsVisibility:)に状態変数として紐付けます。

				
					// 各カラムの表示/非表示をプログラムで制御
// 初期値として設定したオプションが、最初の表示状態となる
@State private var columnVisibility = NavigationSplitViewVisibility.detailOnly

NavigationSplitView(columnVisibility: $columnVisibility) {...}
				
			

NavigationSplitViewvisibilityには以下のオプションが定義されています。

・ detailOnly     ➡️  詳細エリアのみ表示

・ doubleColumn     ➡️  コンテンツエリア・詳細エリアを表示

・ all           ➡️  全てのエリアを表示

・ automatic     ➡️  コンテキストに基づいてシステムが判断

以下は、ボタンアクションによってカラムの表示状態をプログラムでコントロールしている図です。

一部のプラットフォーム下では、NavigationSplitViewVisibilityによるオプション設定が期待通りに反映されない場合があります。

例えば、iPhoneなどの折りたたまれたコンパクトカラムとして構成されるパターンの場合、プログラムによるカラム状態の操作は無視されます。

また、初期値として「detailOnly」を設定していたとしても、最上部の画面は自動で「sidebar」が選択されます。(後述で紹介するiOS17+の新機能で制御可能)

優先コンパクト列(カラム)の指定

NavigationSplitViewColumn(Beta)

iOS17+およびiPadOS17+から「NavigationSplitViewColumn」が新機能として登場しました。

この機能を用いることで、狭いビューサイズによって折りたたまれたコンパクトカラムのうち、どのカラムを初期画面として優先表示させるのかをコントロールできます。

				
					// SwiftUIが構築するカラムの表示優先度をコントロールする
// 詳細ビューを初期表示として設定
@State private var preferredColumn = NavigationSplitViewColumn.detail

NavigationSplitView(preferredCompactColumn: $preferredColumn) {...}
				
			

前項目【iPhone側での画面表示】でも挙げたように、iPhoneやApple Watchなどの狭いサイズのデバイスでは、最前面として初期表示するカラムをSwiftUIが自動で選択します。

NavigationSplitView(preferredCompactColumun:)に値を設定することによって、優先表示カラムを制御することができます。

NavigationSplitViewColumnには以下のオプションが定義されています。

・ sidebar     ➡️  サイドエリアを優先表示

・ content       ➡️  コンテンツエリアを優先表示

・ detail        ➡️  詳細エリアを優先表示

以下のコードは、ドキュメントに記述されている実装例です。

NavigationSplitView(preferredCompactColumn:)に状態変数を渡します。

こちらのコード例では、初期値として「detail」、つまり詳細ビューを優先表示カラムとして設定しています。

				
					// SwiftUIが自動選択する表示カラムの優先度をコントロールする
struct PreferredColumn: View {

    // 詳細ビューを優先表示として設定
    @State private var preferredColumn = NavigationSplitViewColumn.detail

    var body: some View {
        NavigationSplitView(preferredCompactColumn: $preferredColumn) {
            Color.yellow
        } detail: {
            Color.blue
        }
    }
}
				
			

「detail」を優先設定したことで、iPhoneの初期表示として詳細ビューColor.blueがセットされます。

この場合、左上の <Back ボタンをタップすることで、sidebarビューのColor.yellowに遷移します。

ビルド時の初期画面

NavigationSplitViewColumnは現在Beta版であるため、今後機能の変更がある可能性があります。

ベータ版ソフトウェアの使用について、詳しくはAppleの以下サポートページを確認してください。

👉|Using Apple Beta Software – Support – Apple Developer

まとめ

以上、iOS16+で新登場したSwiftUIビュー「NavigationSplitView」についてでした!

要点をまとめておきます。

・主にiPadやMacOSにおいて、複数のビューを分割したカラムレイアウトを構築できる

列(カラム)数は2列/3列のどちらかから選択する

iPhoneなどの狭いビューサイズにおいては、各カラムが一つのスタックとして折りたたまれ、単一のビューとして画面表示される

・カラムはスタイル/幅/表示状態の操作など、一定の状態コントロールが可能

iOS16+で新登場した「NavigationSplitView」と「NavigationStack」、両者の特徴を理解して、うまく使いこなしていきたいですね!

NavigationStackについてはこちらの記事にまとめていますので、よければどうぞ👌

本記事が参考になりましたら幸いです。

\  SHARE  /

Twitter
Facebook
Email

✏️ アプリ開発が学べる勉強会を開催中! 

CodeCandyではアプリ開発を学ぶための勉強会を定期開催しています。
学習する習慣を身につけたい、他の参加者と作業したい、アプリ開発の基本をマスターしたい、という方のために無料で学べる勉強会です。
グループにメンバー登録して頂くと、イベント開催時にメールで通知されます。

▶️グループのメンバーとして参加する

徹底した基礎学習からマスターするiPhoneアプリ開発集中オンライン講座開講!

本書「iPhoneアプリ開発集中講座」を執筆している現役エンジニア講師陣が直接に指導!
基礎、課題実習で実践力を鍛えて、オリジナルアプリ公開までチャレンジ!
充実した転職支援もあるので、エンジニアへ転職したい人にもおすすめです!

まずは、現役エンジニアに相談できる無料相談をご利用ください。

\ Official /

By 中川 賢亮

2022年2月よりSwift学習を始め、4月からiOSアプリ開発オンラインスクール「CodeCandy」にてアプリ開発を学ぶ。 2023年10月に個人開発アプリ「unico」をリリース。現在はアプリの機能アップデートをしながら、スクール運営の技術ブログの執筆や、出版書籍の入稿チェック・デバッグにも携わる。