当前位置: 移动技术网 > 移动技术>移动开发>IOS > iOS开发之触摸事件以及手势

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

2019年07月24日  | 移动技术网移动技术  | 我要评论
ios中的事件分为三类:触摸事件、加速计事件、远程控制事件。只有继承了uiresponder的对象才能接收并处理事件,称之为“响应者对象”。uiapplication、ui

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程序设计有所帮助。

如您对本文有疑问或者有任何想说的,请 点击进行留言回复,万千网友为您解惑!

相关文章:

验证码:
移动技术网