UIAppearanceプロトコル
iOS5 から UIAppearance プロトコルが導入された。このプロトコルを使うと UINavigationBar や UISwitch のインスタンスの色を変更することができる。変更の仕方はこんな感じ。
[[UISwitch appearance] setOnTintColor:[UIColor greenColor]];UIAppearance が有用なのは、上記の通りインスタンスではなくクラスに対して色設定ができること。つまりこれを使うと画面上で使われているすべての UISwitch の色を変えることができる。
例えば緑基調の画面があったとして、UIAppearance プロトコルを使うと別の色(例では赤)に変更することができる。
↓
↓
↓
上記の例は1画面だけだが、アプリで使われるすべての画面で色が変わる。上記例の色変更の箇所のコードはこんな感じ。
UIColor* color = nil; if (index == 0) { color = [UIColor colorWithRed:0 green:0.5 blue:0 alpha:1.0]; } else { color = [UIColor colorWithRed:0.5 green:0 blue:0 alpha:1.0]; } [[UINavigationBar appearance] setTintColor:color]; [[UISegmentedControl appearance] setTintColor:color]; [[UIProgressView appearance] setProgressTintColor:color]; [[UISlider appearance] setMinimumTrackTintColor:color]; [[UISwitch appearance] setOnTintColor:color]; [[UIActivityIndicatorView appearance] setColor:color]; [[UITabBar appearance] setTintColor:color];
UIAppearanceを使うとコントロール自体の基本色を実行中に切り替える事ができる。
所属ビューの限定
そのビューが所属している親ビューを条件に指定することができる。例えば UINavigationBar に配置されている UIBarButtonIem だけ色を変えたい場合は appearanceWhenContainedIn: を使ってこう書く。
[[UIBarButtonItem appearanceWhenContainedIn: [UINavigationBar class], nil] setTintColor:color];親クラスは複数指定することができる。
appearance と appearanceWhenContainedIn: を比べた例。
[[UIBarButtonItem appearance] setTintColor:color];この場合、UINaigationBar と UIToolBar 上の UIBarButtonItem の色が共に変更されている。
これを UINavigatorBar 限定にしてやるとこうなる。
[[UIBarButtonItem appearanceWhenContainedIn: [UINavigationBar class], nil] setTintColor:color];
サブビューへの適用
UINavigationBar や UIToolBar の色を変えるとそのサブビューである UIButtonBarItem の色も変更される。
[[UINavigationBar appearance] setTintColor:color];
ボタンを別の色に変えたい場合は明示的に指定してやる。
[[UINavigationBar appearance] setTintColor:color]; [[UIBarButtonItem appearance] setTintColor:blueColor];
UILabel
UILablel の文字色を変えると UITextField や UIButton の文字色も変わる。
これは UIButton 等が文字表示の為に UILabel をサブビューとして持っている為(例えば UIButton の場合、UIButtonLabel::UILabel を使っている)。UIButton の他にも UITextField が UISearchBar で使われていたりと他のコントロールを内包しているケースが多々ある。この為、UILabel や UItextField など基本的なパーツを使う場合は注意が必要。
ビュー階層内の優先順位
サブビューの設定が優先される。例えば UINavigationBar と UIBarButtonItem の両方で tintColorを設定した場合、UIBarButtonItem はそれ自身に設定した色のが優先される。
[[UINavigationBar appearance] setTintColor:color]; [[UIBarButtonItem appearance] setTintColor:blueColor];呼び出し順は関係ないので逆にしてもこれは変わらない。
[[UIBarButtonItem appearance] setTintColor:blueColor]; [[UINavigationBar appearance] setTintColor:color];
画像の切替
UIAppearance による切替は色に限らず他の属性にも適用できる。例えば背景画像の切替をやって見る。
if (index == 0) { backImage = [UIImage imageNamed:@"back1.png"]; } else { backImage = [UIImage imageNamed:@"back2.png"]; } [[UIButton appearance] setBackgroundImage:backImage forState:UIControlStateNormal];
ボタンを画像で用意している場合や、ビューにテクスチャを貼っている場合など、あらかじめ複数種類の画像セットを用意しておけば簡単に切替ができる。
UIView
UIAppearanceプロトコル適用の宣言は UIView で行われている。
UIKIT_CLASS_AVAILABLE(2_0) @interface UIView : UIResponder <NSCoding, UIAppearance, UIAppearanceContainer> { :この為、UIView の appearance に対して設定を行うと画面に表示されるすべてのビューに対して変更が適用される。例えば
[[Uiview apperance] setbackgroundcolor:[UIColor whitecolor]];とすると、こうなる。
実際のデザインよりはデバッグ目的での利用が多そう。
備考
色を設定した後の再描画方法がわからなかった。-[UIView setNeedsDisplay] を performSelector:@selector(setNeedsDisplay)object:delay: を使ってみたがうまくいかなかった。この理由によりサンプルでは下のタブバーの色だけが切り替わらない。※もしご存知の方がいたら是非教えて下さい。
サンプル
検証に使ったサンプルソースはこちら。
UIAppearanceSample at 2011-10-31 from xcatsan/iOS5-Sample - GitHub
- - - -
スキンの切替が容易に実現できるので今後重宝しそう。
ClimbAppDev says:
2011年10月31日 13:34
かなり強引ですが、tabBarController.viewをremoveFromSuperviewして再度addSubview:すればタブも含め全てのUIがその瞬間に色が切り替わりました。
Appearance切り替え後に
UIView *view = self.tabBarController.view;
UIView *superview = view.superview;
[view removeFromSuperview];
[superview addSubview:view];
です。
かと言って、viewに対してdidMoveToWindowやdidMoveToSuperviewを呼んでも再描画されるわけではないようです。
強引なので正攻法は別途模索する必要がありそうです。
ClimbAppDev says:
2011年10月31日 13:34
かなり強引ですが、tabBarController.viewをremoveFromSuperviewして再度addSubview:すればタブも含め全てのUIがその瞬間に色が切り替わりました。
Appearance切り替え後に
UIView *view = self.tabBarController.view;
UIView *superview = view.superview;
[view removeFromSuperview];
[superview addSubview:view];
です。
かと言って、viewに対してdidMoveToWindowやdidMoveToSuperviewを呼んでも再描画されるわけではないようです。
強引なので正攻法は別途模索する必要がありそうです。
xcatsan says:
2011年10月31日 13:38
ClimbAppDev さん、こんにちは。
その手がありましたかー。
今のところはこれしか方法はなさそうですね。
情報をどうも。
xcatsan says:
2011年10月31日 13:38
ClimbAppDev さん、こんにちは。
その手がありましたかー。
今のところはこれしか方法はなさそうですね。
情報をどうも。