原文:《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 的实用性。一旦你开始使用它的那些方法,你就会开始问本身,之前没用它怎么活过来的。