UIViewController
生命周期
- initWithCoder:
- awakeFromNib
- loadView
- viewDidLoad
- viewWillAppear:
- viewWillLayoutSubviews
- viewDidLayoutSubviews
- viewDidAppear:
- viewWillDisappear:
- viewDidDisappear:
- dealloc
说一下控制器收到内存警告会如何处理
当系统内存告急时,会接收到 didReceiveMemoryWarning。 这是属于 ViewController 的方法,当 ViewController 接收到 didReceiveMemoryWarning,首先会判断当前的 ViewController 是否还显示在 window 上,如果不在就会移除当前的 ViewController,销毁 ViewController 上面的子控件,并执行 ViewDidUnload 方法。
如何以通用的方法找到当前显示的 ViewController
- 判断 rootVC 是否有 presentVC,有的话就是这个 presentedViewController
- 判断是否是 UINavigationViewController,是的话从栈顶拿出 VC,然后再根据这个 VC 去找显示的 VC
- 在看是否是 TabbarController,是的话,找到当前选中的 VC,然后再根据这个 VC 去找显示的 VC
- 如果只是一个普通的 VC,就返回这个 VC 本身
UIView
生命周期
- layoutSubviews
- didAddSubview
- willRemoveSubview
- willMoveToSuperview
- didMoveToSuperview
- willMoveToWindow
- didMoveToWindow
- removeFromSuperview
- dealloc
UIView 和 CALayer 是什么关系
CALayer 负责图像动画的渲染
UIView 负责用户的交互
Bounds & Frame
frame 是在父控件坐标系的位置,是空间的外部尺寸
bounds 是相对自身坐标系的,是空间的内部尺寸。
界面更新
当操作 UI 时,比如改变了 frame、更新 UIView/CALayer 的层级、或者手动调用了 setNeedDisplay 时,这个 UIView/CALayer 就会被标记为待处理,放到一个全局容器中,苹果注册了一个 Observer 监听 BeforeWaiting(即将进入休眠) 和 Exit (即将退出 Loop) 事件,回调去执行一个很长的函数:_ZN2CA11Transaction17observer_callbackEP19__CFRunLoopObservermPv
。这个函数里会遍历所有待处理的 UIView/CAlayer 以执行实际的绘制和调整,并更新 UI 界面。
异步绘制
需要通过系统的 [view.delegate displayLayer:]
实现异步绘制
代理负责生成 bitmap,然后设置 layer 的 contents 属性
如何高性能的为视图加圆角
- 设置 CALayer 的 cornerRadius
ps:iOS9 以后 UIImageView 设置 png 格式图片,不会触发离屏渲染 设置 CALayer 的 mask
1
2
3
4
5
6
7
8
9
10
11// 通过图片生成遮罩,
UIImage *maskImage = [UIImage imageNamed:@"someimg"];
CALayer *mask = [CALayer new];
mask.frame = CGRectMake(0, 0, maskImage.size.width, maskImage.size.height);
mask.contents = (__bridge id _Nullable)(maskImage.CGImage);
view.layer.mask = mask;
//通过贝塞尔曲线生成
CAShapeLayer *mask = [CAShapeLayer new];
mask.path = [UIBezierPath bezierPathWithOvalInRect:view.bounds].CGPath;
view.layer.mask = mask;通过 Core Graphics 重新绘制带圆角的视图
通过 CPU 重新绘制一份带圆角的视图来实现圆角效果,会大大增加 CPU 的负担,1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25@implementation UIImage (CircleImage)
- (UIImage *)drawCircleImage {
CGFloat side = MIN(self.size.width, self.size.height);
UIGraphicsBeginImageContextWithOptions(CGSizeMake(side, side), false, [UIScreen mainScreen].scale);
CGContextAddPath(UIGraphicsGetCurrentContext(),
[UIBezierPath bezierPathWithOvalInRect:CGRectMake(0, 0, side, side)].CGPath);
CGContextClip(UIGraphicsGetCurrentContext());
CGFloat marginX = -(self.size.width - side) / 2.f;
CGFloat marginY = -(self.size.height - side) / 2.f;
[self drawInRect:CGRectMake(marginX, marginY, self.size.width, self.size.height)];
CGContextDrawPath(UIGraphicsGetCurrentContext(), kCGPathFillStroke);
UIImage *output = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return output;
}
@end
//在需要圆角时调用如下
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
UIImage *img = [[UIImage imageNamed:@"image.png"] drawCircleImage];
dispatch_async(dispatch_get_main_queue(), ^{
view.image = img;
});
});通过混合图层
此方法就是在要添加圆角的视图上再叠加一个部分透明的视图,只对圆角部分进行遮挡。图层混合的透明度处理方式与 mask 正好相反。此方法虽然是最优解,没有离屏渲染,没有额外的 CPU 计算,但是应用范围有限。
总结
- 在可以使用混合图层遮挡的场景下,优先使用第四种方法。
- 即使是非 iOS9 以上系统,第一种方法在综合性能上依然强于后两者,iOS9 以上由于没有了离屏渲染更是首选。
- 方法二和方法三由于使用了贝塞尔曲线,都可以应对复杂的圆角。只不过前者牺牲帧率,后者需要大量计算和增加部分内存,需要实际情况各自取舍。
UIButton 的父类是什么?UILabel 的父类又是什么
- UIButton -> UIControl -> UIView -> UIResponder
- UILabel -> UIView -> UIResponder
- 区别就在于 UIControlState……、UIControlEvent……
实现一个控件,可以浮在任意界面的上层并支持拖动
- 可以添加一个 window,然后设置 window 的层级
- 把 View 添加到 window 上
- 给 window 重写 pointInSide 属性,当范围在 view 上的时候,才返回 true,防止影响主 window 的事件
- 重写 view 的 touchDidMove 方法,获取上一次的 point 和当前 touch 的 point,然后调整 view 的坐标
setNeedsDisplay 和 layoutIfNeeded 两者是什么关系
setNeedsDisplay 是给当前的视图做了标记。
layoutIfNeeded 查找是否有标记,如果有标记及立刻刷新。
只有这二者合起来使用,才会起到立刻刷新的效果。