カスタムダイアログ
ユーザに操作指示を尋ねるために標準では UIActionSheetが用意されている。
UIActionSheetはモーダルなので、非モーダルにする場合は通常ツールバーを使う。
下は Pastebotの例。
ツールバーはボタンを赤くするにはカスタムな UIButton を貼り付ける必要があるようだ。
Pastebotの場合、さらにその上にメッセージを表示している。
今回はこれを実現する仕組みを作ってみた。
仕組み
UILabel と UIButton が乗った UIView を1枚用意して、アニメーションを使って下からせり上がるように表示する。
実装
まずダイアログを管理する CustomDialogViewController を用意する。
#import "CustomDialogViewDelegate.h"
@interface CustomDialogViewController : UIViewController {
NSString* labelText_;
NSString* buttonTitle_;
id delegate_;
UILabel* label_;
UIButton* button_;
}
@property (nonatomic, copy) NSString* labelText;
@property (nonatomic, copy) NSString* buttonTitle;
@property (nonatomic, assign) id delegate;
@property (nonatomic, retain) IBOutlet UILabel* label;
@property (nonatomic, retain) IBOutlet UIButton* button;
-(IBAction)touchedButton:(id)sender;
@end
ユーザインターフェイスは Interface Builder で作る。こんな感じ
コントローラを初期化するときにこの Nibを読み込む。
- (id)init {
if (self = [super initWithNibName:NSStringFromClass([self class])
bundle:nil]) {
}
return self;
}
ラベルとボタンはインスタンス化のタイミングが表示される時になる。この為、表示する文字列は別途プロパティを用意し、先にここへいれておくようにする。インスタンス化された後(viewDidLoadのタイミング)に設定する。
- (void)viewDidLoad {
[super viewDidLoad];
[self.button setTitle:self.buttonTitle
forState:UIControlStateNormal];
self.label.text = self.labelText;
}
ダイアログのボタンが押された時にはデリゲート先にメッセージを送る。その為にプロトコルを定義しておく。
@protocol CustomDialogViewDelegate
-(void)touchedButton:(id)sender;
@end
次にこのダイアログの表示制御を行うために UIViewController にカテゴリでメソッドを追加する。
@interface UIViewController (CustomDialog)
- (void)presentDialogViewController:(UIViewController*)controller animated:(BOOL)animated;
- (void)dismissDialogViewController:(UIViewController*)controller animated:(BOOL)animated;
@end
上記は UIViewController 標準の presentModalViewController:animated:, dismissModalViewControllerAnimated: をまねた。標準と異なり、閉じるときにも UIViewController を必要とするのは、カテゴリではインスタンス変数を持てない為、状態管理ができないから。状態管理をやる場合はカテゴリではなくサブクラスにするといい。今回はお手軽に機能追加できるようにカテゴリの方法を採用した。
実装はこう。UIViewのアニメーション関連のメソッドを使っている。Blockが使えるのは非常に便利。
#pragma mark -
#pragma mark Manage dialog
- (BOOL)isExistSubView:(UIView*)view
{
BOOL is_exist = NO;
for (UIView* subview in self.view.subviews) {
if (view == subview) {
is_exist = YES;
break;
}
}
return is_exist;
}
- (void)presentDialogViewController:(UIViewController*)controller animated:(BOOL)animated
{
CGRect frame1 = self.view.frame;
CGRect frame2 = controller.view.frame;
// (1) init position
frame2.origin.y = frame1.size.height;
controller.view.frame = frame2;
if ([self isExistSubView:controller.view]) {
[self.view bringSubviewToFront:controller.view];
} else {
[self.view addSubview:controller.view];
}
// (2) animate
frame2.origin.y = frame1.size.height - frame2.size.height;
if (animated) {
[UIView animateWithDuration:0.5
animations:^{controller.view.frame = frame2;}];
} else {
controller.view.frame = frame2;
}
}
- (void)dismissDialogViewController:(UIViewController*)controller animated:(BOOL)animated
{
if (![self isExistSubView:controller.view]) {
return;
// do nothing
}
CGRect frame1 = self.view.frame;
CGRect frame2 = controller.view.frame;
// (1) animate
frame2.origin.y = frame1.size.height;
if (animated) {
[UIView animateWithDuration:0.5
animations:^{controller.view.frame = frame2;}
completion:^(BOOL finished){[controller.view removeFromSuperview];}
];
} else {
[controller.view removeFromSuperview];
}
}
@end
最後にこれを使うクライアントコード。まず初期化。
- (void)viewDidLoad {
[super viewDidLoad];
CustomDialogViewController* controller =
[[CustomDialogViewController alloc] init];
controller.delegate = self;
controller.buttonTitle = @"close dilaog";
controller.labelText = @"Custom Dialog Opened!";
self.dialogViewController = controller;
[controller release];
}
次にダイアログの開閉。
- (IBAction)openDialog:(id)sender
{
if (opened) {
[self dismissDialogViewController:self.dialogViewController
animated:YES];
} else {
[self presentDialogViewController:self.dialogViewController
animated:YES];
}
opened = !opened;
}
ダイアログボタンが押された時のアラート表示。
-(void)touchedButton:(id)sender
{
UIAlertView* alert = [[[UIAlertView alloc] initWithTitle:@"Message"
message:@"Touched dialog button"
delegate:nil
cancelButtonTitle:nil
otherButtonTitles:@"OK", nil] autorelease];
[alert show];
}
実行結果
さて実行してみよう。初期状態。
ボタンを押すと下からダイアログがせり上がってくる。
ダイアログのボタンを押すと -[CustomDialogDelegate touchedButton:] が呼ばれ、アラートが表示される。
もう一度ボタンを押すとこんどはアニメーションしながらダイアログが下へ消える。
ソースコード
DialogSample at 2010-07-23 from xcatsan's iOS-Sample-Code - GitHub