やはりお前らのiOS7対応は間違っている(解説編)


クックパッドさんが月イチで行っている第3回potatotips（ポテトチップス）というTips共有会がYahoo!Japanさんで開かれたので参加させてもらい、『やはりお前らのiOS7対応は間違っている』という刺激的&挑戦的なタイトルで発表させてもらいました。

発表時には時間もなかったせいかツッコミがなかったのですが、おそらく公開したらツッコミがあると思うのでいくつかの補足を付けて公開します（[発表時の資料はSlideShareに上げてます](http://www.slideshare.net/YoshinoriImajo/ios7-30039408)が、Qiitaのほうが編集リクエストもあるし直しやすいかと思います）。

# 何を間違っているか

最近、[iOS7でUINavigationBarにself.viewが潜り込む](http://techblog.yahoo.co.jp/ios/ios7yahoo/)という話をよく目にすると思います。この対応方法として「edgesForExtededLayoutプロパティをUIRectEdgeNoneとする（StoryboardではUnder Top Barsのチェックを外す）ことで潜りこみを回避できますよ」とあり、デメリットとして「これのデメリットはすりガラス表現が失われることです」とあります。

コードでは次のように書かれていたりしますね

```objc
self.edgesForExtendedLayout = UIRectEdgeNone; //良い方法じゃないぜ？
```


これは長くプロジェクトを続けていく上では決して良い方法ではないし、デメリットも微妙に表現が間違っています。

この表現を多くのブログが引用していることから、おそらくこれらの設定の理解が出来てないのではないかと感じたわけです。

また、Storyboardを使えばコード上でedgesForExtededLayoutを使う必要はなく、Extend Edges項目のUnder Top Barsのチェックになるので以降の説明にはこれを使います。

![スクリーンショット 2014-01-16 1.14.39.png](https://qiita-image-store.s3.amazonaws.com/0/16400/e0bbfe15-f85b-2989-6599-28cff35a06f6.png "スクリーンショット 2014-01-16 1.14.39.png")


ほとんどの場合このチェックはすべきです。チェックしてUINavigationBarにself.viewが潜り込むのなら、それは他の方法でずれを直すべきですよといのうがこの文章の結論です。

## そもそもすりガラス表現は失われてなかったんだぜ

Under Top Barsのチェックを外した場合、背景にはUIWindowしかないことから真っ黒な背景に対してすりガラス表現が適用されているだけです。

ビューを立体的に見てみると分かりやすいでしょう。

![Under Top Bars をオフにした](https://qiita-image-store.s3.amazonaws.com/0/16400/53e2e749-c62a-2d22-19dd-aa9a82ecef6a.jpeg "やはりお前らのiOS7対応は間違っている.019.jpg")

RootViewControllerと書かれたUINavigationBarの下にピンクのビューが無いため、すりガラス効果が黒の部分に対して適用されています。

逆にUnder Top Barsのチェックをオンにした場合は次のようになりピンクの背景色に対してすりガラス表現が適用されてます。

![すりガラス表現](https://qiita-image-store.s3.amazonaws.com/0/16400/80e8a03f-0bf0-43ae-fab7-308d5b7083f1.jpeg "やはりお前らのiOS7対応は間違っているp11.018.jpg")

## 新しいXcodeに移行した途端、ずれているからといってViewの開始をUINavigationBarの下からにするのは良いやり方じゃない

ずれているのはiOS7から考え方が全画面表示になったからで、それに伴ってView上のコンポーネントを調整しましょう。はい、調整するとiOS6がずれますね、そしたらiOS6/7 Deltaで座標値を変えてiOS6用に調整すればよいです。

# パターン別のしっかりとした解決方法

解決方法の分類のため3パターン考えてみました。

- UITableViewControllerを使う場合
- UIViewControllerを使う場合
- UIScrollViewやUICollectionViewで全画面写真表示したい場合

表で設定をまとめるとわかりやすくなります。

||Adjust Scroll View Insets|Under Top Bars|iOS6対応のため|
|:-----------|:-----------:|:-----------:|:------------|
|UITableViewController|チェックする|チェックする||
|UIViewController|チェックする|チェックする|まずiOS7用にコンポーネントを配置し直し、その上でiOS6/7 Deltasで座標値調整|
|UIScrollViewなどで全画面写真表示|チェック外す|チェックする|Use Full Screenをチェック|

基本的にUnder Top Barsは全てチェックをしたままが良いでしょう。
UINavigationBarがない場合はチェックを外してもいいかもしれませんが、なければこのチェックが意味を成さないだけです。

自分の経験上、あえてUnder Top Barsのチェックを外さなきゃならないパターンが見当たらないので他にあれば教えてください。

## Adjust Scroll View Insets

イレギュラーなのが先述の3パターン目だけです。下の右の図のようにAdjust Scroll View InsetsとUnder Top Barsをチェックしてしまうとその設定によって、View自体はちゃんとウインドウの左上から始まるのですがcontentInsets.topが調整されてしまいナビゲーションバーの下からscrollViewがはじまります。全画面表示っぽくないですが、これはUINavigationBarがあってScrollViewがあるパターンでは良さそうです。

![全画面表示.jpg](https://qiita-image-store.s3.amazonaws.com/0/16400/728c446d-ec3d-1a24-a992-ca068b58e799.jpeg "全画面表示.jpg")


# その他

## Storyboard使えるならStoryboardで設定するのがオススメです

Storyboard(Interface Buildr)の設定はわりとよく考えられていて、iOS7だけにしか効果が無い設定やiOS6だけにしか効果が無い設定があるので、コード上で分岐させる必要もなくなる上に、将来的にiOS8などが出てiOS6が非対応になった際にもその設定は変更する必要がなくなるわけです。おそらく表面的にも表示されなくなるでしょう。ソースコードで書いてしまうと精神衛生上それらを消さないってわけにも行かないし、消しても良いコード悪いコードの判別を強いられるの嫌ですよね。

```objc
//ViewControllerのloadViewなどでの処理
if (floor(NSFoundationVersionNumber) > NSFoundationVersionNumber_iOS_6_1) {
    //ここにiOS7用のプロパティへの代入を書かなくてもStoryboardへの設定が反映される
    //例)self.edgesForExtendedLayout = UIRectEdgeAll;
    //Storyboardに設定した値と違う値をセット出来てしまって保守しづらい

    //iOS7以上でStoryboardで設定できない処理があるなら書くしかないが
    //そうすると消しても良いStoryboardで設定できるコードと
    //そうでないコードがViewControllerに混在して判別しづらい
}
```

## potatotipsで発表した資料

SlideShareにアップロードしてます
http://www.slideshare.net/YoshinoriImajo/ios7-30039408

## 設定が反映されるviewについて

Adjust Scroll View InsetsやUnder Top Barsは設定したViewController直下のviewにだけ効果があると思っていましたが、直下のviewに対してaddしたview（view.subviews[0]）についても設定が反映されるようです。