iOS学习笔记--触摸事件
最近空闲时间在学习iOS相关知识,几周没有更新文章了,今天总结下这些天的学习内容,也整理下iOS的学习笔记,以便以后查阅翻看~ 
iOS中的事件可以分为3大类型:
触摸事件
加速计事件
远程控制事件
响应者对象
在iOS中不是任何对象都能处理事件,只有继承了UIResponder的对象才能接收并处理事件。我们称之为“响应者对象”。 
UIApplication、UIViewController、UIView都继承自UIResponder,因此它们都是响应者对象,都能够接收并处理事件。
UIResponder内部提供了以下方法来处理事件
触摸事件(对应android的action_down、action_move、action_up、action_cancel) 
- (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;
加速计事件(有点像android的传感器) 
- (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的触摸事件处理
UIView是UIResponder的子类,可以覆盖下列4个方法处理不同的触摸事件 
一根或者多根手指开始触摸view,系统会自动调用view的下面方法 
- (void)touchesBegan:(NSSet )touches withEvent:(UIEvent )event
一根或者多根手指在view上移动,系统会自动调用view的下面方法(随着手指的移动,会持续调用该方法) 
- (void)touchesMoved:(NSSet )touches withEvent:(UIEvent )event
一根或者多根手指离开view,系统会自动调用view的下面方法 
- (void)touchesEnded:(NSSet )touches withEvent:(UIEvent )event
触摸结束前,某个系统事件(例如电话呼入)会打断触摸过程,系统会自动调用view的下面方法 
- (void)touchesCancelled:(NSSet )touches withEvent:(UIEvent )event
touches中存放的都是UITouch对象。
当用户用一根手指触摸屏幕时,会创建一个与手指相关联的UITouch对象,一根手指对应一个UITouch对象。
UITouch的作用 
保存着跟手指相关的信息,比如触摸的位置、时间、阶段。当手指移动时,系统会更新同一个UITouch对象,使之能够一直保存该手指在的触摸位置,当手指离开屏幕时,系统会销毁相应的UITouch对象。
UITouch的属性
触摸产生时所处的窗口
@property(nonatomic,readonly,retain) UIWindow    *window;
触摸产生时所处的视图
@property(nonatomic,readonly,retain) UIView      *view;
短时间内点按屏幕的次数,可以根据tapCount判断单击、双击或更多的点击
@property(nonatomic,readonly) NSUInteger    tapCount;
记录了触摸事件产生或变化时的时间,单位是秒
@property(nonatomic,readonly) NSTimeInterval      timestamp;
当前触摸事件所处的状态
@property(nonatomic,readonly) UITouchPhase phase;
- (CGPoint)locationInView:(UIView *)view;
返回值表示触摸在view上的位置
这里返回的位置是针对view的坐标系的(以view的左上角为原点(0, 0))
调用时传入的view参数为nil的话,返回的是触摸点在UIWindow的位置
- (CGPoint)previousLocationInView:(UIView *)view;
该方法记录了前一个触摸点的位置自定义控件拖拽效果
ios:
1.自定义view继承自UIView 
2.覆盖touchesMoved实现具体逻辑
- (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
    //取得UITouch对象
    UITouch *touch = [touches anyObject];
    //获取上一个点
    CGPoint p = [touch previousLocationInView:self];
    //获取当前点
    CGPoint currp = [touch locationInView:self];
    //产生平移拖拽效果
    self.transform = CGAffineTransformTranslate(self.transform, currp.x - p.x, currp.y - p.y);
}android:
1.自定义view继承自View 
2.覆盖onTouchEvent实现具体逻辑
private int x, y;
@Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()){
            case MotionEvent.ACTION_DOWN:
                x = (int) event.getRawX();
                y = (int) event.getRawY();
                break;
            case MotionEvent.ACTION_MOVE:
                int mx = (int)event.getRawX();
                int my = (int)event.getRawY();
//                setTranslationX(getTranslationX()+mx-x);
//                setTranslationY(getTranslationY()+my-y);
                layout(getLeft()+mx-x,getTop()+my-y,getLeft()+getMeasuredWidth()+mx-x,getTop()+getMeasuredHeight()+my-y);
                x = mx;
                y = my;
                break;
        }
        return true;
    }UIGestureRecognizer
