原文:《iOS Fundamentals: Frames, Bounds, and CGGeometry》
程康,2016 年 3 月 26 日
本文原鏈:【譯】iOS 基礎:Frames、Bounds 和 CGGeometryios
若是你習慣支持點語法的語言,要搞清楚CGPoint
、CGSize
和CGRect
並不難。不過編程式定位視圖或者編寫繪圖代碼通常都很長,所以變得很難讀明白。git
在這個教程裏,我但願能澄清一些對 frames 和 bounds 的誤解,而且介紹一下CGGeometry,它是一個結構體、常量和功能的集合,能讓你更輕鬆地運用CGPoint
,CGSize
和CGRect
。github
若是你剛開始接觸 iOS 或者 OS X 開發,你可能會想CGPoint
、CGSize
和CGRect
究竟是什麼。CGGeometry Reference 定義了一系列幾何圖元(geometric primitives)或者說結構,咱們如今關注的是其中的CGPoint
、CGSize
和CGRect
。編程
大多數人應該知道,CGPoint
是定義了座標系中一個點的 C 結構體。這個座標系的原點在 iOS 的左上方以及 OS X 的左下方。換句話說,縱軸方向在 iOS 和 OS X 上不同。框架
CGSize
是另外一個簡單的 C 結構體,它定義了一個寬度值(width)和高度值(height)。CGRect
包含一個origin
(原點)字段、一個CGPoint
和一個size
(大小)字段,即一個CGSize
。origin
(原點)和size
(大小)字段一塊兒決定了一個矩形的位置和大小。ide
CGGeometry Reference 也定義了其餘類型,例如CGFloat
和CGVector
。CGFloat
就是一個float
(單精度浮點型)或者double
(雙精度浮點型)的typedef
(類型重定義),是哪種取決於應用運行的機器結構是 32 位仍是 64 位。
<!--more-->post
第一個要搞清楚的是一個視圖的frame
和bounds
之間的區別,由於這困擾着不少 iOS 入門開發者。不過這個區別也不復雜。spa
在 iOS 和 OS X 中,一個應用有多個座標系。好比,在 iOS 中應用窗口定位在屏幕的座標系,而窗口的每個子視圖定位在窗口的座標系。換句話說,一個視圖的子視圖老是定位在該視圖的座標系中。日誌
Framescode
如文檔中說的,視圖的frame
是一個結構體,即一個CGRect
,它定義了這個視圖的大小和它在父視圖中的位置,或者說父視圖座標系中的位置。看看下面的圖應該就能明白了。
Bounds
視圖的bounds
屬性定義了這個視圖的大小和它在自身座標系中的位置。這意味着大多數狀況下一個視圖的 bounds 的原點都是{0,0}
,以下圖。視圖的bounds
對於繪製這個視圖很重要。
當視圖的frame
屬性被修改時,視圖的center
和bounds
屬性兩者或者其一也同時被改變。
方便的取值方法
以前提到過,CGGeometry Reference 是一個讓運用座標和矩形更方便的結構體、常量和方法的集合。你可能碰到過相似的下面的代碼片斷:
CGPoint point = CGPonitMake(self.view.frame.origin.x + self.view.frame.size.width, self.view.frame.origin.y + self.view.frame.size.height);
這樣的片斷不盡難閱讀,並且過於冗長。咱們能夠用在 CGGeometry Reference 中定義的兩個方便的方法重寫這段代碼。
CGRect frame = self.view.frame; CGPoint point = CGPointMake(CGRectGetMaxX(frame), CGRectGetMaxY(frame));
爲了簡化以前那段代碼,咱們把視圖的frame
儲存到一個叫frame
的變量中,而且使用了CGRectGetMaxX
和CGRectGetMaxY
兩個方法。這兩個方法的方法名解釋了本身的功能。
CGGeometry Reference 定義了返回一個矩形 x 軸座標、y 軸座標最小和最大值以及這個矩形中心座標的方法。另外兩個方便的取之方法是`CGRectGetWidth和
CGRectGetHeight`。
建立結構體
當要建立CGPoint
、CGSize
和CGRect
時,大多數人都用CGPointMake
或者相似的方法。這些方法也被定義在 CGGeometry Reference 中。雖然它們的實現很是簡單,它們特別有用而且讓你少寫一些代碼。例如,CGRectMake
是這樣實現的:
CGRectMake(CGFloat x, CGFloat y, CGFloat width, CGFloat height) { CGRect rect; rect.origin.x = x; rect.origin.y = y; rect.size.width = width; rect.size.height = height; return rect; }
修改矩形
以上提到過的方法都是 iOS 開發者熟知的,它們減小了咱們的代碼量而且讓它們可讀性增長。不過,CGGeometry Reference 也定義了一切其餘你們不太瞭解的方法。好比 CGGeometry Reference 定義了一堆修改CGRect
結構的方法。讓咱們來看看其中一些。
CGRectUnion
CGRectUnion
接受兩個CGRect
結構體做爲參數而且返回一個可以包含這兩個矩形的最小矩形。聽起來可能沒什麼,我相信你也能夠用幾行代碼輕鬆實現這個功能,不過 CGGeometry 作的是給你提供一些方法讓你的代碼更乾淨、可讀性更強。
若是你把下面代碼片斷加到一個 view controller 的viewDidLoad
方法中,你將在模擬器中看到以下結果。那個灰色的矩形就是使用CGRectUnion
的結果。
// CGRectUnion CGRect frame1 = CGRectMake(80.0, 100.0, 150.0, 240.0); CGRect frame2 = CGRectMake(140.0, 240.0, 120.0, 120.0); CGRect frame3 = CGRectUnion(frame1, frame2); UIView *view1 = [[UIView alloc] initWithFrame:frame1]; [view1 setBackgroundColor:[UIColor redColor]]; UIView *view2 = [[UIView alloc] initWithFrame:frame2]; [view2 setBackgroundColor:[UIColor orangeColor]]; UIView *view3 = [[UIView alloc] initWithFrame:frame3]; [view3 setBackgroundColor:[UIColor grayColor]]; [self.view addSubview:view3]; [self.view addSubview:view2]; [self.view addSubview:view1];
CGRectDivide
另外一個有用的方法是CGRectDivide
,它幫你把一個給定矩形分割成兩個。看看下面的代碼和截圖來了解它是怎麼運做的。
// CGRectDivide CGRect frame = CGRectMake(10.0, 50.0, 300.0, 300.0); CGRect part1; CGRect part2; CGRectDivide(frame, &part1, &part2, 100.0, CGRectMaxYEdge); UIView *view1 = [[UIView alloc] initWithFrame:frame]; [view1 setBackgroundColor:[UIColor grayColor]]; UIView *view2 = [[UIView alloc] initWithFrame:part1]; [view2 setBackgroundColor:[UIColor orangeColor]]; UIView *view3 = [[UIView alloc] initWithFrame:part2]; [view3 setBackgroundColor:[UIColor redColor]]; [self.view addSubview:view1]; [self.view addSubview:view2]; [self.view addSubview:view3];
若是你不使用CGRectDivide
來計算紅色和橙色矩形的話,你可能要多謝幾十行代碼。不信你就試試。
比較和包含
用下面六個方法來比較幾何結構和檢查包含關係很是簡單。
CGPointEqualToPoint
CGSizeEqualToSize
CGRectEqualToRect
CGRectIntersectsRect
CGRectContainsPoint
CGRectContainsRect
CGGeometry Reference 還有一些其餘寶貝,好比CGPointCreateDictionaryRepresentation
能夠用來將一個 CGPoint 結構體轉換爲一個 CGDictionaryRef
,CGRectIsEmpty
能夠用來檢查一個矩形的寬高是否都爲零。更多詳情請看[《CGGeometry Reference 文檔》]()。
在 Xcode 控制檯打印日誌若是沒有一些輔助方法的話很麻煩。幸運的是,UIKit 框架定義了一些讓它變得很方便的方法。我每天用它們。看看下面的代碼片斷來了解它們是如何工做。並無什麼奇特的。
CGPoint point = CGPointMake(10.0, 25.0); CGSize size = CGSizeMake(103.0, 223.0); CGRect frame = CGRectMake(point.x, point.y, size.width, size.height); NSLog(@"\n%@\n%@\n%@", NSStringFromCGPoint(point), NSStringFromCGSize(size), NSStringFromCGRect(frame));
還有一些方便打印仿射變換(affine transforms)(NSStringFromCGAffineTransform
)、邊緣插入(NSStringFromUIEdgeInsets
)、偏移(NSStringFromUIOffset
)等的日誌的方便方法。
iOS SDK 包含了大量開發者們不瞭解的寶貝。我但願我給大家講明白了 CGGeometry Reference 的實用性。一旦你開始使用它的那些方法,你就會開始問本身,之前沒用它怎麼活過來的。