🕌

【SwiftUI】iPad × Modal × ConfirmationDialogの注意点

2024/03/30に公開

この記事ではconfirmationDialogが適切な位置に付けられていない場合、iPhoneでは普段見慣れた位置に確認ダイアログが表示されるにも関わらず、iPadでは不自然な位置に確認ダイアログが表示される件について解説します。

シチュエーションとしては、iPadで表示しているモーダルをツールバーのボタンから閉じるといったものです。

※ ここでいう不自然な位置とは、Apple製のアプリ(リマインダーやカレンダー)が表示する確認ダイアログの位置とは異なる位置を指しています。

まずはiPadでリマインダーアプリを開き、確認ダイアログの位置を確認します。何かしらの入力をしてからキャンセルボタンを押すと確認ダイアログが表示されます。

このような確認ダイアログを実装する場合、以下のようなコードを書きがちではないかと思います。

struct ContentView: View {
    @State private var isShowModalView = false

    var body: some View {
        Button("Show ModalView") {
            isShowModalView = true
        }
        .sheet(isPresented: $isShowModalView) {
            NavigationStack {
                ModalView()
            }
        }
    }
}

struct ModalView: View {
    @State private var isShowConfirmationDialog = false

    var body: some View {
        VStack {
            Text("Hello, world.")
        }
        .toolbar {
            ToolbarItem(placement: .topBarTrailing) {
                Button("Dismiss", systemImage: "xmark.circle.fill") {
                    isShowConfirmationDialog = true
                }
            }
        }
        .confirmationDialog(
            "画面を閉じてもよろしいですか?",
            isPresented: $isShowConfirmationDialog,
            titleVisibility: .visible
        ) {
            Button("画面を閉じる", role: .destructive) {}
            Button("キャンセル", role: .cancel) {}
        }
    }
}

一見問題がないように見える上、iPhoneで試してみると普段見慣れた感じで表示されます。

しかし、これをiPadで試してみると確認ダイアログの位置が不自然になります。
※ iPadでキャンセルが出てこないのは仕様です。

これでも問題はありませんが、普段iPadを使われている方にとっては不自然に思われるかもしれません。

では、これをどうすればいいかというと、confirmationDialogの位置を以下の通りに変更します。(Buttonに対してつける)

struct ContentView: View {
    @State private var isShowModalView = false

    var body: some View {
        Button("Show ModalView") {
            isShowModalView = true
        }
        .sheet(isPresented: $isShowModalView) {
            NavigationStack {
                ModalView()
            }
        }
    }
}

struct ModalView: View {
    @State private var isShowConfirmationDialog = false

    var body: some View {
        VStack {
            Text("Hello, world.")
        }
        .toolbar {
            ToolbarItem(placement: .topBarTrailing) {
                Button("Dismiss", systemImage: "xmark.circle.fill") {
                    isShowConfirmationDialog = true
                }
                .confirmationDialog(
                    "画面を閉じてもよろしいですか?",
                    isPresented: $isShowConfirmationDialog,
                    titleVisibility: .visible
                ) {
                    Button("画面を閉じる", role: .destructive) {}
                    Button("キャンセル", role: .cancel) {}
                }
            }
        }
    }
}

これでiPhoneでもiPadでも自然な位置に確認ダイアログを表示することができます。
※ iPadでキャンセルが出てこないのは仕様です。

皆様のお役に立てれば幸いです。Twitterもやってます。

https://twitter.com/3MB_JP

Discussion