深度解析UIView

本篇文章仅是学习笔记,详细信息可查看文中链接

UIView:

UIView表示屏幕上的一块矩形区域,它在App中占有绝对重要的地位,因为IOS中几乎所有可视化控件都是UIView的子类。负责渲染区域的内容,并且响应该区域内发生的触摸事件

功能:

  1. 管理矩形区域里的内容
  2. 处理矩形区域中的事件
  3. 子视图的管理
  4. 还能实现动画
    (UIView的子类也具有这些功能)

UIView的内层次

在这里插入图片描述

UIView与UIResponder

  • UIResponder 是 UIView 的父类。

UIResponder的概念

  • responder 能够处理触摸、手势、远程控制等事件。之所以它是一个单独的类而没有合并到 UIView 中,是因为 UIResponder 有更多的子类,最明显的就是 UIApplication 和 UIViewController。
  • 通过重写 UIResponder 的方法,可以决定一个类是否可以成为第一响应者 (first responder),即当前输入焦点元素。
    当 touches (触摸) 或 motion (指一系列运动传感器) 等交互行为发生时,它们被发送给第一响应者 (通常是一个视图)。如果第一响应者没有处理,则该行为沿着响应链到达视图控制器,如果行为仍然没有被处理,则继续传递给应用。

平时用到的响应, 比如点击屏幕空白处, 收键盘, 都是Responder的方法, UIView继承父类UIResponder, 所以可以调用这些方法

触摸事件
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event;
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event;
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event;
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event;
其中只有在程序强制退出或者来电时,取消点击事件才会调用。

