UIAlertViewでとまどったこと…その2

先日の日記「UIAlertViewでとまどったこと…その1」では、アラート表示中にホームボタンを押すと困ったことになるかもしれない…ということを書きました。で、iPhoneにはもう1つボタンがありますよね?


そう、電源ボタンです。
この電源ボタンがUIAlertViewに更なる悲劇をおよぼすことに…。

悲劇のメソッドdidDismissWithButtonIndex

UIAlertViewには様々なメソッドがありますが、先日紹介したのはこちら。

- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex

これはアラートのボタンを押すと呼ばれるメソッドですが、次のようなメソッドもあります。

- (void)alertView:(UIAlertView *)alertView didDismissWithButtonIndex:(NSInteger)buttonIndex {

これは、アラートのボタンを押してアラートが閉じた後に呼ばれるメソッドです。

私は「Touch Touch Shapes」でゲームオーバー後の選択を行う際に、このdidDismissWithButtonIndexを使用しました。


例えば「ゲームオーバー」→「コンティニュー」の動作の場合、clickedButtonAtIndexを使用するとアラートのウィンドウが消える前にゲームが再開されてしまうんですね。

ですがdidDismissWithButtonIndexを使用すると、アラートのウィンドウが消えてからゲームが再開されるので、処理的にも見た目的にも都合がよかった訳です。

が、しかし…

何故呼ぶ

Touch Touch Shapes」のテスト中に、私はとある動作を行いました。それがアラート表示中に電源ボタンを押すことです。*1

ゲームオーバー中にその動作を行いスリープモードにし、続いてスリープを解除したところ…

あれ?アラートのボタンを押していないのにコンティニューされた?

しかもアラートが閉じられていない?

という謎の動作が!


ここで…サンプルコードを書いてみます。これも前回同様UIViewを継承したクラスに実装します。

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
	UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"AlertTest"
               message:@"Test"
               delegate:self
               cancelButtonTitle:@"Button1"
               otherButtonTitles:@"Button2", @"Button3", nil];
	[alert show];
	[alert release];
	
}

- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex {
	NSLog(@"clickedButtonAtIndex");
	NSLog(@"buttonIndex:%d", buttonIndex);
}

- (void)alertView:(UIAlertView *)alertView didDismissWithButtonIndex:(NSInteger)buttonIndex {
	NSLog(@"didDismissWithButtonIndex");
	NSLog(@"buttonIndex:%d", buttonIndex);
}

そして、アラートを表示して電源ボタンを押すとデバッガコンソール上には…

didDismissWithButtonIndex
buttonIndex:0

それだけならまだしも、アラートが消えていない…。ここで試しに、アラートのButton2を押すとアラートが閉じ、デバッガコンソール上には…

clickedButtonAtIndex
buttonIndex:1
didDismissWithButtonIndex
buttonIndex:1

これはひどい


まとめるとこのような動作になります。

  1. アラートを表示する
  2. 電源ボタンを押してスリープモードにする
  3. そのときdidDismissWithButtonIndexが呼び出される
  4. スリープモードを解除
  5. アラートは閉じられていない
  6. アラートが開いている限り、電源ボタンを押すと何回でもdidDismissWithButtonIndexが呼び出される

解決方法

今回の場合は「アラートにキャンセルボタンを設定しない」「alertViewCancelを実装する」という方法では解決されませんでした。
そこで、私がとった方法は以下のようなフラグ処理です。

- 何か初期化処理のメソッド {
	pushFlag = false;
}

- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex {
	pushFlag = true;
	NSLog(@"clickedButtonAtIndex");
	NSLog(@"buttonIndex:%d", buttonIndex);
}

- (void)alertView:(UIAlertView *)alertView didDismissWithButtonIndex:(NSInteger)buttonIndex {
	if (pushFlag) {
		NSLog(@"didDismissWithButtonIndex");
		NSLog(@"buttonIndex:%d", buttonIndex);
		pushFlag = false;
	}
}

グローバル変数のpushFlagを設定して、アラートのボタンが押された時だけdidDismissWithButtonIndex内の処理を実行するようにしています。

このdidDismissWithButtonIndexに関する動作…かなり凶悪なのでiPhoneアプリ開発者の皆さんには本当に知っていてほしいです。


「Touch Touch Shapes」制作時はOS2.2で動作確認を行いましたが、この動作はOS3.0でも変わっていませんでした。


「UIAlertViewでとまどったこと…その3」は今の所ありません。

*1:iPhoneをスリープさせる動作です。