32
19

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

iOS #2Advent Calendar 2019

Day 9

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

Last updated at Posted at 2019-12-08

TL;DR

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は違う値になります。

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)
Screen Shot 2019-11-24 at 17.30.24 2.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を用意します。

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)

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

Screen Shot 2019-11-24 at 12.05.09 2.png

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

redView.bounds = CGRect(x: -20, y: -20, width: 100, height: 100)
Screen Shot 2019-12-07 at 12.08.52.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) にしてみましょう。

redView.bounds = CGRect(x: -20, y: -20, width: 50, height: 50)
Screen Shot 2019-12-07 at 12.10.57.png

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

結論

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

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

参考

環境

Xcode 11.2.1
Swift 5.1

32
19
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
32
19

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?