加速计事件 (摇一摇)
- (void)motionBegan:(UIEventSubtype)motion withEvent:(UIEvent *)event;
- (void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event;
- (void)motionCancelled:(UIEventSubtype)motion withEvent:(UIEvent *)event;

远程控制事件
额外配件如耳机上的音视频播放按键所触发的事件(视频播放、下一首)
- (void)remoteControlReceivedWithEvent:(UIEvent *)event;

事件链(即UIView的事件响应过程)

事件链文字即图片来源
作者:進无尽
文章链接

  • 当我们用手指轻触屏幕,iPhone OS会将它识别为一组触摸对象,并将它们封装在UITouch和UIEvent形式的实例,消息循环(runloop)会接收到触摸事件并放入当前应用程序的事件队列中。
  • 负责管理应用程序的UIApplication单件对象将事件从队列的顶部取出,找到当前运行的程序,典型情况下,它会将事件发送给应用程序的键盘焦点窗口—即拥有当前用户事件焦点的窗口.
  • 然后代表该窗口的UIWindow对象接受到事件开始进行最优响应视图查询的过程(逆序遍历subviews,后加载的先遍历)。
  • UIView对象并不一定会把事件传递给每一个子view,因为UIView是通过hitTest方法来判断点击事件发生在哪个子view上面的,会采用逆序查询也就是优先查询后加载的子试图,这样做也是为了优化查找速度,毕竟后addSubview的视图在上易于命中。如果它第一个hitTest就命中了的话,这个事件就不会再被传递给其他子试图了。
    举个例子:

🌰
就像上图那样,点击了红色的View,

  1. 如果先加载蓝色View,后加载红色UIView 传递过程是这样的:
    UIApplication对象——>UIWindow对象——>rootVC.view对象——>redview对象
  2. 如果先加载红色View,后加载蓝色UIView 传递过程是这样的:
    UIApplication对象——>UIWindow对象——>rootVC.view对象——>blueview对象——>redview对象
为证明这个过程, 我写了两个有重叠区域的button, 点击重叠区域, 观察访问顺序

测试
点击重叠区域(即test2那里)先添加TEST, 则TEST2响应, 先添加TEST2, 则TEST响应。

寻找最优响应视图的过程

事件的传递其实就是在事件产生与分发之后如何寻找最优响应视图的一个过程。其中涉及到了UIView中的两个方法(可以重写),当hitTest返回YES才会调用这个View的 Touch事件,因为如果返回NO,则当前View被排除在相应链之外了。

两个方法
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event;
//判断当前点击事件是否存在最优响应者(First Responder)

- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event;
//判断当前点击是否在控件的Bounds之内
视图命中查找流程
步骤

1.调用hitTest方法进行最优响应视图查询

hidden = YES
userInteractionEnabled = NO
alpha < 0.01
以上三种情况会使该方法返回nil,即当前视图下无最优响应视图

2.hitTest方法内部会调用pointInside方法对点击点进行是否在当前视图bounds内进行判断,如果超出bounds,hitTest则返回nil。 未超出范围则进行步骤3

3.对当前视图下的subviews采取逆序上述1 2步骤查询最优响应视图。如果hitTest返回了对应视图则说明在当前视图层级下有最优响应视图,可能为self或者其subview,这个要看具体返回。

UIView和CALayer

关于CALayer

CALayer封装的内容
CALayer分装的内容
CALayer的属性
CALayer的属性
关于位置
position和anchorPoint属性都是CGPoint类型的
position可以用来设置CALayer在父层中的位置,它是以父层的左上角为坐标原点(0, 0)
anchorPoint称为"定位点",它决定着CALayer身上的哪个点会在position属性所指的位置。它的x、y取值范围都是0~1,默认值为(0.5, 0.5)

一张CALayer的图CALayer

代码

- (void)viewDidLoad {
   [super viewDidLoad];
   self.view.backgroundColor = [UIColor whiteColor];
   CALayer *layer = [CALayer layer];
   layer.backgroundColor = [UIColor redColor].CGColor;
   layer.position = self.view.center;
   layer.anchorPoint = CGPointMake(0.2, 0.5);
   [self.view.layer addSublayer:layer];
   layer.frame = CGRectMake(50, 200, 180, 10);
}

那个红色的一块是一个CALayer

看起来好像和UIView一样?
答案很显然不是正确的。因为上述代码中的CALayer是放在self.view.layer中去,所以才拥有与UIView看似一样的属性。

关系

  1. 在创建UIView对象时,UIView内部会自动创建一个层(即CALayer对象),通过UIView的layer属性可以访问这个层。当UIView需要显示到屏幕上时,会调用drawRect:方法进行绘图,并且会将所有内容绘制在自己的层上,绘图完毕后,系统会将层拷贝到屏幕上,于是就完成了UIView的显示。UIView里面封装了关于layer的相关操作,每个UIview都会有一个默认的layer。

  2. 换句话说,UIView本身不具备显示的功能,是它内部的层才有显示功能。

  3. 上面已经说过了,UIView之所以能够显示,完全是因为内部的CALayer对象。因此,通过操作这个CALayer对象,可以很方便地调整UIView的一些界面属性,比如:阴影、圆角大小、边框宽度和颜色等。

  4. layer不能单独使用,必须依赖一个UIView,不能在layer上添加事件上面的CALayer中, 也是添加到了self.view中
    ( [self.view.layer addSublayer:layer])

区别

  1. UIView是可以响应点击事件的,但是CALayer不能响应事件
    继承于UIResponder, 所以可以响应点击事件
  • UIView继承于UIResponder
    @interface UIView : UIResponder <NSCoding, UIAppearance, UIAppearanceContainer, UIDynamicItem, UITraitEnvironment, UICoordinateSpace, UIFocusItem, CALayerDelegate>
  • CALayer继承于NSObject
    @interface CALayer : NSObject <NSSecureCoding, CAMediaTiming>
  1. UIView主要负责管理内容,而CALayer主要负责渲染和呈现。如果没有CALayer,我们是看不到内容的。
  2. CALayer维护着三个layer tree,分别是presentLayer Tree、modeLayer Tree、Render Tree,在做动画的时候,我们修改动画的属性,其实是修改presentLayer的属性值,而最终展示在界面上的其实是提供UIView的modelLayer。

UIView UIControl

UIControl:即控件

[self.view addSubview:label]: 就是把控件label添加到控制器自带的那个视图里面

UIControl是具有控制功能的视图.只要跟控制相关的控件都是其子类.UIControl是个抽象类,通常使用的时候用的都是其子类.

UIControl的子类有:UIButton,UITextField,UISlider,UISwitch,UIDatePicker,UIPageControl,UISegmentedControl

有这几个子类创建的对象均有

添加一个事件的方法:
addTarget:(id) target action:(SEL) forControlEvents:

移除一个事件的方法:
removeTarget:(id)target action:(SEL) forControlEvents:

UIControl事件实现原理

  • 比如我们有一个按钮,当他点击时候,我们执行ViewContollr的-(void)click:(id)sender方法,
  • 这里传入的UIControlEventTouchUpInside枚举量,就是在控件frame内按下,然后抬起这样一个事件,
  • UIContol将这个事件作为key,和目标(target)和目标方法(action)存到了自己私有的字典里。
  • 当用户点击按钮时,UIControl响应了触摸链的touchesEnded方法,便会根据私有字典,把对应UIControlEventTouchUpInside的目标(target)和目标方法(action)调用,这样完成事件的回传。

自定义控件可以怎样实现

继承自UIView,这是大多数开发者的做法。也能够实现需求,但是一个可交互控件,理应继承于UIControl而非UIView。继承自UIControl,使用UIControl的一套接口规范来实现自定义。

(继承于UIButton, 已经是一个Button, 在Button的基础上自定义一些内容, 而继承于UIControl, 还不是一个Button)

UIControl的重要方法

Target模式就是从UIControl使用的。

  • 准备并发送动作消息
    sendAction:to:forEvent:
响应给定的事件,转发一个动作消息给应用程序派发给目标。
- (void)sendAction:(SEL)`action`     to:(id)`target`       forEvent:(UIEvent*)`event`
- UIControl通过转发一个目标动作给单例UIApplication(在它的sendAction:to:fromSender:forEvent:方法中)来实现这个方法来派发给它的目标,
 或者如果它没有确定的目标,派发给响应链中第一个愿意处理这个消息的对象中。
 子类可能要重载这个方法来观察或者修改动作转发的行为。
 每进行一次指定控件的事件,可能会重复用sendActionsForControlEvents这个方法的实现方法

发送动作消息到给定的控制事件
sendActionsForControlEvents

- (void)sendActionsForControlEvents:(UIControlEvents *)controlEvents`
UIControl实现这个方法来发送所有controlEvents的动作消息,在进程中重复调用,
查找目标和动作列表在`addTarget:action:forControlEvents:`.之前构造。

在内部的派发表中给特殊时间添加一个目标和动作
addTarget:action:forControlEvents:

  - (void)addTarget:(id)`target`      action:(SEL)`action` forControlEvents:(UIControlEvents)`controlEvents`
- 你可能会多次调用这个方法,并且你可能需要为一个特殊的事件识别多个目标-动作组,
动作消息可以是可选的包含发送者和事件作为参数
当你调用这个方法的时候,目标没有被保留。
  • 在内部派发表中将特定事件的目标和动作移除
    removeTarget:action:forControlEvents:

  • 返回所有跟动作事件和特殊指定控制事件相关的动作
    actionsForTarget:forControlEvent:

- (NSArray *)actionsForTarget:(id)`target`   forControlEvent:( UIControlEvents)`controlEvent`
- 一个包含NSString类型的方法名字的数组或者没有与控制事件相关的方法则则为nil
  • 返回与接受者相关联的所有目标对象
- (NSSet*)allTargets
 集合里面的目标是动作消息的接受者,
  • 返回与接受者相关联的所有控制事件
- (UIControlEvents )allControlEvents
一个或者多个`UIControlEvents` 常量指定与当前接受者相关联的的控制事件

UIControlEvents枚举,定义了iOS交互中的交互方式

UIControlEventTouchDown    控件被按下去的事件
UIControlEventTouchDownRepeat  控件被重复点击的时间,点击次数超过一次
UIControlEventTouchDragInside  在控件范围内按下并拖动的事件
UIControlEventTouchDragOutside  在控件范围内按下并在控件外面拖动的事件
UIControlEventTouchDragEnter  从控件范围外拖动到控件范围内的事件
UIControlEventTouchDragExit  从控件范围外拖动到控件范围内的事件
UIControlEventTouchUpInside  点击控件后在控件范围内释放触发事件
UIControlEventTouchUpOutside  点击控件后在控件范围外释放触发事件
UIControlEventTouchCancel  触摸取消事件
UIControlEventValueChanged  当控件的值发生改变时,发送通知。用于滑块,分段控件,以及其他取值控件。
UIControlEventEditingDidBegin  文本控件开始编辑时发送通知
UIControlEventEditingChanged  文本控件中的内容被改变是发送通知
UIControlEventEditingDidEnd  文本控件结束编辑的时候发送通知
UIControlEventEditingDidEndOnExit  文本控件内通过按下回车(或等价行为)结束编辑时,发送通知。
UIControlEventAllTouchEvents  通知所有触摸事件
UIControlEventAllEditingEvents  通知所有关于文本编辑的时间。
UIControlEventApplicationReserved  为应用程序预留
UIControlEventSystemReserved  为系统内部框架预留
UIControlEventAllEvents  通知所有事件

UIControlState定义了控件的基本状态

typedef NS_OPTIONS(NSUInteger, UIControlState) {
UIControlStateNormal       = 0,
UIControlStateHighlighted  = 1 << 0,                  // used when UIControl isHighlighted is set
UIControlStateDisabled     = 1 << 1,
UIControlStateSelected     = 1 << 2,                  // flag usable by app (see below)
UIControlStateFocused NS_ENUM_AVAILABLE_IOS(9_0) = 1 << 3, // Applicable only when the screen supports focus
UIControlStateApplication  = 0x00FF0000,              // additional flags available for application use
UIControlStateReserved     = 0xFF000000               // flags reserved for internal framework use
};

绘制像素到屏幕上

原文链接 :链接

一些基本知识,方便理解文章
  • GPU Driver:直接和 GPU 交流的代码块。
    GPU:图形处理单元
  • OpenGL:(Open Graphics Library) 是一个提供了 2D 和 3D 图形渲染的 API。
  • CoreAnimation:是苹果提供的一套基于绘图的动画框架,下图是官方文档中给出的体系结构。
    在这里插入图片描述
  • VRAM:(Video RAM,影像随机接达记忆器),是显存的一种形式,作为影像绘图卡、显卡所使用的DRAM(内存),属于双埠随机存取内存,可让RAMDAC与影像处理同时存取。
  • RAM:随机存取存储器(英语:Random Access Memory,缩写:RAM),也叫主存,是与CPU直接交换数据的内部存储器。
  • RGBA:是代表Red(红色)Green(绿色)Blue(蓝色)和Alpha的色彩空间。
  • RGB:RGB色彩模式是工业界的一种颜色标准,是通过对红®、绿(G)、蓝(B)三个颜色通道的变化以及它们相互之间的叠加来得到各式各样的颜色的,RGB即是代表红、绿、蓝三个通道的颜色,这个标准几乎包括了人类视力所能感知的所有颜色,是目前运用最广的颜色系统之一。
  • opaque:CALayer的不透明属性
  • 软件组成
    在这里插入图片描述
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值