1、View 的创建与设置
- UIView 创建出来默认是透明的,在 iOS6 的时候是白色的。
// 实例化 view 对象,并设置 view 大小
UIView *view = [[UIView alloc] init];
// 将 view 加到 window 上显示出来
[self addSubview:view];
// 控件矩形框在父控件中的位置和尺寸,以父控件的左上角为坐标原点
view.frame = CGRectMake(10, 20, 200, 100);
// 控件矩形框的位置和尺寸,以自己左上角为坐标原点,所以 bounds 的 x、y 一般为 0
view.bounds = CGRectMake(0, 0, 200, 100);
// 设置中心位置:控件中点的位置,以父控件的左上角为坐标原点
view.center = self.center;
// 设置背景颜色
view.backgroundColor = [UIColor greenColor];
// 设置背景颜色半透明
view.backgroundColor = [[UIColor blackColor] colorWithAlphaComponent:0.5];
// 设置视图透明度
/*
范围:0.0 ~ 1.0 ,0.0 透明,1.0 不透明(默认)
视图上的文字等内容的透明度也同时被改变
*/
view.alpha = 1.0;
// 用于区分不同的 view ,所有继承 view 的类对象都可以设置 tag 值
view.tag = 100;
// 设置用户交互属性:YES:打开用户交互属性,NO:关闭用户交互属性
view.userInteractionEnabled = YES;
// 圆角半径
view.layer.cornerRadius = 20;
// 子图层是否剪切图层边界,默认为 NO
view.layer.masksToBounds = YES;
// 边框粗细
view.layer.borderWidth = 5;
// 边框颜色
view.layer.borderColor = [[UIColor blueColor] CGColor];
// 不允许子视图的范围超过父视图的范围
view.clipsToBounds = NO;
// 获得自己的所有子控件对象:数组元素的顺序决定着子控件的显示层级顺序(下标越大的,越显示在上面)
NSArray *subviews = view.subviews;
// 获得自己的父控件对象
UIView *superview = view.superview;
// 从父视图中移除
[view removeFromSuperview];
// 根据一个 tag 标识找出对应的控件
UIView *view = [self viewWithTag:100];
2、View 的层次设置
// 将 view 放到最上层
[self bringSubviewToFront:view];
// 将 view 放倒最下面
[self sendSubviewToBack:view];
// 位置进行交换
[self exchangeSubviewAtIndex:0 withSubviewAtIndex:2];
// 将 view1 放在 view3 上面
[self insertSubview:view1 aboveSubview:view3];
// 将 view3 放在 view2 下面
[self insertSubview:view3 belowSubview:view2];
// 将 view1 放在1的位置
[self insertSubview:view1 atIndex:1];
3、View 的旋转与缩放设置
-
3.1 单一形变
// 控件的形变属性
@property(nonatomic) CGAffineTransform transform;
// 旋转:(CGFloat angle) 旋转 45 度,需要输入的参数为弧度,45/180 * M_PI,1 度 = PI/180 弧度
self.testView.transform = CGAffineTransformMakeRotation(0.25 * M_PI);
[self.testView.layer setAffineTransform: CGAffineTransformMakeRotation(0.25 * M_PI)];
// 缩放:(CGFloat sx, CGFloat sy) (1, 2) 宽度和高度的放大倍数
self.testView.transform = CGAffineTransformMakeScale(1, 2);
[self.testView.layer setAffineTransform: CGAffineTransformMakeScale(1, 2)];
// 平移:(CGFloat tx, CGFloat ty) (100, 100) 水平和垂直方向的移动距离
self.testView.transform = CGAffineTransformMakeTranslation(100, 100);
[self.testView.layer setAffineTransform: CGAffineTransformMakeTranslation(100, 100)];
-
3.2 叠加形变
// 旋转 + 缩放
CGAffineTransform rotationTransform = CGAffineTransformMakeRotation(0.25 * M_PI);
self.testView.transform = CGAffineTransformScale(rotationTransform, 2, 2);
// 旋转 + 平移
CGAffineTransform rotationTransform = CGAffineTransformMakeRotation(0.25 * M_PI);
self.testView.transform = CGAffineTransformTranslate(rotationTransform, 200, 100);
// 缩放 + 平移
CGAffineTransform scaleTransform = CGAffineTransformMakeScale(2, 2);
self.testView.transform = CGAffineTransformTranslate(scaleTransform, 100, 100);
// 旋转 + 缩放 + 平移
CGAffineTransform rotationTransform = CGAffineTransformMakeRotation(0.25 * M_PI);
CGAffineTransform rotationScaleTransform = CGAffineTransformScale(rotationTransform, 2, 2);
self.testView.transform = CGAffineTransformTranslate(rotationScaleTransform, 200, 100);
-
3.3 累加形变
// 连续旋转
self.testView.transform = CGAffineTransformRotate(self.testView.transform, 0.25 * M_PI);
// 连续缩放
self.testView.transform = CGAffineTransformScale(self.testView.transform, 2, 2);
// 连续平移
self.testView.transform = CGAffineTransformTranslate(self.testView.transform, 100, 100);
// 还原所有形变
self.testView.transform = CGAffineTransformIdentity;
[self.testView.layer setAffineTransform:CGAffineTransformIdentity];
4、View 的跟随模式设置
// 父视图设置
// 父视图允许子视图跟随:default is YES
fatherView.autoresizesSubviews = YES;
// 子视图设置
/*
UIViewAutoresizingNone = 0, // 不跟随
UIViewAutoresizingFlexibleLeftMargin = 1 << 0, // 左边距 随父视图变化
UIViewAutoresizingFlexibleRightMargin = 1 << 2, // 右边距 随父视图变化
UIViewAutoresizingFlexibleTopMargin = 1 << 3, // 上边距 随父视图变化
UIViewAutoresizingFlexibleBottomMargin = 1 << 5 // 下边距 随父视图变化
UIViewAutoresizingFlexibleWidth = 1 << 1, // 宽度 随父视图变化
UIViewAutoresizingFlexibleHeight = 1 << 4, // 高度 随父视图变化
*/
sonView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
5、View 的动画设置
-
-
5.1 block 方式
- 设置控件位置、尺寸、透明度等的代码,放在 animateWithDuration: block 中,将自动以动画的方式改变。
// 移动时间 2 秒
[UIView animateWithDuration:2 animations:^{
// 改变控件的位置和尺寸,改变后的位置或大小
view.frame = CGRectMake([UIScreen mainScreen].bounds.size.width - 60, 20, 50, 50);
} completion:^(BOOL finished) {
// 上一个设置完成后
[UIView animateWithDuration:2 animations:^{
// 改变控件的位置和尺寸,改变后的位置或大小
view.frame = CGRectMake(10, [UIScreen mainScreen].bounds.size.height - 110, 100, 100);
}];
}];
-
5.2 动画块方式
- 设置控件位置、尺寸、透明度等的代码,放在 beginAnimations: 和 commitAnimations 之间,将自动以动画的方式改变。
// 开始一个动画块
[UIView beginAnimations:nil context:nil];
// 动画设置
// 设置动画时间:default = 0.2
[UIView setAnimationDuration:2.0];
// 设置延时:设置指定的时间后开始执行动画,default = 0.0
[UIView setAnimationDelay:1.0];
// 设置动画开始执行时间:default = now ([NSDate date])
[UIView setAnimationStartDate:[NSDate dateWithTimeIntervalSinceNow:10]];
// 设置动画执行节奏
/*
UIViewAnimationCurveEaseInOut, // slow at beginning and end 开始喝结束慢速,默认
UIViewAnimationCurveEaseIn, // slow at beginning 开始慢速
UIViewAnimationCurveEaseOut, // slow at end 结束慢速
UIViewAnimationCurveLinear // 匀速
*/
[UIView setAnimationCurve:UIViewAnimationCurveEaseOut];
// 设置重复次数:default = 0.0. May be fractional
[UIView setAnimationRepeatCount:CGFLOAT_MAX];
// 设置是否自动返回:default = NO. used if repeat count is non-zero
[UIView setAnimationRepeatAutoreverses:YES];
// 设置是否从当前状态开始动画:default = NO
[UIView setAnimationBeginsFromCurrentState:YES];
// 设置代理:default = nil
[UIView setAnimationDelegate:self];
// 设置动画开始时执行的代理方法,自定义方法:default = NULL
[UIView setAnimationWillStartSelector:@selector(startAnimations)];
// 设置动画结束时执行的代理方法,自定义方法:default = NULL
[UIView setAnimationDidStopSelector:@selector(stopAnimations)];
// 动画之行后效果
// 设置透明度,改变后的透明度
view.alpha = 0.0;
// 改变控件的位置和尺寸,改变后的位置或大小
view.center = CGPointMake(250, 250);
view.frame = CGRectMake(100, 180, 50, 50);
// 结束一个动画块
[UIView commitAnimations];
6、frame 与 NSValue 的相互转换
// Frame 转 NSValue
NSValue *freamValue = [NSValue valueWithCGRect:frame];
// NSValue 转 Frame
frame = [freamValue CGRectValue];
7、UI 控件 weak 引用
- 我们知道,从 Storyboard 往编译器拖出来的 UI 控件的属性是 weak 的,
- 那么,如果有一些 UI 控件我们要用代码的方式来创建,那么它应该用 weak 还是 strong 呢?为什么?
@property (weak, nonatomic) IBOutlet UIButton *myButton;
- 这是一道有意思的问题。简单来说,这道题并没有标准答案,但是答案背后的解释却非常有价值,
- 能够看出一个人对于引用计数,对于 view 的生命周期的理解是否到位。
- 我们就能看到一些理解非常不到位的解释,例如说:Storyboard 拖线使用 weak 是为了规避出现循环引用的问题。
- 这个理解是错误的,Storyboard 拖出来的控件即使是 strong 的,也不会有循环引用问题。
- UI 控件用默认用 weak,根源还是苹果希望只有这些 UI 控件的父 View 来强引用它们,
- 而 ViewController 只需要强引用 ViewController.view 成员,则可以间接持有所有的 UI 控件。
- 这样有一个好处是:在以前,当系统收到 Memory Warning 时,会触发 ViewController 的 viewDidUnload 方法,
- 这样的弱引用方式,可以让整个 view 整体都得到释放,也更方便重建时整体重新构造。
- 但是首先 viewDidUnload 方法在 iOS 6 开始就被废弃掉了,苹果用了更简单有效地方式来解决内存警告时的视图资源释放。
- 总之就是,除非你特殊地操作 view 成员,ViewController.view 的生命期和 ViewController 是一样的了。
- 所以在这种情况下,其实 UI 控件是不是 weak 其实关系并不大。
- 当 UI 控件是 weak 时,它的引用计数是 1,持有它的是它的 superview,
- 当 UI 控件是 strong 时,它的引用计数是 2,持有它的有两个地方,
- 一个是它的 superview,另一个是这个 strong 的指针。UI 控件并不会持有别的对象,
- 所以,不管是手写代码还是 Storyboard,UI 控件是 strong 都不会有循环引用的。
- 那么回到我们的最初的问题,自己写的 view 成员,应该用 weak 还是 strong?
- 我个人觉得应该用 strong,因为用 weak 并没有什么特别的优势,其实 weak 变量会有额外的系统维护开销的,
- 如果你没有使用它的特别的理由,那么用 strong 的话应该更好。
- 另外如果你要做 Lazy 加载,那么你也只能选择用 strong。当然,如果你非要用 weak,其实也没什么问题,
- 只需要注意在赋值前,先把这个对象用 addSubView 加到父 view 上,否则可能刚刚创建完,它就被释放了。
8、简述对 UIView、UIWindow 和 CALayer 的理解
- UIView 对象定义了屏幕上的一个矩形区域,用于构建用户界面和响应用户触屏事件。
- 一个 UIView 的实例可以包含和管理若干个子 UIView。UIView 的直接父类为 UIResponder 类,可以响应用户事件。
- UIWindow 对象是所有 UIView 的根,管理和协调应用程序的显示。UIWindow 类是 UIView 的子类,
- 可以看作是特殊的 UIView。一般应用程序只有一个 UIWindow 对象,即使有多个 UIWindow 对象,
- 也只有一个 UIWindow 可以接受到用户的触屏事件。
- UIViewController 对象负责管理所有 UIView 的层次结构,并处理用户的触摸事件。
- UIScreen 可以获取设备屏幕的大小。
- CALayer 直接从 NSObject 继承,因为缺少了 UIResponder 类,所以 CALayer 不能响应任何用户事件。
- QuartzCore 是 iOS 中提供图像绘制的基础库,并且 CALayer 是定义该框架中。
- CALayer 定义了 position、size、transform、animations 等基本属性。UIView 是基于 CALayer 的高层封装。
- UIView 相比 CALayer 最大区别是 UIView 可以响应用户事件,而 CALayer 不可以。
- UIView 侧重于对显示内容的管理,CALayer 侧重于对内容的绘制。UIView 和 CALayer 是相互依赖的关系。
- UIView 依赖与 calayer 提供的内容,CALayer 依赖 UIView 提供的容器来显示绘制的内容。
- 归根到底 CALayer 是这一切的基础,如果没有 CALayer,UIView 自身也不会存在,
- UIView 是一个特殊的 CALayer 实现,添加了响应事件的能力。
- UIView 来自 CALayer,高于 CALayer,是 CALayer 高层实现与封装。UIView 的所有特性来源于 CALayer 支持。