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
}
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行に
参考
http://stackoverflow.com/questions/2422383/uinavigationbar-multi-line-title
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を使って、横方向にページを並べてみます。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
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の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は、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 ;
}
スクリーンショット
ボタンの色まで違和感なく変更できています。
iPhoneアプリ開発に関する情報を公開中 ソースコードもあるよ。