Asial Blog

Recruit! Asialで一緒に働きませんか?

【ぐぬぬ】iPhoneアプリのこんなとき、どうするの??

カテゴリ :
バックエンド(プログラミング)
タグ :
iPhone
Tech
Objective-C
Mac
Cocoa
こんにちは、iPhone大好き橋本です。

今日はiPhoneアプリの作成に関する「こんなときどうするの??」という疑問にお答えするべく、小技をいくつか紹介したいと思います。


1. 文字サイズに合わせてUILabelのサイズを変えたい。

UILabelを作成するときに、Labelに表示する文字によってUILabelのサイズを変えたいということ無いですか??ただ、UILabelを作成するときはframeのサイズを指定しなきゃいけませんよね。

こんなとき、どうするの??

そんなときには、UILabelにこんなカテゴリを書いちゃいましょ。

  1. @interface UILabel (resize)
  2. - (void) resizeWithPoint: (CGPoint)point;
  3. - (id) initWithText:(NSString *)text andFont:(UIFont *)font;
  4. - (id) initWithText:(NSString *)text andFont:(UIFont *)font andPoint:(CGPoint)point;
  5. - (CGRect)resizedFrameWithPoint: (CGPoint)point;
  6. @end
  7. @implementation UILabel (resize)
  8. - (void)resizeWithPoint:(CGPoint)point {
  9.     self.frame = [self resizedFrameWithPoint:point];
  10. }
  11. - (id)initWithText:(NSString *)text andFont:(UIFont *)font {
  12.     self = [self initWithText:text
  13.                       andFont:font
  14.                      andPoint:CGPointMake(0, 0)];
  15.     
  16.     return self;
  17. }
  18. - (id)initWithText:(NSString *)text andFont:(UIFont *)font andPoint:(CGPoint)point {
  19.     self = [super initWithFrame:CGRectZero];
  20. if (self) {
  21. self.numberOfLines = 0;
  22. self.font = font;
  23. self.text = text;
  24.         
  25.         self.frame = [self resizedFrameWithPoint:point];
  26. }
  27.     
  28.     return self;
  29. }
  30. - (CGRect)resizedFrameWithPoint:(CGPoint)point
  31. {
  32.     CGSize size = [self.text sizeWithFont:self.font
  33.                         constrainedToSize:CGSizeMake([[UIScreen mainScreen] bounds].size.width, NSIntegerMax) 
  34.                             lineBreakMode:UILineBreakModeTailTruncation];
  35.     
  36.     CGRect resizedFrame = CGRectMake(point.x, point.y, size.width, size.height);
  37.     
  38.     return resizedFrame;
  39. }
  40. @end

NSStringクラスの sizeWithFont:forWidth:lineBreakMode:メソッドを使用することで、UILabelの表示に必要なCGSizeを得ることができます。

CGSizeが取得できれば、あとは、UILabelのframeに取得したCGSizeを反映させれば、表示する文字に応じたサイズのUILabelを作成することができます。

CGSizeを得るためには、fontとtextが必要なため、今回はfontとtextを引数とするメソッドを書いてみました。オブジェクトを使い回すときのために、resizeWithPoint:も実装しておくといいと思います。

べんりー!


2. UIAlertViewにUITextFieldを表示したい。

UIAlertViewにUITextFieldを表示したいっていう場面があると思うんです。
ログインをユーザに求めるAlertを出したいときとか。

ただ、デフォルトのUIAlertViewだと、テキストを表示することしか出来ませんよね。

こんなとき、どうするの??

そんなときは、こういう感じで強引に実装しちゃいましょ。

  1. UIAlertView *alert = [[[UIAlertView alloc] initWithTitle:@"ログイン"
  2.                                                 message:@" \n\n"
  3.                                                delegate:self
  4.                                       cancelButtonTitle:@"Cancel"
  5.                                       otherButtonTitles:@"Login", nil] autorelease];
  6. // ユーザ名
  7. UITextField *userNameField = [[[UITextField alloc] initWithFrame:CGRectMake(12, 45, 260, 24)] autorelease];
  8. userNameField.placeholder = @"ユーザ名";
  9. userNameField.font = [UIFont systemFontOfSize:12];
  10. userNameField.borderStyle = UITextBorderStyleRoundedRect;
  11. userNameField.backgroundColor = [UIColor clearColor];
  12. userNameField.tag = 1;
  13. // キーボードのフォーカスを当てる
  14. [userNameField becomeFirstResponder];
  15. // パスワード
  16. UITextField *passwordField = [[[UITextField alloc] initWithFrame:CGRectMake(12, 72, 260, 24)] autorelease];
  17. passwordField.placeholder = @"パスワード";
  18. passwordField.font = [UIFont systemFontOfSize:12];
  19. passwordField.borderStyle = UITextBorderStyleRoundedRect;
  20. passwordField.backgroundColor =  [UIColor clearColor];
  21. passwordField.tag = 2;
  22. passwordField.secureTextEntry = YES;
  23. [alert addSubview:userNameField];
  24. [alert addSubview:passwordField];
  25. [alert show];

UIAlertViewのメッセージに改行を2つ入れて二行分のスペースを確保します。そのスペースにうまいことUITextFieldを入れてやります。
これだけでUITextFieldをもつUIAlertViewを表示出来ちゃうんですね。

べんりー!


3. UITextViewで表示したURLのリンク先をSafariじゃなくてUIWebviewで表示したい。

