16
17

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

XamarinAdvent Calendar 2014

Day 23

UIAlertController を async/await 対応させて便利に使う

Last updated at Posted at 2014-12-22

 なんか空いてたのでエントリーしましたが、急だったので軽い話です。

の焼き直しみたいなものです。

 iOS8 では、UIAlertDialog が非推奨になり、代わりに UIAlertController を使えとのこと。

 普通に使うとこうなります。

button1.TouchUpInside += (sender, e) => 
{
    var alert = UIAlertController.Create("", "こんぼう をすてますか?", UIAlertControllerStyle.Alert);
    alert.AddAction(UIAlertAction.Create("はい", 
        UIAlertActionStyle.Default, x=> label1.Text = "こんぼう をすてました")); 
    alert.AddAction(UIAlertAction.Create("いいえ",  
        UIAlertActionStyle.Default, x=> {})); 
     
    this.PresentViewController(alert, true, null);
};

 このくらいなら問題ありません。

 次に、こんぼう をすてる前にもう一度問いかけるようにします。
2つ目の UIAlertController が入れ子になってしまって見づらい、 残念な感じ です。

button1.TouchUpInside += (sender, e) => 
{
    var alert = UIAlertController.Create("", "こんぼう をすてますか?", UIAlertControllerStyle.Alert);
    alert.AddAction(UIAlertAction.Create("はい", 
        UIAlertActionStyle.Default, x=> 
        {
            // 念押しの確認ダイアログ(入れ子でつらい
            var alert2 = UIAlertController.Create("", "ほんとうにすてますか?", UIAlertControllerStyle.Alert);
            alert2.AddAction(UIAlertAction.Create("もちろん", UIAlertActionStyle.Default, _=> 
            {
                label1.Text = "こんぼう をすてました"
            }));
        alert2.AddAction(UIAlertAction.Create("やめる",  UIAlertActionStyle.Default, _=> {})); 

        // アラート2の表示
        this.PresentViewController(alert2, true, null);
    })); 

    // アラート1の表示
    alert.AddAction(UIAlertAction.Create("いいえ",  UIAlertActionStyle.Default, x=> {})); 

    this.PresentViewController(alert, true, null);
};

 Objective-C や Swift なら、ここで打つ手は今のところ無いでしょう。
しかし Xamarin には、C# には async/await がありまぁす!
アラートの表示を async/await(というか Task)対応してみましょう。

private Task<int> ShowDialog(string message, string button1Title, string button2Title)
{
    var comp = new TaskCompletionSource<int>();

    var alert = UIAlertController.Create("", message, UIAlertControllerStyle.Alert);
    alert.AddAction(UIAlertAction.Create(button1Title, UIAlertActionStyle.Default, x=> 
    {
        comp.SetResult(1); // OKボタン
    })); 
    alert.AddAction(UIAlertAction.Create(button2Title,  UIAlertActionStyle.Default, x=> 
    {
        comp.SetResult(0); // Cancel
    })); 

    this.PresentViewController(alert, true, null);

    return comp.Task;
}

Task<int> を返すメソッド ShowDialog です。UIAlertController のボタンが押されたら SetResult して Task の値を決定します。

 このメソッドを使う方は、こうなります。

button1.TouchUpInside += async (sender, e) => 
{
    if (await ShowDialog("こんぼう をすてますか?", "はい", "いいえ") == 0) 
        return;

    if (await ShowDialog("ほんとうにすてますか?", "もちろん", "やめる") == 0) 
        return;

    label1.Text = "こんぼう をすてました";
};

なんて見やすいコードになったことでしょう。すばらしい!

入れ子でなく、フラットに書けるので、こんな事もできます。

button1.TouchUpInside += async (sender, e) => 
{
    while (await ShowDialog("こんぼう をすてますか?", "はい", "いいえ") == 1) 
    {
        label1.Text = "それをすてるなんてとんでもない!";
    }

	label1.Text = "すてるのをやめました";
};

こんぼうを捨てるのをあきらめるまで、なんどでも聞いてきます。
コールバックスタイルのメソッドでループとか、ベタに書くと頭痛いです。

動かすとこんな感じです。

ShowDialog は拡張メソッドとして作成しておくと、呼び出しに便利かもしれません。
コールバックスタイルの機能を、Task化するパターンはよく使いそうな気がします。TaskCompletionSource、覚えておきましょう。

16
17
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
16
17

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?