1.概念
手势是从你用一个或多个手指接触屏幕时开始,直到手指离开屏幕为止所发生的所有事件。无论手势持续多长时间,只要一个或多个手指仍在屏幕上,这个手势就存在。
触摸是指把手指放到IOS设备的屏幕上,从屏幕上拖动或抬起的这样一种行为。手势中涉及的触摸数量等于同时位于屏幕上的手指数量。
手势识别器是一个对象,它知道如何观察用户生成的事件流,并能够识别用户何时以与预定义的手势相匹配的方式进行了触摸和拖动。在检测常见手势时,UIGestureRecognizer类及其各种子类可节省大量工作。UIGestureRecognizer类很好地封装了查找手势的功能,而且方便地应用于任何视图。
2.响应者链、事件
2.1介绍
由于手势是在事件之内传递到系统的,而事件会通过响应者链responder chain进行传递。
在一个应用中,响应者链是一个可变的能够响应用户事件的对象集合。UIResponder是任何响应者类的超类。UIView是UIResponder的子类,UIControl是UIView的之类,因此所有视图和所有控件都是响应者。响应者应该这样命名的,它们响应系统生成的事件,如屏幕触摸。
2.2深入
事件传递:如果第一个响应者不处理某个事件、 某个手势,那么它会将该事件传递到响应者链的下一级。第一响应者总是视图或控件(UIView/UIButton等),并且首先对事件进行相应。如果第一响应者不处理该事件,那么它会将改事件传递给其视图控制器UIViewController。如果此视图控制不处理,将传递给第一响应者的父视图。如果父视图没有响应,则该事件将被转到父视图的控制器。这样一层一层地下去。如果任何视图和控制器都没有处理,那么该事件将会传递给应用的窗口。如果窗口不处理该事件,则窗口会将该事件传递给应用的对象实例UIApplication。
如果UIApplication也不处理该事件,那么还有一个地方可以处理,可以构建一个全局响应者作为响应链的最后一环,那就是应用委托(UIResponder的子类)。
举个例子,如果UIButton注册了事件。那么第一响应者是UIButton、然后是父视图、父控制器、父父视图、父父控制器、UIWindow、UIApplication、AppDelegate。如下图:
3. 4个手势通知方法
我们可以使用4个方法通知响应者有关触摸和手势的情况,他们是touchesBegan:withEvent:、touchesMoved:withEvent:、touchesEnded:withEvent:和touchesCancelled:withEvent:。
//手指开始触碰 - (void) touchesBegan:(NSSet*) touches withEvent:(UIEvent*)event { //多少次点击 int numTaps = [[touches anyObject] tapCount]; //可以将点转换为视图的本地坐标系 CGPoint point = [[touches anyObject] locationInView: self]; } //手指移动 - (void) touchesMoved:(NSSet*) touches withEvent:(UIEvent*)event {} //手指离开屏幕 - (void) touchesEnded:(NSSet*) touches withEvent:(UIEvent*)event {} //当发生某些事件,如来电呼叫,导致手势中断时。在这里可以进行任何的处理操作 - (void) touchesCancelled:(NSSet*) touches withEvent:(UIEvent*)event {}
下面我们通过这4个方法来写两种手势,分别是水平和垂直轻扫这两种手势。
@property (nonatomic) CGPoint gestureStartPoint; static CGFloat const kMinimumGestureLength = 25; static CGFloat const kMaximumVariance = 5; #pragma mark - Touch Handling - (void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { UITouch *touch = [touches anyObject]; //当前的坐标,第一次触摸 self.gestureStartPoint = [touch locationInView: self.view]; } - (void) touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event { UITouch *touch = [touches anyObject]; //触摸移动时的坐标 CGPoint currentPosition = [touch locationInView: self.view]; //函数fabsf放回一个float类型的绝对值 CGFloat deltaX = fabsf( self.gestureStartPoint.x - currentPosition.x ); CGFloat deltaY = fabsf( self.gestureStartPoint.y - currentPosition.y ); if ( deltaX >= kMinimumGestureLength && deltaY <= kMaximumVariance ) { //水平移动,这里开始写处理代码 } else if( deltaY >= kMinimumGestureLength && deltaX <= kMaximumVariance ) { //垂直移动,这里开始写处理代码 } }
4.