iOS中的事件分发

事件的分类

  1. Touch Events(多点触摸事件)

    • touchesBegan:withEvent:方法:一个或多个手指置于视图或窗口上
    • touchesMoved:withEvent:方法:一个或多个手指在移动
    • touchesEnded:withEvent:方法:一个或多个手指离开视图或窗口
    • touchesCancelled:withEvent:方法:如果其他系统事件(如内存不足警告)使得触摸被取消
  2. Motion Events(运动事件)

    • motionBegan:withEvent:方法:运动事件开始
    • motionEnded:withEvent:方法:运动事件结束
    • motionCancelled:withEvent:方法:运动事件取消

    说明:如果要摇一摇之类的应用,就可以使用运动事件进行处理,代码如下所示。

    
    #import "ViewController.h"#import <AVFoundation/AVFoundation.h>@interface ViewController () {BOOL acceptNextShake;
    }@end@implementation ViewController// 封装一个播放段音频的方法
    - (void)playSoundEffect:(NSString *)name withCallback:(void (*)(SystemSoundID, void *)) callback {NSString *audioFile = [[NSBundle mainBundle] pathForResource:name ofType:nil];NSURL *fileUrl = [NSURL fileURLWithPath:audioFile];SystemSoundID soundID;// 在系统中创建一个音效对象并获得其唯一IDAudioServicesCreateSystemSoundID((__bridge CFURLRef)(fileUrl), &soundID);// 注册在播放完之后执行的回调函数// 第二个和第三个参数跟循环播放有关// 第五个参数是指向传给回调函数的数据的指针AudioServicesAddSystemSoundCompletion(soundID, NULL, NULL, callback, NULL);// 播放音效AudioServicesPlaySystemSound(soundID);// 播放音效并震动// AudioServicesPlayAlertSound(soundID);
    }// 摇一摇开始播放金币掉下的短音频
    - (void)motionBegan:(UIEventSubtype)motion withEvent:(UIEvent *)event {if(acceptNextShake) {acceptNextShake = NO;[self playSoundEffect:@"4679.mp3" withCallback:nil];// 阻止播放音效文件5秒钟dispatch_async(dispatch_get_global_queue(0, 0), ^{sleep(5);acceptNextShake = YES;});}
    }- (void)viewDidLoad {[super viewDidLoad];acceptNextShake = YES;
    }@end
  3. Remote-Control Events(远程控制事件)

    • remoteControlReceivedWithEvent:方法:接收到远程控制事件

事件概述

  事件是当用户手指触击屏幕及在屏幕上移动时,系统将事件按照特定的路径传递给可以对其进行处理的对象。在iOS中,一个UITouch对象表示一个触摸,一个UIEvent对象表示一个事件。事件对象中包含与当前多点触摸序列相对应的所有触摸对象,还可以提供与特定视图或窗口相关联的触摸对象。

  响应者对象是可以响应事件并对其进行处理的对象。UIResponder是所有响应者对象的基类,它不仅为事件处理,而且也为常见的响应者行为定义编程接口。UIApplication、UIView和所有从UIView派生出来的UIKit类(包括UIWindow)都直接或间接地继承自UIResponder类。第一响应者是应用程序中当前负责接收触摸事件的响应者对象(通常是一个UIView对象)。UIWindow对象以消息的形式将事件发送给第一响应者,使其有机会首先处理事件。如果第一响应者没有进行处理,系统就将事件(通过消息)传递给响应者链中的下一个响应者,看看它是否可以进行处理。

事件的分发机制

问题:当有多个重叠的UIView时,谁是第一响应者

  1. 默认的点击顺序是按照UIView中subviews的逆顺序
  2. 如果UIView的同级别subviews中有重叠的部分,则优先检查顶部的subview,如果顶部的subview返回nil,再检查底部的subview。

请看下面的例子:

#import <UIKit/UIKit.h>@interface CDMyView : UIView@property (nonatomic, copy) NSString *name;@end
#import "CDMyView.h"@implementation CDMyView- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {NSLog(@"%@", event);NSLog(@"%@", self.name);
}
@end
#import <UIKit/UIKit.h>@interface CDThyView : UIView@property (nonatomic, copy) NSString *name;@end
#import "CDThyView.h"@implementation CDThyView@end
#import "ViewController.h"
#import "CDMyView.h"
#import "CDThyView.h"@interface ViewController ()@end@implementation ViewController- (void)viewDidLoad {[super viewDidLoad];// Do any additional setup after loading the view, typically from a nib.CDMyView *view1 = [[CDMyView alloc] initWithFrame:CGRectMake(50, 50, 275, 500)];view1.backgroundColor = [UIColor redColor];view1.name = @"view1";CDMyView *view2 = [[CDMyView alloc] initWithFrame:CGRectMake(20, 20, 230, 200)];view2.backgroundColor = [UIColor orangeColor];view2.name = @"view2";CDThyView *view3 = [[CDThyView alloc] initWithFrame:CGRectMake(20, 240, 230, 320)];view3.backgroundColor = [UIColor yellowColor];view3.name = @"view3";CDMyView *view4 = [[CDMyView alloc] initWithFrame:CGRectMake(20, 20, 100, 150)];view4.backgroundColor = [UIColor greenColor];view4.name = @"view4";CDMyView *view5 = [[CDMyView alloc] initWithFrame:CGRectMake(20, 40, 240, 60)];view5.backgroundColor = [UIColor cyanColor];view5.name = @"view5";[self.view addSubview:view1];[view1 addSubview:view2];[view1 addSubview:view3];[view2 addSubview:view4];[view4 addSubview:view5];
}
@end

  试着点击屏幕上圈出来的几个区域,看看会发生什么并解释为什么。

