「UINavigationController」カテゴリーアーカイブ

UINavigationViewControllerで、戻るボタンを押したときに処理を実行


stackoverflow:Find out if user pressed the back button in uinavigationcontroller?

iOS 5.0より前のバージョンではnavigationControllerが現在のコントローラを持っているかチェックする。(willDisappearだけど、既にコントローラは除去されているようです)
- (void)viewWillDisappear:(BOOL)animated
{
    [super viewWillDisappear:animated];

    if (![[self.navigationController viewControllers] containsObject:self]) {
        // We were removed from the navigation controller's view controller stack
        // thus, we can infer that the back button was pressed
    }
}
iOS5.0以降なら、-didMoveToParentViewController を使う。

- (void)didMoveToParentViewController:(UIViewController *)parent
{
    // parent is nil if this view controller was removed
}

UINavigationBarに2行のタイトルを表示


UIViewContorollerのnavigationItem.titleViewに、タイトルを2行表示するためのUILabelを設定してから、UINavigationControllerにpushすれば良い。

以下のソースコードをコピーすれば、ラベルのサイズや文字のスタイルを調整する手間が省けます。

UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 480, 44)];
label.backgroundColor = [UIColor clearColor];
label.numberOfLines = 2;
label.font = [UIFont boldSystemFontOfSize: 14.0f];
label.shadowColor = [UIColor colorWithWhite:0.0 alpha:0.5];
label.textAlignment = UITextAlignmentCenter;
label.textColor = [UIColor whiteColor];
label.text = @"タイトル\nサブタイトル";

controller.navigationItem.titleView = label;

スクリーンショット

タイトルを2行に
タイトルを2行に

参考
http://stackoverflow.com/questions/2422383/uinavigationbar-multi-line-title

クラスごとのtintColor実装バージョン


iOS5.0以降では、tintColorを利用できるクラスが増えました。iOS4系以前との互換性を保つためには、新規導入されたtintColorを使わないか、tintColorが実装されていない場合の代替処理を記述しておく必要があるので注意が必要です。

iOS 5.0以降

