iOS开发之触摸事件以及手势

iOS中的事件分为三类:触摸事件、加速计事件、远程控制事件。只有继承了UIResponder的对象才能接收并处理事件,称之为“响应者对象”。UIApplication、UIViewController、UIView都继承自UIResponder。UIResponder内部提供的方法来处理事件:

触摸事件:touchesBegan、touchesMoved、touchesEnded、touchesCancelled

加速计事件:motionBegan、motionEnded、motionCancelled

远程控制事件:remoteControlReceivedWithEvent

UIVeiw的触摸事件处理过程:

/**
 * 当手指开始触摸view时调用
 *
 * @param touches <#touches description#>
 * @param event  <#event description#>
 */
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {

  NSLog(@"%s",__func__);
}

/**
 * 当手指在view上移动时调用
 *
 * @param touches <#touches description#>
 * @param event  <#event description#>
 */
- (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
  NSLog(@"%s",__func__);
}

/**
 * 当手指离开view时调用
 *
 * @param touches <#touches description#>
 * @param event  <#event description#>
 */
- (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {

  NSLog(@"%s",__func__);
}

/**
 * 当触摸事件被系统事件打断时调用
 *
 * @param touches <#touches description#>
 * @param event  <#event description#>
 */
- (void)touchesCancelled:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {

  NSLog(@"%s",__func__);
}

一次触摸动作必然会调用touchesBeagn、touchesMoved和touchesEnded这三个方法。

说到这几个触摸方法,首先要知道UITouch这个对象。当一根手指触摸屏幕时就会产生一个与之关联的UITouch对象,一根手指对应一个UITouch对象。这个对象里面保存着这次触摸的信息,比如触摸的位置,时间,阶段等,当手指移动时,系统会更新同一个UITouch对象。使其能一直保存该手指所在的触摸位置信息。当手指离开屏幕时,系统会销毁对应的UITouch对象。

@interface UITouch : NSObject

@property(nonatomic,readonly) NSTimeInterval   timestamp;
@property(nonatomic,readonly) UITouchPhase    phase;
@property(nonatomic,readonly) NSUInteger     tapCount;  // touch down within a certain point within a certain amount of time

// majorRadius and majorRadiusTolerance are in points
// The majorRadius will be accurate +/- the majorRadiusTolerance
@property(nonatomic,readonly) CGFloat majorRadius NS_AVAILABLE_IOS(8_0);
@property(nonatomic,readonly) CGFloat majorRadiusTolerance NS_AVAILABLE_IOS(8_0);

@property(nullable,nonatomic,readonly,strong) UIWindow            *window;
@property(nullable,nonatomic,readonly,strong) UIView             *view;
@property(nullable,nonatomic,readonly,copy)  NSArray <UIGestureRecognizer *> *gestureRecognizers NS_AVAILABLE_IOS(3_2);

//获取当前位置
- (CGPoint)locationInView:(nullable UIView *)view;
//获取上一个触摸点的位置
- (CGPoint)previousLocationInView:(nullable UIView *)view;

// Force of the touch, where 1.0 represents the force of an average touch
@property(nonatomic,readonly) CGFloat force NS_AVAILABLE_IOS(9_0);
// Maximum possible force with this input mechanism
@property(nonatomic,readonly) CGFloat maximumPossibleForce NS_AVAILABLE_IOS(9_0);

@end

eg:让一个view随着手指的移动而移动

/**
 * 当手指在view上移动时调用
 *
 * @param touches <#touches description#>
 * @param event  <#event description#>
 */
- (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
  NSLog(@"%s",__func__);

  //获取UITouch对象
  UITouch *touch = [touches anyObject];

  //获取当前点的位置
  CGPoint curP = [touch locationInView:self];

  //获取上一个点的位置
  CGPoint preP = [touch previousLocationInView:self];

  //计算x的偏移量
  CGFloat offsetX = curP.x - preP.x;

  //计算y的偏移量
  CGFloat offsetY = curP.y = preP.y;

  //修改view的位置
  self.transform = CGAffineTransformTranslate(self.transform, offsetX, offsetY);
}

就是根据UITouch对象中保存的位置信息来实现的。

事件的产生和传递:

当触摸事件产生后,系统会将该事件添加到一个由UIApplication管理的事件队列中去。UIApplication会从队列中取出最前面的事件,发送给应用程序的主窗口的处理。主窗口会在视图层次结构中,找一个最合适的视图并调用touches方法来处理触摸事件。触摸事件的传递是从父控件传递到子控件。如果父控件不能接收到触摸事件,那么子控件就不可能 接收到触摸事件。

如何找到最合适的控件来处理事件?首先判断自己是否能接收触摸事件?触摸点是否在自己身上?从后往前遍历子控件,重复之前的两个步骤,如果没有符合条件的子控件,那么就自己最合适处理。

控件用hitTest:withEvent:方法来寻找最合适的view,用pointInside这个方法判断这个点在不在方法调用者即控件身上。

hitTest方法的底层实现:

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {

  //判断当前控件是否能接收触摸事件
  if (self.userInteractionEnabled == NO || self.hidden == YES || self.alpha <= 0.01) {
    return nil;
  }

  //判断触摸点是否在当前控件上
  if ([self pointInside:point withEvent:event] == NO) {
    return nil;
  }

  //从后往前遍历自己的子控件
  NSInteger count = self.subviews.count;
  for (NSInteger i = count - 1; i >= 0; i--) {
    UIView *childView = self.subviews[i];

    //把当前控件上的坐标系转换成子控件上的坐标系
    CGPoint childPoint = [self convertPoint:point toView:childView];

    //递归调用hitTest方法寻找最合适的view
    UIView *fitView = [childView hitTest:childPoint withEvent:event];

    if (fitView) {
      return fitView;
    }
  }

  //循环结束,没有比自己更合适的view,返回自己
  return self;

}

然而使用touches方法监听触摸事件是有缺点的,比如要自定义view,所以iOS3.2之后苹果推出了手势识别功能UIGestureRecognizer。UIGestureRecognizer是一个抽象类,它的子类才能处理具体的某个手势。

具体有以下几种手势:

//点按手势
//  UITapGestureRecognizer *tap = [UITapGestureRecognizer alloc]initWithTarget:<#(nullable id)#> action:<#(nullable SEL)#>

  //长按手势 默认是触发两次
//  UILongPressGestureRecognizer *longP = [UILongPressGestureRecognizer alloc]initWithTarget:<#(nullable id)#> action:<#(nullable SEL)#>

  //轻扫手势 默认方向是往右
//  UISwipeGestureRecognizer *swipe = [UISwipeGestureRecognizer alloc]initWithTarget:<#(nullable id)#> action:<#(nullable SEL)#>

  //旋转手势
//  UIRotationGestureRecognizer *rotation = [UIRotationGestureRecognizer alloc]initWithTarget:<#(nullable id)#> action:<#(nullable SEL)#>

  //捏合手势
//  UIPinchGestureRecognizer *pinch = [UIPinchGestureRecognizer alloc]initWithTarget:<#(nullable id)#> action:<#(nullable SEL)#>

  //拖拽手势
//  UIPanGestureRecognizer *pan = [UIPanGestureRecognizer alloc]initWithTarget:<#(nullable id)#> action:<#(nullable SEL)#>

实际运用:

@interface ViewController ()<UIGestureRecognizerDelegate>
@property (weak, nonatomic) IBOutlet UIImageView *imageView;

@end

@implementation ViewController

- (void)viewDidLoad {
  [super viewDidLoad];

  [self setUpPinch];

  [self setUpRotation];

  [self setUpPan];

}
#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];
//
//  if (curP.x < self.imageView.bounds.size.width * 0.5) {
//    return NO;
//  }else{
//    return YES;
//  }
//}

#pragma mark - 点按手势

- (void)setUpTap
{
  // 创建点按手势
  UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tap:)];

  tap.delegate = self;

  [_imageView addGestureRecognizer:tap];
}