iOS 3.2之后,苹果推出了手势识别功能(Gesture Recognizer),在触摸事件处理方面,利用UIGestureRecognizer,能轻松识别用户在某个view上面做的一些常见手势。
UIGestureRecognizer
UIGestureRecognizer是一个抽象类,定义了所有手势的基本行为,使用它的子类才能处理具体的手势
UITapGestureRecognizer(敲击)
UIPinchGestureRecognizer(捏合,用于缩放)
UIPanGestureRecognizer(拖拽)
UISwipeGestureRecognizer(轻扫)
UIRotationGestureRecognizer(旋转)
UILongPressGestureRecognizer(长按)手势识别器的用法相似,比如UITapGestureRecognizer的使用步骤如下:
//创建手势识别器对象
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] init];
//设置手势识别器对象的具体属性
// 连续敲击2次
tap.numberOfTapsRequired = 2;
// 需要2根手指一起敲击
tap.numberOfTouchesRequired = 2;
//添加手势识别器到对应的view上
[self.iconView addGestureRecognizer:tap];
监听手势的触发
[tap addTarget:self action:@selector(tapIconView:)];上面的拖拽效果也可以用这种方式实现:
//页面加载完成时调用
- (void)viewDidLoad {
    [super viewDidLoad];
    //创建手势识别器对象
     UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(pan:)];
    //添加手势识别器到对应的view上
    [self.imageView addGestureRecognizer:pan];
}
- (void)pan:(UIPanGestureRecognizer *)pan
{
    // 获取手势的触摸点
   // CGPoint curP = [pan locationInView:self.imageView];
    // 获取手势的移动,也是相对于最开始的位置
    CGPoint transP = [pan translationInView:self.imageView];
    self.imageView.transform = CGAffineTransformTranslate(self.imageView.transform, transP.x, transP.y);
    // 复位
    [pan setTranslation:CGPointZero inView:self.imageView];
    //判断当前手指状态
    //if (pan.state == UIGestureRecognizerStateBegan) {//手指按下时类似于Android中的ACTION_DOWN
    //}
}其中用pan.state对应UIGestureRecognizerState有如下几种状态:
// 没有触摸事件发生,所有手势识别的默认状态
    UIGestureRecognizerStatePossible,
    // 一个手势已经开始但尚未改变或者完成时
    UIGestureRecognizerStateBegan,
    // 手势状态改变
    UIGestureRecognizerStateChanged,
    // 手势完成
    UIGestureRecognizerStateEnded,
    // 手势取消,恢复至Possible状态
    UIGestureRecognizerStateCancelled,
    // 手势失败,恢复至Possible状态
    UIGestureRecognizerStateFailed,
    // 识别到手势识别
    UIGestureRecognizerStateRecognized = UIGestureRecognizerStateEnded 注意:默认不支持多个手势,也就是默认不能同时缩放和旋转的,如果要支持多个手势需要实现UIGestureRecognizerDelegate代理方法:shouldRecognizeSimultaneouslyWithGestureRecognizer方法:
