iPhone/iPad のアプリでポップアップして情報を表示する小さいウィンドウ(トースト)を表示する

Android の Toast のようなものは Apple からは提供されてないけど、UIButton でわりと簡単に代用できる。方法としては、UIButton をひとつ作ってアプリの window の subview として登録してやればいい。そして、ボタンを押したときに自身を removeFromSuperView してやれば画面から消える。以下、UITableViewController での実装例をメモ的にはってみる。

IB は使わず、ARC は有効にしてある。Empty アプリケーションを作り、Toast.m という UITableViewComtroller のサブクラスをひとつ作っている。ToastView とか独立したクラス化することも容易だけど、複数の Toast を表示させるときに多少制御する必要があって、少々面倒なのでここではやってない。

下のほうに表示されている黒っぽいウィンドウがそれ。

AppDelegate.m

Toast クラスのインスタンスをひとつ作り、rootViewController に登録しているだけ。

#import "Toast.h"

- (BOOL)application:(UIApplication *)application 
             didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
    
    Table *table = [[Table alloc] initWithStyle:UITableViewStylePlain ];

    [self.window setRootViewController:table];
    
    self.window.backgroundColor = [UIColor whiteColor];
    [self.window makeKeyAndVisible];
    return YES;
}

Toast.h

#import <UIKit/UIKit.h>

@interface Table : UITableViewController
{
    UIButton *toast;
}
@end

Toast.m

表示されたトーストは、フェード効果つきで表示され、クリックするとフェードアウトして消えるようにしてみた。Toast を複数表示しようとすると、前のものを消してから表示しなおすようにしてある。表示位置は、コメントアウトされているものを使いわけることで変更できる。なお、変更したメソッドのみ。文字列から Toast のサイズを決めるコードは、下記のコードをかなり使っている。

メソッドは書きかえが必要なところだけ。

#import "Toast.h"
#import <QuartzCore/QuartzCore.h>
// ウィンドウの角を丸めるためには QuartzCore.h の import が必要
// ちなみに角を丸める処理は結構重い

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
    return 1;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    return 20;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *CellIdentifier = @"Cell";
    
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    if (cell == nil) {
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault 
                   reuseIdentifier:CellIdentifier];
    }
    
    cell.textLabel.text = [NSString stringWithFormat:@"%d", indexPath.row ];
    
    return cell;
}


- (void)show:(NSString*)text
{
    if ( toast != nil ){
        [toast removeFromSuperview];
    }
    UIWindow *window = self.view.window;

    // テキストとフォントのサイズから、ラベルのサイズを決めてラベルに文字を書きこむ。
    // 長いテキストは折りかえす。サイズは 280x60 で、これを越える場合は文末が省略される。
    UIFont *font = [UIFont systemFontOfSize:16];
	CGSize textSize = [text sizeWithFont:font constrainedToSize:CGSizeMake(280, 60)];
	
	UILabel *label = [[UILabel alloc] 
                initWithFrame:CGRectMake(0, 0, textSize.width + 5, textSize.height + 5)];
	label.backgroundColor = [UIColor clearColor];
	label.textColor = [UIColor whiteColor];
	label.font = font;
	label.text = text;
	label.numberOfLines = 0;
	label.shadowColor = [UIColor darkGrayColor];

	// テキストの表示位置のオフセット
	label.shadowOffset = CGSizeMake(1, 1);

	// toast として使うボタンの生成	
	UIButton *v = [UIButton buttonWithType:UIButtonTypeCustom];
	v.frame = CGRectMake(0, 0, textSize.width + 10, textSize.height + 10);
	label.center = CGPointMake(v.frame.size.width / 2, v.frame.size.height / 2);

	// label をボタンに登録する
	[v addSubview:label];
	
    v.backgroundColor = [UIColor colorWithRed:0 green:0 blue:0 alpha:0.7];

    // 角を丸める処理
	v.layer.cornerRadius = 5;
    
	CGPoint point = CGPointMake(window.frame.size.width/2, 
                window.frame.size.height/2);

    // 上部に表示させる場合	
    //point = CGPointMake(window.frame.size.width / 2, 45); 

    point = CGPointMake(window.frame.size.width / 2, window.frame.size.height - 45);

    // 中央に表示させる場合
    //	point = CGPointMake(window.frame.size.width/2, window.frame.size.height/2);
	
    point = CGPointMake(point.x, point.y);
    v.center = point;
    
    // ボタンを押したときに非表示にする
    [v addTarget:self action:@selector(hide:) forControlEvents: UIControlEventTouchDown];

    // ボタンが表示されるときにフェード効果を付ける
    toast = v;
    toast.alpha = 0.0;

    [window addSubview: toast];

    // toast.alpha を 0 から 1.0 まで 0.5 秒で変化させるアニメーションを登録する
    [UIView beginAnimations:nil context: NULL];
    toast.alpha = 1.0f;
    [UIView setAnimationDuration:0.5];

    // アニメーションを実行する。
    [UIView commitAnimations];
}

-(void)removeToast
{
    // 画面から強制的に消す
    [toast removeFromSuperview]; 
//    NSLog(@"remove");
}

-(void)hide:(UIButton*)button
{
    // toast.alpha を 1.0 から 0 まで 0.5 秒で変化させるアニメーションを登録する
    [UIView beginAnimations:nil context: NULL];

    button.alpha = 0.0;

    [UIView setAnimationDuration:0.5];
    [UIView setAnimationDelegate:self];

    // アニメーション終了後に removeToast を呼びだす。
    [UIView setAnimationDidStopSelector:@selector(removeToast)];
    [UIView commitAnimations];
    
//    NSLog(@"push");
}

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    // Toast を表示させる処理
    [self show: [NSString stringWithFormat:@" 選ばれた項目は %d 番目っすよ", indexPath.row]];
}

@end