前言
hihi,勇敢的小伙伴儿们大家好,平常我总是只会简单的使用CGRect,CGPoint,CGSize,发现自己真的low到不行···所以今天来跟大家一起学习一下CGGeometry中的一些方法。
CGGeometry参考定义几何结构和功能,操作简单。数据结构中的一个点CGPoint代表在一个二维坐标系统。数据结构的位置和尺寸CGRect代表的一个长方形。数据结构的尺寸CGSize代表宽度和高度。
正文
1.CGRect中的CGRectGet
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.y + frame.size.width
- CGRectGetMaxY 用来替代 frame.origin.y + frame.size.height
emmm暴风哭泣!用了它们几个基本不用再繁琐的计算了,超级方便!
这时候你也许会想到,那CGRectGetMinX,CGRectGetMinY和CGRectGetWidth,CGRectGetHeight四个方法是不是也是用来替代类似这样的代码。
- CGRectGetMinX 用来替代 frame.origin.x
- CGRectGetMinY 用来替代 frame.origin.y
- CGRectGetWidth 用来替代 frame.size.width
- CGRectGetHeight 用来替代 frame.size.height
答案是肯定的。但是这四个方法看起来却不是那么的方便,因为跟直接调用比,这样写代码要更长,emmmm,对于没用过这个的程序员来讲,读代码的难度也增加了。不建议这么写。
而且如果想要唤起 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 redColor];
[self.view addSubview:view];
CGRect frame1 = CGRectMake(-50, -100, 100, 150);
UIView *subview1 = [[UIView alloc] initWithFrame:frame1];
subview1.backgroundColor = [UIColor yellowColor];
[view addSubview:subview1];
CGRect frame2 = CGRectMake(-50, -100, -100, -150);
UIView *subview2 = [[UIView alloc] initWithFrame:frame2];
subview2.backgroundColor = [UIColor blueColor];
[view addSubview:subview2];
NSLog(@"%f, %f", CGRectGetMinX(frame2), frame2.origin.x);
NSLog(@"%f, %f", CGRectGetMinY(frame2), frame2.origin.y);
NSLog(@"%f, %f", CGRectGetMidX(frame2), frame2.origin.x + frame2.size.width / 2);
NSLog(@"%f, %f", CGRectGetMidY(frame2), frame2.origin.y + frame2.size.height / 2);
NSLog(@"%f, %f", CGRectGetMaxX(frame2), frame2.origin.x + frame2.size.width);
NSLog(@"%f, %f", CGRectGetMaxY(frame2), frame2.origin.y + frame2.size.height);
NSLog(@"%f, %f", CGRectGetWidth(frame2), frame2.size.width);
NSLog(@"%f, %f", CGRectGetHeight(frame2), frame2.size.height);输出结果为:
界面效果为:
通过图片显而易见,使用CGRectGetMinX 获取到的是subView2显示在屏幕最左边位置就是-150。而通过 frame.origin.x获取到的必然是-50。简单来说,只要width或者height是负数,那么这个view的左右或上下其实是翻转过的。之前用直接调用的方法(frame.origin.x + frame.size.width)去获取的(-50 -100 = -150)其实是view的左边而不是右边。
通过这样的规则,我们推理出这八种方法的函数原型,如下所示。
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真实的位置
- 调用接口简单
参考地址:
[StackOverflow] CGRectGetWidth vs CGRect.size.width
2.CGRect中的CGRectInset和CGRectOffset
CGRectInset和CGRectOffset都是通过参数改变CGRect并返回一个修改后的CGRect类型的数据。
总结两者的区别在于:CGRectInset会进行平移和缩放两个操作,CGRectOffset只能进行平移。
CGRect CGRectInset(CGRect rect, CGFloat dx, CGFloat dy)
通过dx和dy重置rect作为结果返回。
重置的方式为,首先将rect的坐标(origin)按照(dx,dy)进行平移,然后将rect的大小(size)宽度(width)缩小2倍的dx,高度(height)缩小2倍的dy。
代码如下:
CGRect rect = CGRectMake(100, 200, 100, 100);
CGRect newRect = CGRectInset(rect, 20, 20);
UIView *view = [[UIView alloc] initWithFrame:rect];
view.backgroundColor = [UIColor redColor];
[self.view addSubview:view];
UIView *view1 = [[UIView alloc] initWithFrame:newRect];
view1.backgroundColor = [UIColor blackColor];
[self.view addSubview:view1];展示效果:
CGRect CGRectOffset(CGRect rect, CGFloat dx, CGFloat dy)
通过dx和dy重置rect作为结果返回。
重置的方式为,将rect的坐标(origin)按照(dx,dy)进行平移。
代码如下:
CGRect rect = CGRectMake(100, 200, 100, 100);
CGRect newRect = CGRectOffset(rect, 20, 20);
UIView *view = [[UIView alloc] initWithFrame:rect];
view.backgroundColor = [UIColor redColor];
[self.view addSubview:view];
UIView *view1 = [[UIView alloc] initWithFrame:newRect];
view1.backgroundColor = [UIColor blackColor];
[self.view addSubview:view1];
展示效果:
参考地址:
3.常用方法集锦
- 创建一个几何原始数值
//返回一个指定坐标点
CGPoint CGPointMake(CGPoint A,CGPoint B)
//根据指定的坐标和大小创建一个矩形
CGRect CGRectMake(CGFloat x,CGFloat y,CGFloat width,CGFloat height)
//根据指定长宽创建一个CGSize
CGSize CGSizeMake(CGFloat width,CGFloat height)- 修改矩形
CGRectDivide使用方法:【iOS】CGRectDivide
//水平或垂直分割矩形
CGRectDivide(CGRect rect, CGRect * slice,
CGRect * remainder, CGFloat amount, CGRectEdge edge)
//返回一个比原矩形大或小的矩形,但是中心点是相同的
CGRect CGRectInset(CGRect rect, CGFloat dx, CGFloat dy)
//将矩形A的值转变成整数,得到一个最小的矩形,
CGRect CGRectIntegral(CGRect A)
//获取两个矩形相交处所的矩形,没有相交返回NULL,用CGRectIsNull来检测
CGRect CGRectIntersection:(CGRect A,CGRect B)
//返回一个比原矩形上、下、左、右偏移的矩形
CGRect CGRectOffset(CGRect rect, CGFloat dx, CGFloat dy)
//找出相对的坐标
//如CGRectStandardize(CGRectMake(100,100,-50,-50)) = CGRectMake(50,50,50,50)
CGRect CGRectStandardize(CGRect rect)
//包含两个ri和r2的总Rect
//如CGRectUnion(CGRectMake(100,200,100,100), CGRectMake(50,50,50,50)) = CGRectMake(50,50,150,250)
CGRect CGRectUnion(CGRect r1, CGRect r2)- 比较数值
//返回两个点是否相等
bool CGPointEqualToPoint(CGPoint A,CGPoint B)
//CGSizeAB是否相等
bool CGSizeEqualToSize(CGSize A,CGSize B)
//矩形AB的位置大小是否相等
bool CGRectEqualToRect(CGRect A,CGRect B)
//矩形AB是否相交,可用来判断精灵是否离开了屏幕
bool CGRectIntersectsRect(CGRect A,CGRect B)- 检查
//检测矩形A是否包含指定的点B
bool CGRectContainsPoint(CGRect A, CGPoint B)
//检测矩形A是否包含矩形B
bool CGRectContainsRect(CGRect A,CGRect B)
- 获取最大值、中间值和最小值
//获取矩形x坐标的最小值
CGFloat CGRectGetMinX(CGRect A)
//获取矩形y坐标的最小值
CGFloat CGRectGetMinY(CGRect A)
//获取矩形x坐标的中间值
CGFloat CGRectGetMidX(CGRect A)
//获取矩形y坐标的中间值
CGFloat CGRectGetMidY(CGRect A)
//获取矩形x坐标的最大值
CGFloat CGRectGetMaxX(CGRect A)
//获取矩形y坐标的最大值
CGFloat CGRectGetMaxY(CGRect A)- 获取高度和宽度
//获取矩形A的高
CGFloat CGRectGetHeight(CGRect A)
//获取矩形A的宽
CGFloat CGRectGetWidth(CGRect A) - 检测矩形是否存在或是无穷大
//矩形A是否长和宽都是0,或者是个NULL
bool CGRectIsEmpty(CGRect A)
//矩形A是否为NULL
bool CGRectIsNull(CGRect A)
//矩形A是否无穷大,没有边界
bool CGRectIsInfinite(CGRect A)参考地址:
Objective-c 中CGGeometry几何类常用方法简单整理
如有错误烦请小伙伴儿们指出,感激不尽~