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