欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

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

程序员文章站 2023-11-26 16:47:04
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程序设计有所帮助。