- (void)tap:(UITapGestureRecognizer *)tap
{
  NSLog(@"%s",__func__);
}

#pragma mark - 长按手势
// 默认会触发两次
- (void)setUpLongPress
{
  UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(longPress:)];

  [self.imageView addGestureRecognizer:longPress];
}

- (void)longPress:(UILongPressGestureRecognizer *)longPress
{

  if (longPress.state == UIGestureRecognizerStateBegan) {

    NSLog(@"%s",__func__);
  }
}

#pragma mark - 轻扫
- (void)setUpSwipe
{
  // 默认轻扫的方向是往右
  UISwipeGestureRecognizer *swipe = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(swipe)];

  swipe.direction = UISwipeGestureRecognizerDirectionUp;

  [self.imageView addGestureRecognizer:swipe];

  // 如果以后想要一个控件支持多个方向的轻扫,必须创建多个轻扫手势,一个轻扫手势只支持一个方向
  // 默认轻扫的方向是往右
  UISwipeGestureRecognizer *swipeDown = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(swipe)];

  swipeDown.direction = UISwipeGestureRecognizerDirectionDown;

  [self.imageView addGestureRecognizer:swipeDown];

}

- (void)swipe
{
  NSLog(@"%s",__func__);
}

#pragma mark - 旋转手势
- (void)setUpRotation
{
  UIRotationGestureRecognizer *rotation = [[UIRotationGestureRecognizer alloc] initWithTarget:self action:@selector(rotation:)];
  rotation.delegate = self;
  [self.imageView addGestureRecognizer:rotation];
}

