[iOS] ユーザ名/パスワードの入力ができるカスタム UIAlertView ライブラリを公開

2011年5月11日水曜日 | Published in | 0 コメント

このエントリーをはてなブックマークに追加

ユーザ名とパスワードの入力を促すカスタム UIAlertView を公開しました。
lakesoft/LKAccountPanel - GitHub



呼び出しコードはこんな感じ。
[LKAccountPanel showWithTitle:@"Test"
                       completion:^(BOOL result, NSString* username, NSString* password) {
                           NSLog(@"result: %d\nusername: %@\npassword: %@",
                                 result, username, password);
                       }];
結果を Blocks で処理できるようにしてみた。

[2011-05-17 追記] 非同期用のメソッドを追加しました。
BOOL result = [LKAccountPanel showWithTitle:@"Test2"
                                       username:&username
                                       password:&password];
    NSLog(@"done2");
    NSLog(@"result2: %d\nusername: %@\npassword: %@",
          result, username, password);

参考情報:Cocoaの日々: [iOS] 非同期処理を同期処理に変える



インストール


GitHubからプロジェクトをダウンロードした後 Xcode 4 で開く。

その中から LKAccountPanel.h と LKAccountPanel.m、そして LKAccountPanel.strings を自分のプロジェクトへコピーして追加する。


使い方


クラスメソッド showWithTitle:completion: を呼び出すだけ。
(例)[LKAccountPanel showWithTitle:@"タイトル"
                       completion:^(BOOL result, NSString* username, NSString* password) {
                                   if (result) {
                                       // OKボタンが押された場合の処理
                                   }
                       }];
OKボタンが押された場合は、result == YESとなる。この時入力値が username, password に格納されて渡される。いわゆる必須チェックは行っていないので未入力でもOKボタンが押せる。未入力やキャンセル時には username と password には nil が入る。


カスタマイズ


ボタンの文言や、UITextField のプレースホルダに表示する文字列は LKAccountPanel.strings で定義している。
"OK" = "OK";
"Cancel" = "キャンセル";
"Username" = "ユーザ名";
"Password" = "パスワード";
他の文言に変更したい場合はここを変える。現在は日本語と英語の文字列を用意しているが必要なら他の言語の追加もできる。


ソース解説


定義はこう。
@interface LKAccountPanel :
 NSObject  {

}
@property (nonatomic, retain) UITextField* usernameTextField;
@property (nonatomic, retain) UITextField* passwordTextField;

// API
+ (void)showWithTitle:(NSString*)title completion:(void(^)(
  BOOL result, NSString* username, NSString* password))completion;

@end
UIAlertViewDelegate を実装してこのクラスで UIAlertViewに関する処理を完結させている。

シングルトンパターンを使っていて、showWithTitle:completion: を呼び出すと内部的には1つのインスタンスを使い回し、このインスタンスで実際の処理を行わせる。
+ (void)showWithTitle:(NSString*)title
  completion:(void(^)(BOOL result, NSString* username, NSString* password))completion
{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        accountPanel_ = [[LKAccountPanel alloc] init];
    });
    
    [accountPanel_ _showWithTitle:title completion:completion];
}
シングルトンの初期化には dispatch_once を使っている。
[参考] Cocoaの日々: [iOS][Mac] dispatch_once を使ったシングルトン

