使用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阅读 158,736评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,167评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,442评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,902评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,302评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,573评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,847评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,562评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,260评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,531评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,021评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,367评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,016评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,068评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,827评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,610评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,514评论 2 269

推荐阅读更多精彩内容