iOS SDK 的 frame & bounds

開發 iOS App 時,我們時常存取 UI 元件的 property frame,不過我們也常看到另一個跟它很像,型別一樣是 CGRect 的 property bounds。

frame 和定位有關,指的是元件相對於 superview 的位置和大小,bounds 則是元件在它自己座標系統的位置大小。剛剛的描述聽起來像火星文,有聽沒有懂。沒關係,接下來就讓我們透過實際的例子觀察它們不一樣的地方吧。

正常情況的 frame & bounds

比方我們在 storyboard 的畫面上加入一個 image view。

接著我們在 function viewDidLoad 裡印出它的 frame & bounds。

@IBOutlet weak var imageView: UIImageView!

override func viewDidLoad() {
super.viewDidLoad()
print("frame", imageView.frame, "bounds", imageView.bounds)
}

結果

frame 的 origin 為 (77, 150),代表它相對於 superview 左上角的距離。但 bounds 的 origin 則為 (0, 0),跟它在畫面上的位置無關。而 frame & bounds 的 size 則是一樣的,等同於 image view 的大小。

frame (77.0, 150.0, 261.0, 317.0) bounds (0.0, 0.0, 261.0, 317.0)

調整 frame 的 size

為了方便看出 size 調整後的變化,我們在 image view 的背後加入一個一樣大小位置的 view。

print("frame", imageView.frame, "bounds", imageView.bounds)
imageView.frame.size = CGSize(width: 200, height: 100)
print("frame", imageView.frame, "bounds", imageView.bounds)

結果

frame (77.0, 150.0, 261.0, 317.0) bounds (0.0, 0.0, 261.0, 317.0)frame (77.0, 150.0, 200.0, 100.0) bounds (0.0, 0.0, 200.0, 100.0)

bounds 的 size 將同步改變,但 frame & bounds 的 origin 都不受影響,因此圖片不會有任何移動。

調整 bounds 的 size

print("frame", imageView.frame, "bounds", imageView.bounds)
imageView.bounds.size = CGSize(width: 130, height: 158)
print("frame", imageView.frame, "bounds", imageView.bounds)

結果

frame (77.0, 150.0, 261.0, 317.0) bounds (0.0, 0.0, 261.0, 317.0)frame (142.5, 229.5, 130.0, 158.0) bounds (0.0, 0.0, 130.0, 158.0)

frame 的 size 將同步改變,而且元件還將維持它的中心點座標,因此它會改變 frame 的 origin。

調整 frame 的 origin

print("frame", imageView.frame, "bounds", imageView.bounds)
imageView.frame.origin = CGPoint(x: 100, y: 100)
print("frame", imageView.frame, "bounds", imageView.bounds)

結果

frame (77.0, 150.0, 261.0, 317.0) bounds (0.0, 0.0, 261.0, 317.0)frame (100.0, 100.0, 261.0, 317.0) bounds (0.0, 0.0, 261.0, 317.0)

此時圖片位置移動了,但 bounds 的 origin 將不受影響,依然是 (0, 0)。

調整 bounds 的 origin

大部分的時候,元件 bounds 的 origin 都會是 (0, 0),很少需要改變。不過為了理解 bounds 的 origin,接下來我們將故意修改 bounds 的 origin。

為了看出 bounds origin 的作用,我們在 image view 上加入一個 redView。

override func viewDidLoad() {
super.viewDidLoad()
let redView = UIView(frame: CGRect(x: 0, y: 0, width: 200, height: 200))
redView.backgroundColor = UIColor.red.withAlphaComponent(0.5)
imageView.addSubview(redView)
}

如下圖所示,由於 redView frame 的 origin 為 (0,0),因此它將對齊 image view 左上的頂點。

但是當我們將 imageView bounds 的 origin 設為 (-50, -50) 時,redView 卻向右下偏移了 !

imageView.bounds.origin = CGPoint(x: -50, y: -50)

這是因為對 imageView 自己的座標系統來說,它的左上角座標變成 (-50, -50),所以當放在它裡面的 redView 的 origin 為 (0, 0) 時,將變成跟圖片的左上角有一段距離。

調整 transform

大部分的時候,frame & bounds 的 size 會是一樣的,因此從 frame & bounds 都能取得元件的尺寸。不過它們還是有可能不一樣,比方以下利用 transform 旋轉 imageView 的例子。

print("frame", imageView.frame, "bounds", imageView.bounds)
imageView.transform = CGAffineTransform(rotationAngle: .pi / 4)
print("frame", imageView.frame, "bounds", imageView.bounds)

結果

frame (77.0, 150.0, 261.0, 317.0) bounds (0.0, 0.0, 261.0, 317.0)frame (3.1461402370877636, 104.14614023708776, 408.7077195258245, 408.7077195258244) bounds (0.0, 0.0, 261.0, 317.0)

當圖片旋轉後,它的 frame 將是包含它的框框,因此下圖的紅色框框代表 frame 的位置大小,但圖片本身的 bounds 並不會有任何改變。

其它參考連結

  • Apple 的 View Programming Guide for iOS
  • qedqed6 研究 UIView 的 frame, bounds, center

--

--

彼得潘的 iOS App Neverland
彼得潘的 Swift iOS App 開發問題解答集

彼得潘的iOS App程式設計入門,文組生的iOS App程式設計入門講師,彼得潘的 Swift 程式設計入門,App程式設計入門作者,http://apppeterpan.strikingly.com