0
2
iOS強化月間 - iOSアプリ開発の知見を共有しよう -

【SwiftUI】モーダル表示の却下

Last updated at Posted at 2023-09-22

この記事は何?

SwiftUIアプリのシートやポップオーバー表示を取り消す方法について、Appleの開発者向けドキュメントを独自に解説する。

Swiftを基礎から学ぶには
自著、工学社より発売中の「まるごと分かるSwiftプログラミング」をお勧めします。変数、関数、フロー制御構文、データ構造はもちろん、構造体からクロージャ、エクステンション、プロトコル、クロージャまでを基礎からわかりやすく解説しています。

dismissプロパティ

Dismiss環境値を使用して、特定の環境に対してこの構造体のインスタンスを取得する。
次に、インスタンスを呼び出すと、表示の取り消しを実行する。
インスタンスを呼び出すときにSwiftが呼び出すcallAsFunction()メソッドを定義するため、インスタンスを直接呼び出す。

このアクションを使用して、次のことができる。

  • シートやポップオーバーなどのモーダル表示を取り消す
  • NavigationStackから現在のビューをポップする
  • WindowGroupまたはWindowで作成したウィンドウを閉じる

アクションの具体的な動作は、どこから呼び出すかによって異なる。
たとえば、シートとして機能するビュー内でDismissActionを呼び出すボタンを作成できる。

// モーダル表示されるシート
private struct SheetContents: View {
    @Environment(\.dismiss) private var dismiss

    var body: some View {
        Button("Done") {
            dismiss()
        }
    }
}

SheetContentsビューを表示すると、シートのボタンをタップしてシートを閉じることができる。

// 通常の画面
private struct DetailView: View {
    // 「シートが表示されているかどうか」を追跡する状態プロパティ
    @State private var isSheetPresented = false

    var body: some View {
        // ボタンをタップすると、シートがモーダル表示される
        Button("Show Sheet") {
            isSheetPresented = true
        }
        .sheet(isPresented: $isSheetPresented) {
            SheetContents()
        }
    }
}

注意すべき点

アクションが適切な環境で定義されているかを確認する。
たとえば、上例のDetailViewビューでdismissプロパティを作成することは適切ではない。
そのようにして、dismiss()アクションを.sheet(item:onDismiss:content:)修飾子のコンテンツクロージャから呼び出しても、シートを正しく却下できない。

不正な方法でのモーダル表示
private struct DetailView: View {
    @State private var isSheetPresented = false
    @Environment(\.dismiss) private var dismiss // Applies to DetailView.

    var body: some View {
        Button("Show Sheet") {
            isSheetPresented = true
        }
        .sheet(isPresented: $isSheetPresented) {
            // .sheetモディファイア内でdismissアクションを呼び出さないこと
            Button("Done") {
                dismiss() // Fails to dismiss the sheet.
            }
        }
    }
}

アクションは「それを宣言した環境」に適用される。
つまり、先の方法では、アクションが「sheetContentsの環境」ではなく「DetailViewビューの環境」に適用されてしまったので、シート表示が取り消されない。
実際に、このDetailViewビューがルートビューだった場合、macOSとiPadOSのdismiss()アクションはウィンドウを閉じる。

dismiss()アクションは「表示中でないビュー」に影響しない。
SwiftUIが「ビューを表示しているかどうか」を照会したい場合は、isPresented環境値を利用する。

isPresentedプロパティ

「環境に関連付けられたビュー」が表示中かどうか、を示す真偽値。

宣言
var isPresented: Bool { get }

この環境値を読み取るには、@Environmentプロパティラッパーを使用してプロパティを作成する。

プロパティを作成して、コードで呼び出す
@Environment(\.isPresented) private var isPresented

SwiftUIが「そのビューをいつ表示するか」を知りたい場合は、他の環境値を利用する。
たとえば、.onChange(of:perform:)修飾子を使用して、SwiftUIがビューを表示したときにアクションを実行する。

.onChange(of: isPresented) { isPresented in
    if isPresented {
        // Do something when first presented.
    }
}

この.onChange(of:perform:)は、すでにナビゲーション階層にあるビューに戻るときなど、SwiftUIが特定のプレゼンテーションに対して複数回呼び出せる.onAppear(perform:)とは動作が異なる。

表示中のビューを取り消すには、dismissアクションを使用すること。

0
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
2