以下の3つのクラスではiOS5.0以降でtintColorを設定できます。

  • UIBarButtonItem
  • UIButton
  • UITabBar

    iOS2.0以降

    以下の4つのクラスではiOS2.0以降でtintColorを設定できます。

  • UINavigationBar
  • UISearchBar
  • UISegmentedControl
  • UIToolbar
  • UIScrollViewを使って画面を横に並べる


    UIScrollViewを使って、横方向にページを並べてみます。Empty Applicationを作成し、ApplicationDelegate.mの起動メソッドを以下のように書き換えます。PAGE_COUNTにはページ数を設定しておいてください。(例: #define PAGE_COUNT 3)

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

        UIScrollView * scrollView = [[UIScrollView alloc] init];
        scrollView.backgroundColor = [UIColor whiteColor];
        //ページ数分の横幅を確保します
        scrollView.contentSize = CGSizeMake(320.0 * PAGE_COUNT, 400.0f);
        //1ページごとにめくれるように設定。これがないと中途半端な位置で止まります。
        scrollView.pagingEnabled = YES;
        for(int i = 0; i < PAGE_COUNT; i++){//ページの中身を作成
            UILabel * label = [[UILabel alloc] initWithFrame:CGRectMake(320.0f * i, 0, 100, 100)];
            label.text = [NSString stringWithFormat:@"%d", i];
            label.font = [UIFont fontWithName:label.font.fontName size:40.0f];
            label.textAlignment = UITextAlignmentCenter;
            label.backgroundColor = [UIColor yellowColor];
            [scrollView addSubview:label];
        }
        //ナビゲーションコントロールの中に設定してみます。
        UIViewController * scrollViewController = [[UIViewController alloc] init];
        scrollViewController.view = scrollView;    
        UINavigationController *navigationController = [[UINavigationController alloc] initWithRootViewController:scrollViewController];
        self.window.rootViewController = navigationController;
       
        [self.window makeKeyAndVisible];
        return YES;
    }

    スクリーンショット

    ナビゲーションバーの表示を切り替える

    スクロールを検知してナビゲーションバーの表示を切り替えます。以下の二点だけを書き換えると、プロトコルをサポートしていないと警告でるので、適宜変更して下さい。また、scrollViewControllerは複数メソッドから利用するので、AppDelegateのプロパティに移動しましょう。よくわからない場合はプロジェクトをダウンロードしてご確認下さい。

    //以下の行を起動メソッドに追加
        scrollView.delegate = self;

    //ApplicationDelegate.mに以下のメソッドを追加
    -(void)scrollViewDidScroll:(UIScrollView *)_scrollView{
        scrollViewController.title = [NSString stringWithFormat:@"%d", (int)round(_scrollView.contentOffset.x / 320.0f) + 1];
    }

    スクリーンショット

    プロジェクトをダウンロード: HorizontalScroll

    tintColor


    iOS5から、UIViewのサブクラスの幾つかでtintColor属性が使えるようになった。UINavigationBarや、UIToolBarで使えるようになっているのは間違いないが、それ以外のどのクラスで使えるのか調査してみた。

    1. ヘッダーを調査

    公式ドキュメントでtintColorという文字列を検索する方法がわからなかったので、headerファイルを調査した。tintColorが含まれているproperty行は以下の7つあった。おそらくこの7つのクラス(とそのサブクラス)で使えるのだろう。7つものクラスで独立して定義されているのは違和感を感じるが(プロトコルとかを使うべき?)、得られたクラスのリストは妥当そうだ。

    macbookpro:Headers mac$ pwd
    /Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS5.0.sdk/System/Library/Frameworks/UIKit.framework/Headers
    macbookpro:Headers mac$ grep tintColor * | grep @property
    UIBarButtonItem.h:@property(nonatomic,retain) UIColor *tintColor __OSX_AVAILABLE_STARTING(__MAC_NA,__IPHONE_5_0) UI_APPEARANCE_SELECTOR;
    UIButton.h:@property(nonatomic,retain)   UIColor     *tintColor __OSX_AVAILABLE_STARTING(__MAC_NA,__IPHONE_5_0); // default is nil. only valid for some button types
    UINavigationBar.h:@property(nonatomic,retain) UIColor *tintColor UI_APPEARANCE_SELECTOR;
    UISearchBar.h:@property(nonatomic,retain) UIColor                *tintColor;             // default is nil
    UISegmentedControl.h:@property(nonatomic,retain) UIColor *tintColor UI_APPEARANCE_SELECTOR;
    UITabBar.h:@property(nonatomic,retain) UIColor *tintColor __OSX_AVAILABLE_STARTING(__MAC_NA,__IPHONE_5_0) UI_APPEARANCE_SELECTOR;
    UIToolbar.h:@property(nonatomic,retain) UIColor   *tintColor  UI_APPEARANCE_SELECTOR;

    この結果から、tintColor属性を持つクラスは以下の7つだとわかる。

    • UIBarButtonItem
    • UIButton
    • UINavigationBar
    • UISearchBar
    • UISegmentedControl
    • UITabBar
    • UIToolbar

    2. 使ってみた

    想定通りの動作をしている。

    • UINavigationBar + UIBarButtonItem
      UIViewController * vc = [[UIViewController alloc] init];
      vc.title = @"Title";
      UINavigationController * nc = [[UINavigationController alloc] initWithRootViewController:vc];
      nc.navigationBar.tintColor = [UIColor redColor];
      UIBarButtonItem * bbi1 = [[UIBarButtonItem alloc] initWithTitle:@"info" style:UIBarButtonItemStylePlain target:nil action:nil];
      UIBarButtonItem * bbi2 = [[UIBarButtonItem alloc] initWithTitle:@"info" style:UIBarButtonItemStylePlain target:nil action:nil];
      bbi2.tintColor = [UIColor grayColor];
      vc.navigationItem.rightBarButtonItems = [NSArray arrayWithObjects:bbi1, bbi2, nil];

    • UIToolbar + UIBarButtonItem
      UIViewController * vc = [[UIViewController alloc] init];
      UIToolbar * tb = [[UIToolbar alloc] initWithFrame:CGRectMake(0, 0, 320, 50)];
      [vc.view addSubview:tb];
      tb.tintColor = [UIColor orangeColor];
         
      UIBarButtonItem * bbColored = [[UIBarButtonItem alloc] initWithTitle:@"UIBarButtonItem" style:UIBarButtonItemStyleBordered target:nil action:nil];
      bbColored.tintColor = [UIColor grayColor];
         
      UIBarButtonItem * bbNotColored = [[UIBarButtonItem alloc] initWithTitle:@"UIBarButtonItem" style:UIBarButtonItemStyleBordered target:nil action:nil];
      tb.items = [NSArray arrayWithObjects:bbColored, bbNotColored, nil];

    まとめ

    iOS5から導入されたtintColorを使うことで、手軽に標準コンポーネントの表示色を変更できることを確認できた。

    UIViewの階層構造を解析


    これはちょっとグレーな話題です。UIViewのsubViewメソッドを実行すると、そのView内に含まれているViewの一覧を取得できます。再帰的に実行すれば、全てのViewの構造がわかります。これを使うとフレームワークに含まれている特殊なViewの中もみごとに解析できちゃいます。

    SubViewをすべて表示するためのメソッド

    2番目のメソッドを呼び出すと、1番目のメソッドが実行され、階層構造がコンソールに表示されます。なお、20階層以上を超える場合はエラーが発生するのでご注意下さい。

    -(void)dumpSubViews: (UIView*) view depth: (int) depth{
        NSString * indent = [@"--------------------" substringToIndex:depth];
        NSLog(@"%@%@", indent, view);
        for(UIView * subView in [view subviews]){
            [self dumpSubViews:subView depth:depth + 1];
        }
    }
    -(void)dumpSubViews: (UIView*) view{
        [self dumpSubViews:view depth:0];
    }

    テスト用のコード

    例によってEmpty Applicationをベースに作成しました。

    - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
    {
        self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
        self.window.backgroundColor = [UIColor whiteColor];

        UIViewController * viewCtr = [[UIViewController alloc] init];
        UINavigationController * navCtr = [[UINavigationController alloc] initWithRootViewController:viewCtr];
        self.window.rootViewController = navCtr;
        viewCtr.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"info" style:UIBarButtonItemStylePlain target:nil action:nil];
       
        navCtr.navigationBar.tintColor = [UIColor redColor];   
       
        [self dumpSubViews:navCtr.navigationBar];
       
        [self.window makeKeyAndVisible];
        return YES;
    }

    実行結果

    View内部の構造が丸裸になりました。この情報はドキュメントに記載されていないものなので、将来的に、警告無く変更される可能性があるのでご注意下さい。この手法では、ドキュメントに記載されているAPIしか使っていませんが、やってることはリバースエンジニアリングの類なので、アプリの承認に影響を及ぼす可能性があります。

    2011-11-18 00:04:47.531 UIViewHack[9920:f803] <UINavigationBar: 0x6dac460; frame = (0 20; 320 44); autoresize = W; layer = <CALayer: 0x6dcddc0>>
    2011-11-18 00:04:47.532 UIViewHack[9920:f803] -<UINavigationBarBackground: 0x6da4970; frame = (0 0; 320 44); opaque = NO; userInteractionEnabled = NO; layer = <CALayer: 0x6da51d0>> - (null)
    2011-11-18 00:04:47.533 UIViewHack[9920:f803] -<UINavigationItemView: 0x6dafcb0; frame = (160 21; 0 0); opaque = NO; userInteractionEnabled = NO; layer = <CALayer: 0x6dad340>>
    2011-11-18 00:04:47.533 UIViewHack[9920:f803] -<UINavigationButton: 0x6dbe410; frame = (267 7; 48 30); opaque = NO; layer = <CALayer: 0x6dbbb30>>
    2011-11-18 00:04:47.534 UIViewHack[9920:f803] --<UIButtonLabel: 0x6dbf730; frame = (0 0; 0 0); clipsToBounds = YES; opaque = NO; userInteractionEnabled = NO; layer = <CALayer: 0x6dc0990>>

    UINavigationBarの色を変える方法


    UINavigationBarは、backgroundの値を変えても色を変えることができません。色を変えたい場合はtintColorの値を変更して下さい。この手法はiOS5.0以降で利用できます。

    navCtr.navigationBar.tintColor = [UIColor redColor];

    Empty Applicationから作る場合はこんな感じになります。

    - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
    {
        self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
        self.window.backgroundColor = [UIColor whiteColor];

        UIViewController * viewCtr = [[UIViewController alloc] init];
        UINavigationController * navCtr = [[UINavigationController alloc] initWithRootViewController:viewCtr];
        self.window.rootViewController = navCtr;
        viewCtr.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"info" style:UIBarButtonItemStylePlain target:nil action:nil];
       
        navCtr.navigationBar.tintColor = [UIColor redColor];   
       
        [self.window makeKeyAndVisible];
        return YES;
    }

    スクリーンショット

    ボタンの色まで違和感なく変更できています。