酢ろぐ!

カレーが嫌いなスマートフォンアプリプログラマのブログ。

iOS 7.1.2でUIActionSheetに表示されている1番目の項目を選択しても何も実行されない

割と有名な話かもしれませんが iOS 7とiOS 8以降とでUIActionSheetの挙動が違っていたためハマりました。

Xcode 7.3.1ではすでにiOS 7はサポートされておらず、手元にiOS 7デバイスがなかったのでハマったとも言えます。挙動が掴めずに最終的にiOS 7デバイスを手配することとなってしまいました。

問題の発生した状況はいたって単純で、0〜3の数字をアクションシートを表示させてユーザーに選択させるというシチュエーションでした。すでにUIActionSheetはiOS 8.3でdeprecated扱いになっていますが、互換性維持のためかiOS 9.3.2時点でも普通に動いていました。

let actionSheet = UIActionSheet(title: "タイトル", delegate: self, cancelButtonTitle: "キャンセル", destructiveButtonTitle: nil)
actionSheet.addButtonWithTitle("0")
actionSheet.addButtonWithTitle("1")
actionSheet.addButtonWithTitle("2")
actionSheet.addButtonWithTitle("3")
actionSheet.showFromTabBar(tabbar)

ユーザーによって数字が選択されるとactionSheet(:clickedButtonAtIndex:)が呼ばれ、選択されたボタンのインデックスがcancelButtonIndexと異なっていればhogeActionを実行するというものです。

func actionSheet(actionSheet: UIActionSheet, clickedButtonAtIndex buttonIndex: Int) {
    if buttonIndex != actionSheet.cancelButtonIndex {
        hogeAction(buttonIndex)
    }
}

問題としては、iOS 7.1.2デバイスで「0」を選択するとhogeActionが実行できませんでした。iOS 8.4、iOS 9.3.2ではhogeActionは実行されていました。

ドキュメントではcancelButtonIndexについて以下のように書かれていました。

Button indices start at 0. The default value of this property is normally -1, which indicates that no cancel button has been set. However, a cancel button may be created and set automatically by the initWithTitle:delegate:cancelButtonTitle:destructiveButtonTitle:otherButtonTitles: method. If you use that method to create a cancel button, you should not change the value of this property.

「通常は-1が割り当てられるけど、コンストラクタでキャンセルボタンを指定していると自動で割り当てられる」と書かれています*1。iOS 7の時はcancelButtonIndexは0が初期値になっているのかもしれないので明示的にcancelButtonIndexの値を指定することにしました。

解決編

UIActionSheetのコンストラクタでcancelButtonTitleの「キャンセル」を指定せずに、cancelButtonIndexを明示的に指定することで意図した挙動をおこなうようにしました。

let actionSheet = UIActionSheet(title: "タイトル", delegate: self, cancelButtonTitle: nil, destructiveButtonTitle: nil)
actionSheet.addButtonWithTitle("0")
actionSheet.addButtonWithTitle("1")
actionSheet.addButtonWithTitle("2")
actionSheet.addButtonWithTitle("3")
actionSheet.cancelButtonIndex = actionSheet.addButtonWithTitle("キャンセル")
actionSheet.showFromTabBar(tabbar)

これでiOS 7とiOS 8以降でもきちんと動いてくれましたが、開発環境やCocoaPodsの関係でiOS 7のサポートが切れると嬉しいなと考えています。

*1:読み間違えてたら教えて欲しい!