响应者链处理原则

  1. 点击检测视图或者第一响应者传递事件或动作消息给它的视图控制器(如果它有的话);如果没有一个视图控制器,就传递给它的父视图。
  2. 如果一个视图或者它的视图控制器不能处理这个事件或动作消息,它将传递给该视图的父视图。
  3. 在这个视图层次中的每个后续的父视图遵循上述的模式,如果它不能处理这个事件或动作消息的话。
  4. 最顶层的视图如果不能处理这个事件或动作消息,就传递给UIWindow对象来处理。
  5. 如果UIWindow 对象不能处理,就传给单件应用程序对象UIApplication。
  6. 如果应用程序对象也不能处理这个事件或动作消息,将抛弃它。

iOS中的手势操作

  手势操作是通过使用者的手指触控,计算手指移动轨迹、坐标,然后对程序做相应处理的过程。手势操作需要添加手势识别器,所有手势识别器公共的父类是UIGestureRecognizer。

各种手势的使用

  • 点击手势:UITapGestureRecognizer

  • 长按手势:UILongPressGestureRecognizer

  • 旋转手势:UIRotationGestureRecognizer

  • 捏合手势:UIPinchGestureRecognizer

  • 滑动手势:UISwipeGestureRecognizer

  • 拖动手势:UIPanGestureRecognizer

  下面的例子演示了如何使用手势操作实现图片的旋转和缩放以及如何通过长按手势在窗口上添加一个新的视图(一个篮球图片字符),运行效果如下图所示。

  

#import "ViewController.h"@interface ViewController () <UIGestureRecognizerDelegate> {UIImageView *imageView;
}@end@implementation ViewController- (void)viewDidLoad {[super viewDidLoad];self.view.backgroundColor = [UIColor blackColor];// 创建UIImage对象并获得其尺寸UIImage *image = [UIImage imageNamed:@"Dudu.jpg"];CGSize imageSize = image.size;// 用UIImage创建UIImageView并执行其位置和尺寸imageView = [[UIImageView alloc] initWithImage:image];CGRect rect = self.view.bounds;imageView.frame = CGRectMake((rect.size.width - imageSize.width) / 2, (rect.size.height - imageSize.height) / 2, imageSize.width, imageSize.height);// 设置允许用户交互行为imageView.userInteractionEnabled = YES;// 创建捏合手势识别器并添加到上面的UIImageView上UIPinchGestureRecognizer *r1 = [[UIPinchGestureRecognizer alloc] initWithTarget:self action:@selector(doPinch:)];[imageView addGestureRecognizer:r1];// 给手势识别器绑定委托r1.delegate = self;// 创建旋转手势识别器并添加到上面的UIImageView上UIRotationGestureRecognizer *r2 = [[UIRotationGestureRecognizer alloc] initWithTarget:self action:@selector(doRotate:)];[imageView addGestureRecognizer:r2];// 给手势识别器绑定委托r2.delegate = self;// 连续3次点击删除图片UITapGestureRecognizer *r3 = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(doRemove)];// 设置需要3次点击r3.numberOfTapsRequired = 3;[imageView addGestureRecognizer:r3];// 添加一个长按手势识别器UILongPressGestureRecognizer *r4 = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(doAddBall:)];r4.minimumPressDuration = 1;    // 至少按住1秒钟[self.view addGestureRecognizer:r4];[self.view addSubview:imageView];
}- (void) doPinch:(UIPinchGestureRecognizer *) sender {// 通过放射变换实现图片缩放imageView.transform = CGAffineTransformScale(imageView.transform, sender.scale, sender.scale);// 防止效果叠加sender.scale = 1;
}- (void) doRotate:(UIRotationGestureRecognizer *) sender {// 通过放射变换实现图片旋转imageView.transform = CGAffineTransformRotate(imageView.transform, sender.rotation);// 防止效果叠加sender.rotation = 0;
}- (void) doRemove {[imageView removeFromSuperview];
}- (void) doAddBall:(UILongPressGestureRecognizer *) sender {// 对手势做一个状态判断:只有在刚识别到长按手势时才绘制篮球if(sender.state == UIGestureRecognizerStateBegan) {// 获取长按的位置在屏幕上对应的点CGPoint point = [sender locationInView:self.view];UIView *tempView = [[[NSBundle mainBundle] loadNibNamed:@"CDBallView" owner:self options:nil] lastObject];// 将手指按下的位置作为新视图的中心点tempView.center = point;[self.view addSubview:tempView];}
}// 同时支持多个手势操作的回调方法
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {// 有没有什么情况只能支持一个手势呢?return YES;
}@end

  上面的例子中使用的XIB文件如下图所示:

  手势识别器也可以设置委托(UIGestureRecognizerDelegate)来支持下面的操作:

  1. 两个手势可以同时被认可

    - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer;
  2. 手势与控件冲突

    - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch;

