【Swift】frameとboundsの違いは?UIViewにどう影響するのか

Programming

SwiftのUIViewクラスには、自身の位置や大きさを情報としてもつプロパティが2つあります。
frameプロパティboundsプロパティです。これらは、どちらかが変更されるともう一方も合わせて変更されるようになっています。

frameプロパティとboundsプロパティの違い

frameプロパティはそのUIViewの親UIViewにおけるローカル座標系での、自身の矩形のCGRect表現を示します。
boundsはそのUIView自身におけるローカル座標系での自身の矩形のCGRect表現を示します。

CGRect構造体

補足すると、SwiftのCGRect構造体は


public struct CGRect {

    public var origin: CGPoint

    public var size: CGSize

    public init()

    public init(origin: CGPoint, size: CGSize)
}

からわかるように、CGPoint型のoriginとCGSize型のsizeをプロパティとして持つようになっています。
originで自身の矩形の左上の頂点の位置を指定し、sizeで矩形の大きさを指定します。(左上の頂点から矩形が作られていくので、右下に伸びていきます)

CGPointは、


public struct CGPoint {

    public var x: CGFloat

    public var y: CGFloat

    public init()

    public init(x: CGFloat, y: CGFloat)
}

のようにx,yというCGFloat型のプロパティをもち、この2つでxy平面上の座標を表します。

CGSizeは、


public struct CGSize {

    public var width: CGFloat

    public var height: CGFloat

    public init()

    public init(width: CGFloat, height: CGFloat)
}

のようにwidth,heightというCGFloat型のプロパティをもち、この2つで大きさを表します。

ちなみにCGRectはextensionされていて、origin・sizeを引数とするのではなく、x・y・width・heightを引数としても生成できます。


extension CGRect {

    public init(x: CGFloat, y: CGFloat, width: CGFloat, height: CGFloat)

    public init(x: Double, y: Double, width: Double, height: Double)

    public init(x: Int, y: Int, width: Int, height: Int)
}

frame、boundsの相互変化

上でも述べたように、frameプロパティとboundsプロパティはお互いの変化に合わせて自身の値も変更されます。

frameを変更した時のboundsの変化

当たり前のようですが、frameの変更はboundsのsizeにしか影響を与え得ません
なぜなら、frameのoriginが変更されても、UIViewは自身のローカル座標系ごと移動してしまうので、boundsのoriginは(0,0)のまま変化がないからです。
frameのsizeが変更されると、その変更された大きさに合わせる形で、boundsのsizeも変化します。

boundsを変更した時のframeの変化

これは、origin,size両方に影響を与え得ます。

  1. boundsのsizeのみの変更
    frameのsizeがboundsのsizeに合わせて変化する
  2. boundsのoriginのみの変更
    frameのoriginがboundsのorigin変更によって移動した位置に合わせて変化する
  3. boundsのsize・originを両方変更
    frameのsize・originが両方変化する(矩形の中心から上下均等に変更される

ちなみに、frameの中心座標はcenterプロパティから参照可能です。(これもframeプロパティの変更に応じて変化します)
centerプロパティを指定すると、指定した位置を中心にするように、大きさを保ったまま矩形を平行移動できます。

親UIViewのframe,bounds変更による子UIViewの変化

親子関係を結んだUIViewの場合、親UIViewの変更が子UIViewに変化を与え得ます。
簡単にわかるかと思いますが、ここで親のsizeの変更は特に子に変化を与えません。そのため、ここから親のoriginを変化させるという前提で話を進めます。

親のframeを変更した時の子の変化

親のframeを変化させるということは、そのまた親におけるローカル座標系において自身の矩形を変化させるということです。
つまり、frameのoriginを変化させると、自身のローカル座標系の原点も共に変化し、画面上での位置が変わります。
すると、子のframeは「親UIViewのローカル座標系におけるCGRect表現」だったので、変化した親のローカル座標系に応じて自身の画面上での位置も変化します。
しかし画面上での位置が変化しても、origin.x,origin.yの値は特に変わりません。なぜなら子のframeが基準とする「親のローカル座標系」そのものが変化しているから画面上の位置が変わっただけであって、子の親との画面上での相対位置が変化したわけではないからです。

親のboundsを変更した時の子の変化

親のboundsを変更すると、親UIView自身のローカル座標系における自身の矩形が変化します。
つまり上のframeを変更した場合と違い、自身のローカル座標系自体は変化しません
よって、親の画面上での位置が変化しても、子のframeが基準とする「親のローカル座標系」は変化していないので子の画面上での位置は変わりません。
結果、子の親との画面上での相対位置が変化してしまいます。

参考書籍

親切すぎるiPhoneアプリ開発の本 著:國居貴浩

コメント

タイトルとURLをコピーしました