UITextViewのdataDetectorTypesプロパティにUIDataDetectorTypeLinkを設定すると、UITextViewに表示した文字列に含まれるリンクを拾うことができるようになるのはご存知のことかと思いますが、これだけだとリンクをクリックしたときにSafariで表示されちゃうんですね。

いちいちSafariが起動するのはイヤな感じですよね。

iPhone3Gだとマルチタスク対応してないので、Safariから戻ってきたときに、アプリを起動しなおすことになりますよね。

なんてだるい。
これはナンセンス。

UIWebViewを使ってアプリ内で表示できたら最高ですね。
いや、むしろ、ユーザにUIWebViewで開くか、Safariで開くか選択させてあげたいですよね。

UIActionSheetとか使って。

ただ、UITextView側ではこれを制御することが出来ないんです。

こんなとき、どうするの??

リンクが押されたときには、UIApplicationのopenUrl:メソッドが呼ばれるんです。
なので、このメソッドをオーバーライドしたサブクラスを作っちゃえばいいんですね。こんな感じで。

  1. @interface MyUIApplication : UIApplication {
  2.     NSURL *openUrl_;
  3. }
  4. @property (nonatomic, retain) NSURL *openUrl;
  5. @end
  6. #define ACTIONSHEET_OPEN_URL 1
  7. @implementation MyUIApplication
  8. @synthesize openUrl = openUrl_;
  9. - (BOOL)openURL:(NSURL *)url
  10. {
  11.     if (!url) {
  12.         return NO;
  13.     }
  14.     
  15.     self.openUrl = url;
  16.     
  17.     UIActionSheet *sheet = [[[UIActionSheet alloc] init] autorelease];
  18.     sheet.delegate = self;
  19.     [sheet addButtonWithTitle:@"このアプリで開く"];
  20.     [sheet addButtonWithTitle:@"Safariで開く"]; 
  21.     [sheet addButtonWithTitle:@"キャンセル"];
  22.     sheet.cancelButtonIndex = 2;
  23.     sheet.tag = ACTIONSHEET_OPEN_URL;
  24.     
  25.     hogeAppDelegate *appDelegate = (hogeAppDelegate *)[self delegate];
  26.     
  27.     [sheet showInView:appDelegate.window];
  28.     
  29.     return YES;
  30. }
  31. - (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex
  32. {
  33.     if (actionSheet.tag == ACTIONSHEET_OPEN_URL) {
  34.         switch (buttonIndex) {
  35.             case 0:
  36.             {
  37.                 // WebView表示用のController。各々自作してくださいね!デフォじゃないよ!
  38.                 WebViewController *webViewController = [[[WebViewController alloc] initWithUrlStr:[self.openUrl absoluteString]] autorelease];
  39.                 
  40.                  // 今回はUITabBarとUINavigationを組み合わせたアプリのnavigationに突っ込んでる例です。ここは各自カスタムしてね!
  41.                 hogeAppDelegate *appDelegate = (hogeAppDelegate *)[self delegate];
  42.                 UINavigationController *nav = (UINavigationController *)[appDelegate.tabBarController selectedViewController];
  43.                 
  44.                 [nav pushViewController:webViewController animated:YES];
  45.                 break;                
  46.             }
  47.                 
  48.             case 1:
  49.             {
  50.                 [super openURL:self.openUrl];
  51.                 break;                   
  52.             }
  53.                 
  54.             case 2:
  55.             {
  56.                 break;
  57.             }
  58.             default:
  59.                 break;
  60.         }
  61.     }
  62.     
  63.     self.openUrl = nil;
  64. }
  65. @end
あとはこのクラスをデフォルトのUIApplicationと置き換えるだけ!main.mを書き換えてください。

  1. int main(int argc, char *argv[])
  2. {
  3.     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
  4.     // UIApplicationMain関数の第三引数を書き換える。
  5.     int retVal = UIApplicationMain(argc, argv, @"MyUIApplication", nil);
  6.     [pool release];
  7.     return retVal;
  8. }

なんてユーザフレンドリー!

べんりー!


4. UIViewを角丸にしたい。
したいですよねー、角丸。やっぱりどうしてもこういう欲求出てきますよねー。

こんなとき、どうするの??

簡単です。

UIViewのlayerプロパティをいじりましょう。layerをいじりたいときはQuartsCore/QuartsCore.hをimportするのをお忘れなく。

  1. UIView *hoge = [[[UIView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)] autorelease];
  2. hoge.layer.cornerRadius = 8.0f;

はい。これだけです。

べんりー!


5. UIViewを角丸にしたついでに影とかつけたい。

影もつけたいですよね。そうくると思ってました。わかります。

じゃあ、こんなとき、どうするの??

これもlayerいじると簡単に実装出来ちゃうんですね。

  1. UIView *hoge = [[[UIView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)] autorelease];
  2. hoge.layer.cornerRadius = 8.0f
  3. // 次の二行を追加するだけです。
  4. hoge.layer.shadowOpacity = 0.8;
  5. hoge.layer.shadowOffset = CGSizeMake(2, 2);

shadowOpacityは影の濃さ、shadowOffsetは影の場所です。あとは、shadowColorで色を変えたりすることもできますよ。

べんりー!

今回は5つほど小技を紹介させていただきました。どれも実際にアプリを作っていると出てくる疑問点、欲求かと思います。

今回紹介させていただいた小技が、少しでも読んでいただいた方のお役にたてると幸いです。

ではでは。