アプリ開発日記 #44 iOS版のダイアログを表示させた話

今日の目標

iOS版でダイアログの表示を行う

実際にやったこと

ミニマムプログラムを作成し、iOSでダイアログを表示させる。
実際のアプリへのダイアログ表示処理の組み込み

ミニマムプログラムを作成したことでわかったこと
  • MainPage.xaml.csからはダイアログが表示できた
  • Navigation.PushModalAsyncで画面遷移した後の画面からはダイアログが表示できなかった

また、デバッグ時のログにはダイアログの表示階層がおかしいと読み取れる情報が記録されていました。
つまり、UIApplication.SharedApplication.KeyWindow.RootViewControllerを使用して、ルート画面の上にダイアログを表示しようとしていたことが、ダイアログが表示できない原因でした。

そのため、ルート画面ではなく、遷移先の画面の上にダイアログを表示するための方法を佐田しました。
結論、RootViewControllerのプロパティーには、ModalViewControllerというプロパティーがあり、このModalViewControllerを使用してダイアログを表示することで、Navigation.PushModalAsyncで画面遷移した後の画面でもダイアログの表示が可能となるようでした。

なお、ModalViewControllerは、Navigation.PushModalAsyncによる画面遷移を行った数だけ入れ子になっているようです。
そのため、画面遷移を複数行っている場合は、ModalViewControllerがnullとなっている階層を探したうえで、ModalViewControllerがnullとなっている階層に対してダイアログを表示するようにする必要があるようです。

実際のコード

GetNavigationViewForShowDialogメソッドでは、再帰処理により、ModalViewControllerがnullとなっている階層を探すようにしています。
そして、Showメソッドでは、GetNavigationViewForShowDialogメソッドで探したModalViewControllerがnullとなっている階層に対して、PresentViewControllerを実行することで、ダイアログ表示を行っています。

public class EntryAlertService : IEntryAlertService
{
    public Task<DialogResult> Show(string title, string message, string entryText, string accepte, string cancel)
    {
        var tcs = new TaskCompletionSource<DialogResult>();

        UIApplication.SharedApplication.InvokeOnMainThread(
            () =>
            {
                // ダイアログの作成
                var dialog = UIAlertController.Create(title, message, UIAlertControllerStyle.Alert);

                dialog.AddTextField(
                    (textField) =>
                    {
                        textField.Text = entryText;
                    });

                // ダイアログボタンのイベントの登録
                var cancelAction = UIAlertAction.Create(cancel, 
                    UIAlertActionStyle.Cancel, 
                    action => tcs.SetResult(
                    new DialogResult()
                    {
                        PressedButtonTitle = cancel,
                        Text = dialog.TextFields[0].Text
                    }));

                dialog.AddAction(cancelAction);

                var acceptAction = UIAlertAction.Create(accepte, 
                    UIAlertActionStyle.Default, 
                    action => tcs.SetResult(
                    new DialogResult()
                    {
                        PressedButtonTitle = accepte,
                        Text = dialog.TextFields[0].Text
                    }));

                dialog.AddAction(acceptAction);

                // ダイアログの表示
                var viewController = 
                    this.GetNavigationViewForShowDialog(
                        UIApplication.SharedApplication.KeyWindow.RootViewController);
                viewController.PresentViewController(dialog, true, null);
            });

        return tcs.Task;
    }

    /// <summary>
    /// ダイアログを表示させるナビゲーション画面を返却する
    /// </summary>
    /// <param name="controller"></param>
    /// <returns></returns>
    private UIViewController GetNavigationViewForShowDialog(UIViewController controller)
    {
        if(controller.ModalViewController != null)
        {
            return GetNavigationViewForShowDialog(controller.ModalViewController);
        }
        else
        {
            return controller;
        }
    }
}

残っている実装

一通り機能の実装は完了しました。
あとは、スプラッシュ画面、アプリアイコン、広告の実装をするだけとなります。
また、Androidおよび、iOSでの動作確認を行い、正しく動いていない箇所や、画面レイアウトの微調整をする必要があると考えています。

明日への思い

スプラッシュ画面と、アイコンの実装をしていく。

過去に作成したアプリ

①概算家計簿
家計簿を毎日つけれれないあなたのための家計簿アプリです。
f:id:b-kimagure:20190722205238p:plain:w150
Google Play で手に入れよう

②ToDoボタン
忙しくてついつい放置してしまう家事
そんな家事の最後にやった日を記録するアプリです。
f:id:b-kimagure:20190805230012p:plain:w150
Google Play で手に入れよう

今読んでる本

「ひとり情シス」虎の巻

「ひとり情シス」虎の巻

気になる本

仕事ごっこ ~その“あたりまえ

仕事ごっこ ~その“あたりまえ"、いまどき必要ですか?

エリック・エヴァンスのドメイン駆動設計 (IT Architects’Archive ソフトウェア開発の実践)

エリック・エヴァンスのドメイン駆動設計 (IT Architects’Archive ソフトウェア開発の実践)

Amazonプライム・ビデオ

Amazonプライム・ビデオ

J-POPヒッツ in Prime

J-POPヒッツ in Prime