subview.frame = view.bounds よりも良い書き方

記事の内容はこちらを参照してください。
https://mishimay.github.io/e9ecf3f352aad4433c24/
<!--




## TL;DR
```swift
subview.frame = CGRect(origin: .zero, size: view.bounds.size)
```
と書く方が良い。 


## 本文

あるUIViewに別のUIViewを同じサイズにして乗せたいケースはよくあると思います。
その際にframeの指定をどのように行うのかの選択肢があり、現場のコードでもいくつかの種類を見かけます。
`~.frame = view.frame` だったり `~.frame = view.bounds` だったり...

`view.bounds` を用いることが多いと思いますし自分もうそう書いていたのですが、改めてどのような指定方法が良いのか考えみようと思います。


## sizeの指定にはframeではなくboundsを使う

frameは親viewの座標系における自分の場所を示すものであるのに対して、boundsは自分自身の座標系が基準となっています。

例えば以下のように子viewをtransformで回転させると、frameとboundsのsizeは違う値になります。

```swift
let view = UIView(frame: CGRect(x: 0, y: 0, width: 100, height: 100))

let redView = UIView(frame: CGRect(x: 0, y: 0, width: 100, height: 100))
redView.backgroundColor = .red
view.addSubview(redView)

let blueView = UIView(frame: CGRect(x: 20, y: 20, width: 50, height: 50))
blueView.backgroundColor = .blue
redView.addSubview(blueView)

blueView.transform = CGAffineTransform(rotationAngle: 1)
blueView.frame // (x: 10.456, y: 10.456, width: 69.089, height: 69.089)
blueView.bounds // (x: 0, y: 0, width: 50, height: 50)
```

<img width="100" alt="Screen Shot 2019-11-24 at 17.30.24 2.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/23679/9802d4b4-fe80-3338-e79e-457f74473b29.png">


これは、傾いた四角形を表示するのに四角形自身のサイズ (width: 100, height: 100) よりも大きな領域が必要となったためです。
よって親viewからどう見えるかを示すframe.sizeよりも自分自身の大きさを示すbounds.sizeの値を使うのが良いでしょう。


## originの指定は (x: 0, y: 0) にする

bounds.originは (x: 0, y: 0) 以外も指定できます。
(x: 0, y: 0) 以外を指定するとsubviewの位置が変化します。

例えば以下のようなviewを用意します。

```swift
let view = UIView(frame: CGRect(x: 0, y: 0, width: 100, height: 100))

let redView = UIView(frame: CGRect(x: 0, y: 0, width: 100, height: 100))
redView.backgroundColor = .red
view.addSubview(redView)

let blueView = UIView(frame: CGRect(x: 0, y: 0, width: 50, height: 50))
blueView.backgroundColor = .blue
redView.addSubview(blueView)
```

このときの表示は以下になります。

<img width="100" alt="Screen Shot 2019-11-24 at 12.05.09 2.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/23679/66efc2f4-9eb6-e564-1c35-826ee5b1271f.png">


ここでredViewのboundsのoriginを以下のように (-10, -10) に変更すると、表示はこのようになります。

```swift
redView.bounds = CGRect(x: -20, y: -20, width: 100, height: 100)
```

<img width="100" alt="Screen Shot 2019-12-07 at 12.08.52.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/23679/078c1a1a-077e-927a-46a1-a6f97110e2a9.png">


これは **bounds.origin が (x: 0, y: 0) になる場所がsubviewの描画の起点になるため** です。

つまり、ほとんどないとは思いますがbounds.originの値が (x: 0, y: 0) 以外に設定されて描画を調整しているという場合もあり得るということです。
なのでbounds.originの値が常に (x: 0, y: 0) になると想定せず、直接 (x: 0, y: 0) を指定した方が無難でしょう。



### bounds.sizeを変化させてみると

少し蛇足ですがbounds.sizeの値を変えたときにどうなるか見てみます。
上記のコードでboundsのsizeを (width: 50, height: 50) にしてみましょう。

```swift
redView.bounds = CGRect(x: -20, y: -20, width: 50, height: 50)
```

<img width="100" alt="Screen Shot 2019-12-07 at 12.10.57.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/23679/019b7721-923f-e6bd-53d4-a5e65379a90d.png">


redViewのframeは変化させていないにも関わらず描画サイズが変化しました。
frameのsizeではなくboundsのsizeの方が実際の描画サイズを表すという結果になりました。


# 結論
sizeの指定には親viewのbounds.size、originの指定には (x: 0, y: 0) を指定するのが良いことが分かりました。
CGPointで (x: 0, y: 0) を指定するときは `.zero` と書けますのでSwiftのコードとしては以下のような書き方になります。

```swift
subview.frame = CGRect(origin: .zero, size: view.bounds.size)
```


## 参考
- https://stackoverflow.com/questions/8452730/when-would-a-uiviews-bounds-origin-not-be-0-0 
- https://ashfurrow.com/blog/you-probably-dont-understand-frames-and-bounds/


## 環境
Xcode 11.2.1
Swift 5.1






-->