iOS开发系列课程(08) --- 事件处理详解和手势操作相关推荐

  1. iOS 开发:『Runtime』详解(二)Method Swizzling

    本文用来介绍 iOS 开发中『Runtime』中的黑魔法Method Swizzling. 通过本文,您将了解到: Method Swizzling(动态方法交换)简介 Method Swizzlin ...

  2. iOS开发系列课程(01) --- iOS编程入门

    iOS概述 什么是iOS   iOS是苹果公司为它的移动设备(iPhone.iPad.iWatch等)开发的移动操作系统. iOS发展史 2007年苹果发布iPhone Runs OS X 2008年 ...

  3. (0054)iOS开发之制作静态库详解

    最近在做Apple的IOS开发,有开发静态库的需求,下面就简单写一个静态库和一个调用静态库的例子. 静态库的编写: 1. 静态库工程的建立: Xcode New一个新的project,选择IOS下面的 ...

  4. iOS开发 蓝牙技术4.0详解

    前言 前端时间,同学在做项目过程中遇到关于蓝牙方面的问题,今天我就给大家进行详细的进行讲解下蓝牙在iOS开发中的具体实现.在介绍蓝牙前,大家要搞清楚什么是蓝牙? 什么是蓝牙? 随着蓝牙低功耗技术BLE ...

  5. 淘宝模板开发系列之开放接口详解

    SDK是通过php方式对外提供接口的,详细的接口信息,请阅读SDK安装目录下的htdocs/dcsdk_functions.php,提供了全局变量.函数和相关的接口. 目录 [隐藏] 1 卖家数据 1 ...

  6. iOS开发系列课程(10) --- 表格视图

      UITableView(表格视图)是iOS应用程序开发中应用最广泛的一个控件,几乎十有八九的应用程序中都会用到它,它是基于滚动视图的列表互动类.使用UITableView可以在屏幕上显示单元格的列 ...

  7. iOS开发-Apple开发者账号详解

    证书类型 账号类型 价格 可以发布AppStore? 支持安装设备数量 申请条件 证书类型 个人账号 $99 √ 500 无限制 Ad Hoc, App Store 公司账号 $99 √ 500 DU ...

  8. IOS开发网络篇之──ASIHTTPRequest详解

    目录 目录 发起一个同步请求 创建一个异步请求 队列请求 请求队列上下文 ASINetworkQueues, 它的delegate提供更为丰富的功能 取消异步请求 安全的内存回收建议 向服务器端上传数 ...

  9. iOS 开发 二维码扫描详解

    1.扫描二维码的控制器.m代码 #import "JDGScanQRcodeController.h" #import <AVFoundation/AVFoundation. ...

最新文章

  1. SAP MM 并非奇怪现象之MB5B报表里期初库存余额或者期末库存余额为负数?
  2. ScrollView嵌套ListView处理事件冲突
  3. MFC接收命令行参数的三种方法
  4. Android - Intentservice源码解析
  5. android获取imei兼容_Android获取IMEI号码
  6. jQuery:插入,复制,替换和删除节点
  7. python怎么读写文件-一文看懂Python文件的读取写入操作,建议收藏-bak文件怎么打开...
  8. python分位点计算(正态分布,卡方分布,t分布,F分布)
  9. 洛谷 P5056 【模板】插头dp
  10. 汪磊Node基础、Node入门、黑马Node
  11. select *和select 字段名
  12. 2014年南京航空航天大学计算机学院推荐研究生公示,南京航空航天大学2013-2014学年研究生评优评奖公示...
  13. PYTHON 之 多线程 and 多进程
  14. 【泛微表单】请假流程中计算请假时长(非系统自带考勤类型)
  15. 【计算机网络】网络层精选习题1(含联考真题)
  16. Android 自定义View java.lang.RuntimeException: Unable to start activity ComponentInfo
  17. 屏幕录制和编辑神器ScreenFlow轻松上手
  18. 【完整记录】使用kubeadm部署kubernetes集群踩坑记录及解决方案
  19. 计算机电子工程论文,电子信息工程论文范文
  20. 解决阿里云OSS,打开图片地址无法预览,直接下载

热门文章

  1. socket编程流程及函数详解
  2. excel快速拆分合并的单元格并填充数据的方法
  3. mysql 5.6 federated_Mysql 开启Federated引擎以及使用
  4. MySQL的相关面试题:
  5. 由ORA-12519、ORA-12520错误进而修改oracle的最大连接数
  6. 如何稳定高效利用GOOGLE搜索
  7. 导出excel测试-excel行列限制
  8. 策略产品经理学习 魏政 第一章学习
  9. MAIL(POSTFIX-SMTPS DOVECOT-IMAPS)
  10. 英国雕塑家获特殊颜料专利