使用CGGeometry方法獲取CGRect數據

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;
CGRect.png

通過圖片顯而易見,使用 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 方法的好處進行簡單總結。

  1. 語義生動直觀
  2. 代碼風格統一
  3. 能夠獲取到view真實的位置
  4. 調用接口簡單

參考資料

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,882評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,208評論 3 414
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,746評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,666評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,477評論 6 407
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,960評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,047評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,200評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,726評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,617評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,807評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,327評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,049評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,425評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,674評論 1 281
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,432評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,769評論 2 372

推薦閱讀更多精彩內容