// 默认传递的旋转的角度都是相对于最开始的位置
- (void)rotation:(UIRotationGestureRecognizer *)rotation
{

  self.imageView.transform = CGAffineTransformRotate(self.imageView.transform, rotation.rotation);

  // 复位
  rotation.rotation = 0;

  // 获取手势旋转的角度
  NSLog(@"%f",rotation.rotation);
}

#pragma mark - 捏合
- (void)setUpPinch
{
  UIPinchGestureRecognizer *pinch = [[UIPinchGestureRecognizer alloc] initWithTarget:self action:@selector(pinch:)];
  pinch.delegate = self;
  [self.imageView addGestureRecognizer:pinch];
}

- (void)pinch:(UIPinchGestureRecognizer *)pinch
{
  self.imageView.transform = CGAffineTransformScale(self.imageView.transform, pinch.scale, pinch.scale);

  // 复位

  pinch.scale = 1;
}

#pragma mark - 拖拽
- (void)setUpPan
{
  UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(pan:)];

  [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];

 // NSLog(@"%@",NSStringFromCGPoint(curP));
}

@end

以上就是iOS触摸事件以及手势的相关内容介绍,希望对大家学习iOS程序设计有所帮助。

(0)

相关推荐

  • iOS触摸事件UITouch应用详解

    因为UIView或者UIViewController都是继承与UIResponder ,所以都有UITouch这个事件.当用户点击屏幕的时候,会产生触摸事件. 通过UITouch事件,可以监听到开始触摸.触摸移动过程.触摸结束以及触摸打断四个不同阶段的状态,在这些方法中,我们能够获取到很多有用的信息,比如触摸点的坐标.触摸的手指数.触摸的次数等等,下面通过一个小例子来说明一下. 详细代码如下: /* 定义属性 */ @interface ViewController () { CGPoint _

  • iOS开发中使用cocos2d添加触摸事件的方法

    CCLayer类是用来接收触摸输入的.不过你要首先启用这个功能才可以使用它.你通过设置isTouchEnabled为YES来让层接收触摸事件: 复制代码 代码如下: self.isTouchEnabled = YES; 此项设定最好在init方法中设置.你可以在任何时间将其设置为NO或者YES. 一旦启用isTouchEnabled属性,许多与接收触摸输入相关的方法将会开始被调用.这些事件包括:当新的触摸开始的时候,当手指在触摸屏上移动的时候,还有在用户手指离开屏幕以后.很少会发生触摸事件被取消

  • iOS开发之触摸事件

    一.事件分发处理[由外到内] 在iOS中发生触摸后,事件会加到UIApplication事件队列,UIApplication会从事件队列取出最前面的事件进行分发处理,通常会先分发给主窗口,主窗口会调用hitTest:withEvent:方法,查找适合的事件触发视图,即 找到被触摸的视图对象 寻找流程如下: 在顶级视图(keyWindow的视图)上调用pointInside:withEvent:方法判断触摸点是否在当前视图内: 如果返回NO,那么keyWindow的hitTest:withEven

  • IOS 开发之触摸事件详细介绍

    IOS 触摸事件 iOS中的事件可以分为3大类型: 触摸事件 加速计事件 远程控制事件 响应者对象 在iOS中不是任何对象都能处理事件,只有继承了UIResponder的对象才能接收并处理事件.我们称之为"响应者对象". UIApplication.UIViewController.UIView都继承自UIResponder,因此它们都是响应者对象,都能够接收并处理事件. UIResponder内部提供了以下方法来处理事件 触摸事件(对应Android的action_down.acti

  • 举例讲解iOS应用开发中hitTest触摸事件的编写方法

     hitTest:withEvet  调用过程 比如如果是当前的View A, 还有一个viewB 如果不重写 hitTest 方法,那么 系统默认是先调用viewA的hitest 方法,然后再调用viewB的htest方法. 系统的调用过程,跟下面的重写hitest的方法是一模一样的. 复制代码 代码如下: -(UIView*)hitTest:(CGPoint)point withEvent:(UIEvent *)event  {      if ([self pointInside:poin

  • iOS开发之触摸事件以及手势

    iOS中的事件分为三类:触摸事件.加速计事件.远程控制事件.只有继承了UIResponder的对象才能接收并处理事件,称之为"响应者对象".UIApplication.UIViewController.UIView都继承自UIResponder.UIResponder内部提供的方法来处理事件: 触摸事件:touchesBegan.touchesMoved.touchesEnded.touchesCancelled 加速计事件:motionBegan.motionEnded.motion

  • JS触摸事件、手势事件详解

    触屏已经是我们身边电子设备的常态了.触摸事件当然也是随着触屏的出现,用户使用最多的事件啦! 难道使用触屏事件后,其他原来的鼠标事件就都不能用啦?当然不是,只不过不是那么好用啊. 针对鼠标事件,有哪些不适应? dbclick 触屏设备不支持双击事件.双击浏览器窗口,会放大画面. 可以通过在head标签内加上这么一行: 复制代码 代码如下: <meta name="viewport" content="width=device-width, minimum-scale=1.

  • jQuery 移动端拖拽(模块化开发,触摸事件,webpack)

    通过jquery可以很容易实现CP端的拖拽.但是在移动端却不好用了.于是我自己写了一个在移动端的拖拽demo,主要用到的事件是触摸事件(touchstart,touchmove和touchend). 这个demo实现的功能是:可以拖拽的元素(在这里是图片)位于列表中,这些元素可以被拖到指定区域,到达指定区域(控制台)后,元素被插入控制台后,原来的拖动元素返回原位置,新的元素依然可以在控制台中拖动,也能拖出控制台. 在这个demo中一个用三个模块,分别为ajax模块,drag模块,position

  • iOS开发中的几个手势操作实例分享

    手势操作---识别单击还是双击 在视图上同时识别单击手势和双击手势的问题在于,当检测到一个单击操作时,无法确定是确实是一个单击操作或者只是双击操作中的第一次点击.解决这个问题的方法就是:在检测到单击时,需要等一段时间等待第二次点击,如果没有第二次点击,则为单击操作:如果有第二次点击,则为双击操作. 检测手势有两种方法,一种是定制子视图,重写视图从UIResponder类中继承来的事件处理方法,即touchesBegan:withEvent:等一系列方法来检测手势:另一个方法是使用手势识别器,即U

  • iOS开发之运动事件和远程控制

    之前我们已经学习了触摸处理和手势识别,其实这两个同属于iOS事件的触摸事件,今天我们来学习下iOS事件的另外两个事件: 一.运动事件 运动事件,是通过加速器进行触发,和触摸事件一样,继承UIResponder类的对象才能处理运动事件 UIResponder处理运动事件的方法: 复制代码 代码如下: #pragma mark 运动开始时执行 - (void)motionBegan:(UIEventSubtype)motion withEvent:(UIEvent *)event; #pragma

  • 移动端js触摸事件详解

    在移动开发中,一种较为容易的做法是,先在桌面上开始原型设计,然后再在打算要支持的设备上处理移动特有的部分.多点触摸正是难以在PC上进行测试的那些功能之一,因为大部分的PC都没有触摸输入. 不得不在移动设备上进行的测试有可能会拉长你的开发周期,因为你所做的每项改变都需要提交代码到服务器上,接着再加载到设备上.然后,一旦运行后,对应用也就没有太多的调试了,因为平板电脑和智能手机都很缺乏web开发者所用的工具. 这个问题的一个解决方案是在开发机器上模拟触发事件.对于单点触摸,触摸事件可以基于鼠标事件来

  • Android中View位置和触摸事件详解

    一.简述 View是Android中所有控件的基类,不管是简单的Button和TextView,还是复杂的RelativeLayout和ListView,其基类都是View类:ViewGroup也继承了View类,这意味着View本身就可以代表简单的和复杂的所有控件和布局,通过这种关系,就形成了View树的结构. 本文Demo都是在自定义View中进行的,文末有下载链接 View的位置参数 MotionEvent屏幕触摸事件 GestureDetector手势检测(单击,双击,长摁,滑动) 二.

  • IOS开发之手势响应事件优先级的实例详解

    IOS开发之手势响应事件优先级的实例详解 交互响应事件都是通过手势的操作完成的,如点击.或双击.或长按,这些交互都是在视图中完成的,但是不同的视图可能会有不同的交互,有时候就会出现交互响应事件冲突的情况.这时候就需要处理事件优先级,以便达到想要的效果. 示例场景:一个自定义模式视图view中,有一个列表视图table,同时有一个确定的按钮视图button:在view中有一个单击事件UITapGestureRecognizer,在table中点击每个cell也会有点击事件,同样的button中有个

随机推荐