CGGeometry 中提供了取特定 CGRect 值的便捷方法。
- CGRectGetMinX
- CGRectGetMinY
- CGRectGetMidX
- CGRectGetMidY
- CGRectGetMaxX
- CGRectGetMaxY
- CGRectGetWidth
- CGRectGetHeight
這其中CGRectGetMidX
,CGRectGetMidY
,CGRectGetMaxX
,CGRectGetMaxY
四個方法十分有用。用CGRectGetMaxX
代替frame.origin.x + frame.size.width
將使代碼更加清晰、語義上也更為生動直觀。另外三個方法也同樣,他們都可以用來代替類似這樣的代碼。
-
CGRectGetMidX
用來替代frame.origin.x + frame.size.width / 2
-
CGRectGetMidY
用來替代frame.origin.y + frame.size.height / 2
-
CGRectGetMaxX
用來替代frame.origin.x + frame.size.width
-
CGRectGetMaxY
用來替代frame.origin.y + frame.size.height
這時你也許會想到,那CGRectGetMinX
,CGRectGetMinY
,CGRectGetWidth
,CGRectGetHeight
四個方法是不是也是用來代替類似這樣的代碼。
-
CGRectGetMinX
用來替代frame.origin.x
-
CGRectGetMinY
用來替代frame.origin.y
-
CGRectGetWidth
用來替代frame.size.width
-
CGRectGetHeight
用來替代frame.size.width
答案是肯定的。但這四個方法看起來卻不是那么有必要。因為跟直接調用相比,這樣寫代碼要更長。
- CGRectGetMinX(frame)
- frame.origin.x
而且如果想要喚起 Xcode 的聯想,最少需要輸入“CGRectGet”八個字母。而直接調用通常只需要輸入“f”,“.o”,“.x”五個字母就能聯想完成。經過性能測試,CGRectGetMinX(frame) 的億次調用耗時561毫秒,而直接調用由于不明原因,不管循環多少次測試調用時間都低于1毫秒。竟然代碼更長,輸入更慢,效率更低,那 CGGeometry 提供這些接口的意義在哪呢。其實,僅憑代碼統一的代碼風格,生動直觀的語義也值得你這樣去做。而且效率上的差異只是理論數值,實際上CGRectGet的10萬次調用只耗時1毫秒,在整個APP的運行期間也很難有超過10萬次的調用。
蘋果官方文檔推薦使用 CGGeometry 方法,同時盡量避免直接調用。事實上僅僅因為上述的這些好處蘋果是不會建議盡量避免直接調用的,這兩種調用方法其實存在這本質的差異。在平時的使用中我們沒有遇到差異,是因為我們使用的 frame 或 bounds 通常都是正數,當負數出現的時候,有意思的事情就來了。
StackOverflow摘自蘋果郵件列表的原文說明了這個現象。大意就是“CGRectGetWidth/Height 會將 width 或者 height 格式化,格式化基本上只是檢查 width 或者 height 是否為負數,如果為負數則取絕對值”。下邊就來解釋這個現象背后的原因和他們本質的差別。
CGRectGetWidth/Height will normalize the width or height before returning them. Normalization is basically just checking if the width or height is negative, and negating it to make it positive if so.
CGRect 是用來儲存矩形幾何信息的容器,通過這些信息就能知道怎么去繪制矩形。UIView 通過 CGRect 類型的成員變量 frame 來描述自己的位置和形狀。通過 CGRectGet 方法獲取到的是 view 的實際位置和形狀,而直接調用獲取的只是描述幾何體位置和形狀的信息,他們的是帶有方向性的。比如,view 的寬是-100,代表的是在 x 軸反方向的100,最終繪制出來矩形的寬還是100,只是在 x 軸負方向而已。所以,通過 CGRectGetWidth(frame) 獲取到的就是真實的寬度100,而直接調用只能獲取同時描述寬度和方向的數值-100。這看起來只是取絕對值這么簡單,但其他方法可并不是這樣。通過如下代碼繪制出來下圖所示的兩個 view 將幫助你更好的理解這一點。
UIView *view = [[UIView alloc] initWithFrame:CGRectMake(200, 500, 100, 100)];
view.backgroundColor = [UIColor grayColor];
[self.view addSubview:view];
UIView *subview = [[UIView alloc] initWithFrame:CGRectZero];
subview.backgroundColor = [UIColor lightGrayColor];
[view addSubview:subview];
CGRect frame = CGRectMake(-50, -100, -100, -150);
NSLog(@"%f, %f", CGRectGetMinX(frame), frame.origin.x);
NSLog(@"%f, %f", CGRectGetMinY(frame), frame.origin.y);
NSLog(@"%f, %f", CGRectGetMidX(frame), frame.origin.x + frame.size.width / 2);
NSLog(@"%f, %f", CGRectGetMidY(frame), frame.origin.y + frame.size.height / 2);
NSLog(@"%f, %f", CGRectGetMaxX(frame), frame.origin.x + frame.size.width);
NSLog(@"%f, %f", CGRectGetMaxY(frame), frame.origin.y + frame.size.height);
NSLog(@"%f, %f", CGRectGetWidth(frame), frame.size.width);
NSLog(@"%f, %f", CGRectGetHeight(frame), frame.size.height);
subview.frame = frame;
通過圖片顯而易見,使用 CGRectGetMinX 獲取到的是 viewB 顯示在屏幕上最左邊位置,就是-150。而通過 frame.origin.x 獲取到的必然是-50。簡單來說,只要 width 或者 height 是負數,那么這個 view 的左右或者上下其實是翻轉過的。你之前用直接調用的方法(frame.origin.x + frame.size.width)去獲取的(-50 - 100 = -150)其實 view 左邊。
代碼打印出來 CGRectGet 方法的數據都符合這樣的規則。
2016-02-26 15:40:13.141 LPTest[2319:859916] -150.000000, -50.000000
2016-02-26 15:40:13.141 LPTest[2319:859916] -250.000000, -100.000000
2016-02-26 15:40:13.141 LPTest[2319:859916] -100.000000, -100.000000
2016-02-26 15:40:13.141 LPTest[2319:859916] -175.000000, -175.000000
2016-02-26 15:40:13.141 LPTest[2319:859916] -50.000000, -150.000000
2016-02-26 15:40:13.141 LPTest[2319:859916] -100.000000, -250.000000
2016-02-26 15:40:13.142 LPTest[2319:859916] 100.000000, -100.000000
2016-02-26 15:40:13.142 LPTest[2319:859916] 150.000000, -150.00000
通過這樣的規則,我們推理出這八種方法的函數原型。如下所示。
CG_EXTERN CGFloat CGRectGetMinX(CGRect rect) {
return rect.origin.x + (rect.size.width > 0? 0: rect.size.width);
}
CG_EXTERN CGFloat CGRectGetMinY(CGRect rect) {
return rect.origin.y + (rect.size.height > 0? 0: rect.size.height);
}
CG_EXTERN CGFloat CGRectGetMidX(CGRect rect) {
return rect.origin.x + rect.size.width / 2;
}
CG_EXTERN CGFloat CGRectGetMidY(CGRect rect) {
return rect.origin.y + rect.size.height / 2;
}
CG_EXTERN CGFloat CGRectGetMaxX(CGRect rect) {
return rect.origin.x + (rect.size.width < 0? 0: rect.size.width);
}
CG_EXTERN CGFloat CGRectGetMaxY(CGRect rect) {
return rect.origin.y + (rect.size.height < 0? 0: rect.size.height);
}
CG_EXTERN CGFloat CGRectGetWidth(CGRect rect) {
return fabs(rect.size.width);
}
CG_EXTERN CGFloat CGRectGetHeight(CGRect rect) {
return fabs(rect.size.height);
}
大部分情況下,我們調用 frame.origin.x 的目的其實都是想獲取 view 最左邊的位置,其實都應該調用 CGRectGetMinX(frame) 方法。也許你以前習慣通過 frame.origin.x 來獲取左邊的位置,通過 CGRectGetMaxX(frame) 來獲取右邊的位置。這樣混合調用的代碼是存在很多隱患的,只是可能并沒有體現出來。在這種情況你最好還是改正下自己的習慣。了解了這些后你就能根據使用場景來決定自己應該怎么做。最后,對 CGRectGet 方法的好處進行簡單總結。
- 語義生動直觀
- 代碼風格統一
- 能夠獲取到view真實的位置
- 調用接口簡單