しっぽを追いかけて

ぐるぐるしながら考えています

Unity と猫の話題が中心   掲載内容は個人の私見であり、所属組織の見解ではありません

Xamarin.iOS で UIBarButtonItem の Click イベントが検知できない件

前回の投稿iOS 用の BottomBar にボタンを追加できたので、Click イベントを拾ってみます

UIToolBar のボタン、UIBarButtonItem の Click イベントはセレクター指定や GestureRecognizer を利用する方法などいろんな方法で設定できるようですが、今回は下記のようにスタンダードなやり方で検知することにしました

/// <summary>
/// Element 変更イベントハンドラ
/// </summary>
/// <param name="e">イベント引数</param>
protected override void OnElementChanged(ElementChangedEventArgs<BottomBar> e)
{
    base.OnElementChanged(e);

    var toolBar = new UIToolbar()
    {
        BarStyle = UIBarStyle.Default,
    };

    this.stopButton = new UIBarButtonItem(UIBarButtonSystemItem.Rewind)
    {
        AccessibilityLabel = "停止",
    };
    this.pauseButton = new UIBarButtonItem(UIBarButtonSystemItem.Pause)
    {
        AccessibilityLabel = "一時停止",
    };
    this.startButton = new UIBarButtonItem(UIBarButtonSystemItem.Play, this.OnStart)
    {
        AccessibilityLabel = "開始",
    };

    var barItems = new List<UIBarButtonItem>()
    {
        new UIBarButtonItem(UIBarButtonSystemItem.FlexibleSpace),
        this.stopButton,
        new UIBarButtonItem(UIBarButtonSystemItem.FlexibleSpace),
        this.pauseButton,
        new UIBarButtonItem(UIBarButtonSystemItem.FlexibleSpace),
        this.startButton,
        new UIBarButtonItem(UIBarButtonSystemItem.FlexibleSpace),
    };

    toolBar.SetItems(barItems.ToArray(), true);
            
    // UIToolBar をネイティブコントロールとして設定
    this.SetNativeControl(toolBar);

    // 画面描画を要求
    this.SetNeedsDisplay();
}

/// <summary>
/// 開始ボタン押下イベントハンドラ
/// </summary>
/// <param name="sender">イベント発行者</param>
/// <param name="e">イベント引数</param>
private void OnStart(object sender, EventArgs e)
{
    // クリックされたらダイアログ表示
    new UIAlertView(
        "Tapped!",
        "Start Button Tapped",
        null,
        "OK",
        null).Show(); 
}

これでできるだろうと実行してみると・・・これがボタンを押してもイベントハンドラが呼ばれない!

文法的な誤りはないのにイベントハンドラが呼ばれない謎・・・いろいろ調べた結果、iOS アプリの場合 GestureRecognizer が設定されているとボタンの Click イベントまでそこで処理されてしまいイベントハンドラが呼び出されないことがあるそうです;

でも GestureRecognizer は使っていないぞ?!と思ったら・・・Xamarin.iOS でもデフォルトで ViewController には GestireRecognizer が設定されるとか

暗黙的ルールとか困ります・・・じゃあその GestireRecognizer はどうやってはずすんだ?!と調べたらアプリ起動処理がかかれた AppDelegate を下記のように改修すればいけました

// The UIApplicationDelegate for the application. This class is responsible for launching the 
// User Interface of the application, as well as listening (and optionally responding) to 
// application events from iOS.
[Register("AppDelegate")]
public partial class AppDelegate : UIApplicationDelegate
{
    // class-level declarations
    UIWindow window;

    //
    // This method is invoked when the application has loaded and is ready to run. In this 
    // method you should instantiate the window, load the UI into it and then make the window
    // visible.
    //
    // You have 17 seconds to return from this method, or iOS will terminate your application.
    //
    public override bool FinishedLaunching(UIApplication app, NSDictionary options)
    {
        Forms.Init();

        window = new UIWindow(UIScreen.MainScreen.Bounds);

        window.RootViewController = App.GetMainPage().CreateViewController();

        window.MakeKeyAndVisible();

        var controller = window.RootViewController.ChildViewControllers.FirstOrDefault();
        if (controller != null)
        {
            // デフォルトの GestureRecognizer を無効にする
            controller.View.RemoveGestureRecognizer(controller.View.GestureRecognizers.FirstOrDefault());
        }

        return true;
    }
}

f:id:matatabi_ux:20140927225005p:plain

GestureRecognizer は単純なイベントハンドラを組み合わせてスワイプやピンチなどのタッチ操作を認識する機能を提供しているらしく、お手軽にタッチ検知するには便利なようですが他のイベントを抑制してしまうので使いにくいですね;