UIAlertView のカスタマイズと表示は下請けのメソッド _showWithTitle:completion: でやる。
- (void)_showWithTitle:(NSString*)title
  completion:(void(^)(BOOL result, NSString* username, NSString* password))completion;
{
    self.completionBlock = completion;
    self.alertView =
        [[[UIAlertView alloc] initWithTitle:title
                                    message:@"\n\n\n"
                                   delegate:self 
                          cancelButtonTitle:NSLocalizedStringFromTable(
                              @"Cancel", LOCALIZED_STRING_TABLE, nil)
                          otherButtonTitles:NSLocalizedStringFromTable(
                              @"OK", LOCALIZED_STRING_TABLE, nil), nil] autorelease];

    LKAccountPanelBackgroundView* backgroundView =
        [[[LKAccountPanelBackgroundView alloc]
          initWithFrame:CGRectMake(15.0, 47.0, 255.0, 65)] autorelease];
    [self.alertView addSubview:backgroundView];
    
    self.usernameTextField = [[[UITextField alloc] initWithFrame:
                               CGRectMake(20.0, 52.0, 245.0, 30.0)] autorelease];
    self.usernameTextField.placeholder =
         NSLocalizedStringFromTable(@"Username", LOCALIZED_STRING_TABLE, nil);
    self.usernameTextField.keyboardType = UIKeyboardTypeEmailAddress;
    self.usernameTextField.autocapitalizationType = UITextAutocapitalizationTypeNone;
    self.usernameTextField.returnKeyType = UIReturnKeyNext;
    self.usernameTextField.delegate = self;
    [self.alertView addSubview:self.usernameTextField];

    self.passwordTextField = [[[UITextField alloc] initWithFrame:
                               CGRectMake(20.0, 87.0, 245.0, 30.0)] autorelease];
    self.passwordTextField.placeholder =
       NSLocalizedStringFromTable(@"Password", LOCALIZED_STRING_TABLE, nil);
    self.passwordTextField.keyboardType = UIKeyboardTypeASCIICapable;
    self.passwordTextField.autocapitalizationType = UITextAutocapitalizationTypeNone;
    self.passwordTextField.secureTextEntry = YES;
    self.passwordTextField.returnKeyType = UIReturnKeyDone;
    self.passwordTextField.delegate = self;
    [self.alertView addSubview:self.passwordTextField];

    [self.alertView show];
    [self.usernameTextField becomeFirstResponder];
}
・UITextFiled を2つ作って UIAlertView へ貼りつけている。
・場所を確保するために改行を3つ入れている。
・ローカライズ文字列の取得には NSLocalizedStringFromTable() を使用している。
と、あんまりたいしたことはやってない。UIAlertView のインスタンスを保持しておくのはキーボードで "Done"が押された時に閉じたいから。
- (BOOL)textFieldShouldReturn:(UITextField *)textField
{
    if (textField == self.usernameTextField) {
        [self.passwordTextField becomeFirstResponder];
    } else if (textField == self.passwordTextField) {
        [self.alertView dismissWithClickedButtonIndex:1 animated:YES];
        [self alertView:self.alertView clickedButtonAtIndex:1];
    }
    return YES;
}
-[UIAlertView dismissWithClickedButtonIndex:animated:] は何故かデリゲートメソッド alertView:clickedButtonAtIndex: を呼び出してくれなかったので明示的に呼び出すようにした。

なお Blocks もプロパティとして定義することができる。
@property (nonatomic, retain copy)
   void(^completionBlock)(BOOL result, NSString* username, NSString* password);
こうやっておくと retain の手間が省けるし release も楽。

UITextFiled の背景用に専用の UIView を用意している。
@interface LKAccountPanelBackgroundView : UIView {
}
@end
drawRect: で UIBezierPath を使い角丸矩形と横線、そして白い背景を描く。
@implementation LKAccountPanelBackgroundView

- (id)initWithFrame:(CGRect)frame {
    self = [super initWithFrame:frame];
    if (self) {
        self.backgroundColor = [UIColor clearColor];
    }
    return self;
}

- (void)drawRect:(CGRect)rect
{
    UIBezierPath* path = [UIBezierPath bezierPathWithRoundedRect:self.bounds
                                                    cornerRadius:5.0];
    [path moveToPoint:CGPointMake(0, self.bounds.size.height/2.0)];
    [path addLineToPoint:CGPointMake(self.bounds.size.width-1.0,
                                     self.bounds.size.height/2.0)];
    [[UIColor whiteColor] set];
    [path fill];
    [[UIColor lightGrayColor] set];
    [path stroke];
}
@end
UIBezierPath が追加されてほんと楽だ。


ライセンス


MIT ライセンスです。商用・非商用を問わず利用可能。カスタマイズしての再配布も自由。連絡も不要(でもくれるとうれしい)。

Responses

Leave a Response

人気の投稿(過去 30日間)