iOS7ステータスバー対策まとめ


iOS7ではスタースバーは透明、ナビゲーションバー、タブバー、ツールバー、検索バー、スコープバーは半透明。一般的なルールとしてコンテンツの上にこれらのバーを被せることを想定しスタースバーの下に何もバーがないならコンテンツはフルスクリーンコンテンツにすべき。とある。

## iOS7 フレームワーク判定

* [iOS 7 UI Transition Guide](https://developer.apple.com/library/prerelease/ios/documentation/UserExperience/Conceptual/TransitionGuide/index.html)にはこんなやり方が書いてある。場合によってはバージョンよりもNSFoundationVersionNumberを使うほうが意図が明確になる。

```objective-c
if (floor(NSFoundationVersionNumber) <= NSFoundationVersionNumber_iOS_6_1) {
    // Load resources for iOS 6.1 or earlier
} else {
    // Load resources for iOS 7 or later
}
```

## wantsFullScreenLayoutの廃止

iOS7ではフルスクリーンコンテンツを想定しているのでiOS6以下であった ``wantsFullScreenLayout`` は非推奨になる。

## UIViewControllerBasedStatusBarAppearance

iOS7で動的にスタイルを変更する場合はアプリのInfo.plistファイルで **UIViewControllerBasedStatusBarAppearance** を **YES** に設定しておく。Info.plistをソースから編集する場合は以下の二行を追加すればいい。

```xml:Info.plist
<key>UIViewControllerBasedStatusBarAppearance</key>
<true/>
```

Properrty List Editorから編集する場合は **View controller-based status bar appearance** を選択し **YES** を設定する。

## ステータスバーを消す

アプリにステータスバーが要らない場合は、XcodeのTargetのGeneraのHide during application launchにチェックを入れる。これだけだと反映されないのでinfoファイルのUIViewControllerBasedStatusBarAppearanceを **NO** にしておく。

## ステータスバーのスタイルを設定する

アプリのInfo.plistファイルで **UIStatusBarStyle** によって設定できる。

```xml:Info.plist
<key>UIStatusBarStyle</key>
<string>UIStatusBarStyleLightContent</string>
```

コードで変更もできるがポイントになるのはiOS7での起動中の話。iPadの起動画面の画像サイズが変わったように起動イメージがステータスバーの領域を含めるので、起動イメージからスタイルを変えたい場合はInfoファイルで制御する。

iOS7で設定可能な値は以下となる。

```objective-c
typedef enum : NSInteger {
   UIStatusBarStyleDefault,
   UIStatusBarStyleLightContent,
   UIStatusBarStyleBlackTranslucent,
   UIStatusBarStyleBlackOpaque
} UIStatusBarStyle;
```

時刻などステータスバー内のコンテンツの色を白にする場合は ``UIStatusBarStyleLightContent`` をつかうと良い。

参考: http://stackoverflow.com/questions/18924345/how-to-change-status-bar-style-during-launch-on-ios-7

## 動的にステータスバーのスタイルを変更する

iOS7で動的にスタイルを変更する場合はUIViewControllerBasedStatusBarAppearance を **YES**  にしておく。

デフォルトはApplicationのstatusBarStyleで変更できる。

```objective-c
[UIApplication sharedApplication].statusBarStyle = UIStatusBarStyleLightContent;
```

ViewController単位での変更も可能で動的にスタイルを変える場合は
``(UIStatusBarStyle)preferredStatusBarStyle``を実装して変更したいUIStatusBarStyleを返すようにする、任意のタイミングで変更する場合は``setNeedsStatusBarAppearanceUpdate`` を呼び出すことで更新ができる。

```objective-c
{   
    UIStatusBarStyle _statusBarStyle;
}

- (IBAction)toggleStatusBar:(id)sender
{
    if (_statusBarStyle == UIStatusBarStyleDefault) {
        _statusBarStyle = UIStatusBarStyleLightContent;
    } else {
        _statusBarStyle = UIStatusBarStyleDefault;
    }
    if ([self respondsToSelector:@selector(setNeedsStatusBarAppearanceUpdate)]) {
        [self setNeedsStatusBarAppearanceUpdate];
    }
}

- (UIStatusBarStyle)preferredStatusBarStyle
{
    return _statusBarStyle;
}
```


## 動的にステータスバーを非表示にする

iOS7からはViewControllerで

```objective-c
- (BOOL)prefersStatusBarHidden
{
    return YES;
}
```

を入れるとViewController単位でスターテスバーが消せる。アニメーションは``preferredStatusBarUpdateAnimation``で指定。iOS6も対応させるなら以下のようになる。

```objective-c
- (void)viewDidLoad
{
    [super viewDidLoad];
    
    if (floor(NSFoundationVersionNumber) <= NSFoundationVersionNumber_iOS_6_1) {
        [[UIApplication sharedApplication] setStatusBarHidden:YES withAnimation:UIStatusBarAnimationFade];
    }
}

- (BOOL)prefersStatusBarHidden
{
    return YES;
}

- (UIStatusBarAnimation)preferredStatusBarUpdateAnimation
{
    return UIStatusBarAnimationFade;
}
```

iOS7の ``prefersStatusBarHidden`` による対応はそのUIViewControllerのみに限定されるが、iOS6向けのUIApplicationに対する変更はアプリケーション全体に適応されるので画面ごとに切り替える場合は元に戻す処理も必要になるところが注意点。

またiOS7ではUINavigationControllerなどがStatusBarの有無によるマージンをうまく取り扱ってくれることがあるがいくつかのパターンではStatusBarを消したあとのレイアウトも変更する必要がある。

参考:  http://stackoverflow.com/questions/18375898/status-bar-appear-over-my-views-bounds-in-ios-7

## UINavigationController, UITabBarController 経由の場合

iOS7でUINavigationControllerやUITabBarControllerをつかう場合は内部のViewControllerの ``preferredStatusBarStyle `` や``prefersStatusBarHidden`` が呼び出されない。

自身で設定するか
``childViewControllerForStatusBarStyle`` や``childViewControllerForStatusBarHidden`` をオーバーライドして処理を委譲するChildViewControllerを指定できる。

```objective-c
- (UIViewController *)childViewControllerForStatusBarStyle;
- (UIViewController *)childViewControllerForStatusBarHidden; 
```

## AutoLayoutでStatsuBarに対する余白を変更する

iOS7向けにステータスバーに重ならないようにViewを配置していると、iOS6ではそれが余計な余白になる。例えばiOS7でステータスバーの真下にラベルを配置すると

![ios7_2013-09-24_1204.png](https://qiita-image-store.s3.amazonaws.com/0/2435/1921acbf-0751-2cc7-d73e-6a08eac0f1d3.png "ios7_2013-09-24_1204.png")

iOS6ではこのようにiOS7向けでStatusBarに対して設定した余白が余分になってしまう。

![ios6_2013-09-24_1206.png](https://qiita-image-store.s3.amazonaws.com/0/2435/8f097335-50bd-9932-5884-2df63ec2b003.png "ios6_2013-09-24_1206.png")


iOS6、あるいはiOS7のときにViewの位置を変えるという試練が待ち受けている。iOS6以上ならAutoLayoutを使った方が簡単になる。

簡単な解決の方法は **topLayoutGuide** を使う方法。topのマージンをtopLayoutGuideから0とすると、iOS7ではステータスバーがある場合はステータスバーのbottomからの距離となり、iOS6ではステータスバーは画面領域に含めないので画面のtopからの距離になる。


![toplayoutguide_2013-09-24_1212.png](https://qiita-image-store.s3.amazonaws.com/0/2435/783ef688-2e1e-8f03-8bff-38dde9247e9e.png "toplayoutguide_2013-09-24_1212.png")

もう一つの方法はAutoLayoutの値を自分で調整する方法。例えばまずiOS7向けにViewのtopのマージンを20と設定する。

![topview_2013-09-24_1219.png](https://qiita-image-store.s3.amazonaws.com/0/2435/675aa342-e966-5976-39ba-c31277c9aa9d.png "topview_2013-09-24_1219.png")

AutoLayoutのConstraintはIBOutletでコードから操作することができる。

![iboutlet_2013-09-24_1227.png](https://qiita-image-store.s3.amazonaws.com/0/2435/0996b4e3-b7e5-172b-db54-c0bcc9432441.png "iboutlet_2013-09-24_1227.png")

これを利用してiOS6のときにtopのマージンを20->0にするという実装が可能になる。

```objective-c
{
    IBOutlet NSLayoutConstraint *_headerMargin;
}

- (void)viewDidLoad
{
    [super viewDidLoad];
    
    if (floor(NSFoundationVersionNumber) <= NSFoundationVersionNumber_iOS_6_1) {
        _headerMargin.constant = 0;
    }
}
```

## iOS5でStatsuBarに対する余白を変更する

iOS5をターゲットにしているとAutoLayoutが使えないのでtopLayoutGuideを利用できない。

代わりに ``iOS 6/7 Deltas`` をつかうことで解決する。これはiOS6(以下)向けに座標やサイズを調整する機能でY座標を-20にすればiOS7向けに設定したステータスバー分の余白を相殺することができる。(View as ``iOS 7.0 and Later`` で表示している場合、iOS 6.1 and EarilerでみるとDeltaが逆になる)

![autoresize_2013-09-30_1652.png](https://qiita-image-store.s3.amazonaws.com/0/2435/52050b27-9a05-4f09-4e5f-d1c90d2d079e.png "autoresize_2013-09-30_1652.png")

この例ではUILabelを直接設定しているが、他のコントロールも調整が必要になるのでレイアウト用にコンテナのUIViewを追加してそこで iOS 6/7 Deltas を設定し、それ以外はそのコンテナUIViewのsubViewとしてレイアウトするのが良いと思われる。レイアウトに使うUIVewiはY座標を-20をした場合にはHeightを+20しないと画面下部に余白ができてしまうことに注意する。

参考: [iOS 6/7 で UILabel などの見た目がずれるよ〜とお嘆きのあなたへ](http://qiita.com/ne_ko_/items/5da4b2a247e65431c7cf)


## StatusBarをUINavigationBarにかぶせる

iOS7でUINavigationControllerをつかう場合、UINavigationBarの上にStatusBarがかぶさるようになっている。

UINavigationControllerを使わずにUIコントロールとして直接UINavigationBarを直接つかう場合、単にStatusBar分の余白を空ける方法だと以下のようにUINavigationBarの色が画面上部まで適応されないのでiOS7らしくならない。

![navbar_2013-09-30_1618.png](https://qiita-image-store.s3.amazonaws.com/0/2435/3846f238-e3ae-fa6d-efa9-104a5eca0acb.png "navbar_2013-09-30_1618.png")


これを解決するために ``UIBarPositioningDelegate``の``(UIBarPosition)positionForBar:(id<UIBarPositioning>)bar`` で **UIBarPositionTopAttached** を返す。

```objective-c
{
    IBOutlet UINavigationBar *_navBar;
}

- (void)viewDidLoad
{
    [super viewDidLoad];
    
    _navBar.delegate = self;    
    if (floor(NSFoundationVersionNumber) > NSFoundationVersionNumber_iOS_6_1) {
        _navBar.barTintColor = [UIColor yellowColor];
    }
}

- (UIBarPosition)positionForBar:(id <UIBarPositioning>)bar
{
    return UIBarPositionTopAttached;
}
```

これによりbarTintColorがStatusBarの下にもちゃんと設定されるようになる。

![navbar_2013-09-30_1631.png](https://qiita-image-store.s3.amazonaws.com/0/2435/1426f611-77f1-8516-896a-180bbabf18ed.png "navbar_2013-09-30_1631.png")


これは前節のtopLayoutGuideと合わせてつかった場合。なお、UIBarPositioningDelegateやbarTintColorなどはiOS 7以降の機能となっている。

参考: https://devforums.apple.com/message/853889


## その他

AutoLayoutやiOS 6/7 DeltasなどInterface Builderに頼らない場合は以下にあるように直接ViewのFrameを設定するなどの調整が必要になる。

* http://stackoverflow.com/questions/18294872/ios-7-status-bar-back-to-ios-6-style/