[SwiftUI] Form と組み合わせて使う Picker

SwiftUI2021

Picker を Form と組み合わせて使うときに気をつける点を説明します。

環境&対象

以下の環境で動作確認を行なっています。

  • macOS Monterery beta 2
  • Xcode 13 beta2
  • iOS 15 beta

Picker

Picker を使ったサンプル

以下は、0〜9の数字を Picker で選択するコードです。

PurePickerExample
PurePickerExample

//
//  ContentView.swift
//
//  Created by : Tomoaki Yagishita on 2021/07/05
//  © 2021  SmallDeskSoftware
//

import SwiftUI
import SwiftUIDebugUtil

struct ContentView: View {
    @State private var selectedInt: Int = 1

    var body: some View {
        VStack {
            Text("SelectedInt: \(selectedInt)")
            Picker("select int", selection: $selectedInt) {
                ForEach(0..<10) { index in
                    Text("\(index)")
                        .tag(index)
                }
            }

        }
    }
}

Form

Form は、ユーザーからの入力を期待するフィールド等をまとめるためのビューの1つで、子要素のビューをプラットフォーム毎の特徴にあわせて配置してくれるビューです。

Apple のドキュメントは、こちら

例えば、SwiftUI で Text を縦に並べると以下のようになります。

02MultiTextWithoutForm
02MultiTextWithoutForm

複数の Text を Form を使ってまとめると、以下のような表示に切り替わります。(iOS の例です)

03MultiTextWithForm
03MultiTextWithForm

アプリの設定画面でよくみるレイアウトです。

Form に入れられた Picker

上記のスクリーンショットをよくみるとわかりますが、Picker が Wheel 表示でなくなり かつ グレーアウトされています。

Picker の右側に ">" なるインディケータがあるので、クリックすると別画面に移動して値を選択できそうですが、このままでは、そのような動作は行いません。
クリックは無視され、値変更ができません。

04PickerDoesNotWork
04PickerDoesNotWork

Picker を Form 内で動作させる

この振る舞いは、このときの Picker が、NavigationView の仕組みを使っていることからくるようです。(ドキュメントには、明記されていません)

ですので、NavigationView で Form を wrap することで、期待する動作にすることができます。


//
//  ContentView.swift
//
//  Created by : Tomoaki Yagishita on 2021/07/05
//  © 2021  SmallDeskSoftware
//

import SwiftUI
import SwiftUIDebugUtil

struct ContentView: View {
    @State private var selectedInt: Int = 1

    var body: some View {
        NavigationView{ // <-- 🆕
            Form {
                Text("SelectedInt: \(selectedInt)")
                Text("Short Text")
                Text("Long long long Text")
                Picker("select int", selection: $selectedInt) {
                    ForEach(0..<10) { index in
                        Text("\(index)")
                            .tag(index)
                    }
                }
            }
        }
    }
}

Tip: NavigationView のタイトル領域を減らす

上記を見るとわかりますが、NavigationView で wrap したことで、タイトル部分のスペースが取られていることがわかります。

06SpaceFromNavView
06SpaceFromNavView

タイトルを非表示にする

NavigationView のタイトルバー表示を制御するために、.navigationBarHidden という View Modifier が用意されています。

以下のように適用することで、タイトルバー表示を抑制することができます。

07NoTitleBar
07NoTitleBar

//
//  ContentView.swift
//
//  Created by : Tomoaki Yagishita on 2021/07/05
//  © 2021  SmallDeskSoftware
//

import SwiftUI
import SwiftUIDebugUtil

struct ContentView: View {
    @State private var selectedInt: Int = 1

    var body: some View {
        NavigationView{
            Form {
                Text("SelectedInt: \(selectedInt)")
                Text("Short Text")
                Text("Long long long Text")
                Picker("select int", selection: $selectedInt) {
                    ForEach(0..<10) { index in
                        Text("\(index)")
                            .tag(index)
                    }
                }
            }
            .navigationBarHidden(true)  // <--- 🆕
        }
    }
}

Form のセクションヘッダーも非表示に

実は、Form 内には、Section を複数持たせることができます。Section を持たせることで、Section ヘッダーが表示されるようになります。

以下のイメージは、Form の外周のボーダーを表示させています。Form のボーダーを表示してみると、Form の外周と1行目の Text がずいぶん空いていることがわかります。

この領域は、Section ヘッダー向けにとられている領域です。

08SpaceFromSectionHeader
08SpaceFromSectionHeader

このSection ヘッダーの調整を SwiftUI で行うことはできません。UIKit を使って、調整することが必要になります。

通常ではヘッダー高さは自動調整で行われています。以下のコードを使うことで その自動調整を、OFF にすることができます。


UITableView.appearance().sectionHeaderHeight = 5

正の整数を設定することで、自動調整を OFF にすることができ、以下のような表示になります。

09ZeroHeightHeader
09ZeroHeightHeader

まとめ:Form と組み合わせて使う Picker のまとめ

Form と組み合わせて使う Picker のまとめ
  • NavigationView を組み合わせないと Picker が動作しない
  • NavigationView は、デフォルトでは、タイトルバー表示領域を確保する
  • セクションヘッダーの高さも調整したいならば、UIKit を使って、変更する

説明は以上です。
不明な点やおかしな点ありましたら、こちらまで。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です