#pragma mark - 手势代理方法
// 是否允许开始触发手势
//- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer
//{
//    return NO;
//}
// 是否允许同时支持多个手势,默认是不支持多个手势
// 返回yes表示支持多个手势
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
{
    return YES;
}
// 是否允许接收手指的触摸点
//- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch{
//    // 获取当前的触摸点
//    CGPoint curP = [touch locationInView:self.imageView];
//        return YES;
//}事件传递
一个view怎么不能处理事件:
userInteractionEnabled = NO,hidden = YES,alpha <= 0.01UIImageView默认不允许用户交互,因此默认它上面的子控件不能接收事件。
通过递归找到最合适的view
第一个接收事件的控件是窗口,当事件传递给窗口的时候,就会让窗口去找最合适的view,
1. 判断自己能不能接收事件
2. 点在不在窗口上
3. 去找比自己更合适的view,从后往前遍历子控件,拿到子控件后,把事件传递给这个子控件
4. 子控件拿到事件之后,又会做同样的判断,一直递归去找,直到找到最合适的view.
事件传递的目的在于要找到最合适的view,把事件交给他。
hitText方法和pointInside方法
// 事件传递的时候调用
// 当事件传递给控件的时候,就会调用控件的这个方法,寻找最合适的view
// point:当前的触摸点,point这个点的坐标系就是方法调用者
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
    // 调用系统的做法去寻找最合适的view,返回最合适的view
    UIView *fitView = [super hitTest:point withEvent:event];
//    NSLog(@"fitView--%@",fitView);
    return fitView;
}
// 作用:判断当前这个点在不在方法调用者(控件)上
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
{
    return YES;
}hitTest的底层实现
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
    // 1.判断当前控件能否接收事件
    if (self.userInteractionEnabled == NO || self.hidden == YES || self.alpha <= 0.01) return nil;
    // 2. 判断点在不在当前控件
    if ([self pointInside:point withEvent:event] == NO) return nil;
    // 3.从后往前遍历自己的子控件
    NSInteger count = self.subviews.count;
    for (NSInteger i = count - 1; i >= 0; i--) {
        UIView *childView = self.subviews[i];
        // 把当前控件上的坐标系转换成子控件上的坐标系
        CGPoint childP = [self convertPoint:point toView:childView];
        UIView *fitView = [childView hitTest:childP withEvent:event];
        if (fitView) { // 寻找到最合适的view
            return fitView;
        }
    }
    // 循环结束,表示没有比自己更合适的view
    return self;
}
- 判断窗口能不能处理事件? 如果不能,意味着窗口不是最合适的view,而且也不会去寻找比自己更合适的view,直接返回nil,通知UIApplication,没有最合适的view。
- 判断点在不在窗口
- 遍历自己的子控件,寻找有没有比自己更合适的view
- 如果子控件不接收事件,意味着子控件没有找到最合适的view,然后返回nil,告诉窗口没有找到更合适的view,窗口就知道没有比自己更合适的view,就自己处理事件。
响应者链的事件传递过程
touch的默认做法:自己不处理,交给上一个响应者。
上一个响应者默认是父控件
- 如果view的控制器存在,就传递给控制器;如果控制器不存在,则将其传递给它的父视图
- 在视图层次结构的最顶级视图,如果也不能处理收到的事件或消息,则其将事件或消息传递给window对象进行处理
- 如果window对象也不处理,则其将事件或消息传递给UIApplication对象
- 如果UIApplication也不能处理该事件或消息,则将其丢弃
iOS学习笔记--触摸事件的更多相关文章
- iOS学习之触摸事件
		触摸事件 iOS中的事件: 在用户使用app过程中,会产生各种各样的事件.iOS中的事件可以分为3大类型: view的触摸事件处理: 响应者对象: 在iOS中不是任何对象都能处理事件,只有继承了UIR ... 
- Android学习笔记触摸事件
		案例代码: activity_main.xml <?xml version="1.0" encoding="utf-8"?> <Relativ ... 
- iOS学习笔记之触摸事件&UIResponder
		iOS学习笔记之触摸事件&UIResponder 触摸事件 与触摸事件相关的四个方法如下: 一根手指或多根手指触摸屏幕 -(void)touchesBegan:(NSSet *)touches ... 
- iOS学习笔记-精华整理
		iOS学习笔记总结整理 一.内存管理情况 1- autorelease,当用户的代码在持续运行时,自动释放池是不会被销毁的,这段时间内用户可以安全地使用自动释放的对象.当用户的代码运行告一段 落,开始 ... 
- iOS学习笔记总结整理
		来源:http://mobile.51cto.com/iphone-386851_all.htm 学习IOS开发这对于一个初学者来说,是一件非常挠头的事情.其实学习IOS开发无外乎平时的积累与总结.下 ... 
- Android笔记:触摸事件的分析与总结----TouchEvent处理机制
		原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处 .作者信息和本声明.否则将追究法律责任.http://glblong.blog.51cto.com/3058613/1559320 ... 
- IOS学习笔记(四)之UITextField和UITextView控件学习
		IOS学习笔记(四)之UITextField和UITextView控件学习(博客地址:http://blog.csdn.net/developer_jiangqq) Author:hmjiangqq ... 
- iOS学习笔记之UITableViewController&UITableView
		iOS学习笔记之UITableViewController&UITableView 写在前面 上个月末到现在一直都在忙实验室的事情,与导师讨论之后,发现目前在实验室完成的工作还不足以写成毕业论 ... 
- IOS学习笔记48--一些常见的IOS知识点+面试题
		IOS学习笔记48--一些常见的IOS知识点+面试题 1.堆和栈什么区别? 答:管理方式:对于栈来讲,是由编译器自动管理,无需我们手工控制:对于堆来说,释放工作由程序员控制,容易产生memor ... 
随机推荐
- Redis、Memcache与MongoDB的区别
			>>Memcached Memcached的优点:Memcached可以利用多核优势,单实例吞吐量极高,可以达到几十万QPS(取决于key.value的字节大小以及服务器硬件性能,日常环境 ... 
- 学习HTML的第二次课
			1.图片 <img / > 图片的格式: 1.1BMP 占用空间大,颜色丰富. 1.2JPEG 有损压缩,占用空间较小. 1.3GIF 多帧动图,支持透明色. 1.4PNG 无损压缩,位图 ... 
- 关于Goldwell平台推出赠金及手数奖励
			关于Goldwell平台推出赠金及手数奖励 Goldwell平台是一家拥有30多年现货黄金经验平台,平台位于柬埔寨金边,是一家国际衍生品的经纪公司.Goldwell平台它对柬埔寨金融市场和客户绝对的承 ... 
- [LeetCode] Dota2 Senate 刀塔二参议院
			In the world of Dota2, there are two parties: the Radiant and the Dire. The Dota2 senate consists of ... 
- [LeetCode] Encode and Decode TinyURL 编码和解码精简URL地址
			Note: This is a companion problem to the System Design problem: Design TinyURL. TinyURL is a URL sho ... 
- [FJOI2014]最短路径树问题
			Description 给一个包含n个点,m条边的无向连通图.从顶点1出发,往其余所有点分别走一次并返回. 往某一个点走时,选择总长度最短的路径走.若有多条长度最短的路径,则选择经过的顶点序列字典序最 ... 
- 计蒜客NOIP模拟赛(2) D1T1邻家男孩
			凡是一个具有领导力的孩子.现实生活中他特别喜欢玩一个叫做 UNO 的纸牌游戏,他也总是带着其他小朋友一起玩,然后战胜他们.慢慢地,他厌倦了胜利,于是准备发明一种新的双人纸牌游戏. 初始时,每个人手中都 ... 
- hdu 3948 后缀数组
			The Number of Palindromes Time Limit: 6000/3000 MS (Java/Others) Memory Limit: 262144/262144 K (J ... 
- 习题9-8 uva1631
			题意: 给你一串密码,每次我们可以转动1-3个数字,求转出最终答案的最小步数 思路: 感觉自己好坑,最开始想的是dp[cur][t1][t2][t3]也就是t1的位置以及连续的三个数的状态 但是卡死循 ... 
- C语言程序设计第四次作业——选择结构(二)
			(一)改错题 错误信息: 错误原因:第13行sqrt数学函数缺少")",导致编译器无法将括号正确配对 改正方法:补齐缺少的")" 错误信息: 错误原因:if语句 ... 
