首页 > 代码库 > iOS手势处理

iOS手势处理

iOS手势处理

iOS手势有着如下几种:

  • UITapGestureRecognizer  
  • UIPinchGestureRecognizer
  • UIRotationGestureRecognizer
  • UISwipeGestureRecognizer
  • UIPanGestureRecognizer
  • UILongPressGestureRecognizer

上面的手势对应的操作是: 

  • Tap          (点一下)
  • Pinch        (二指往內或往外拨动,平时经常用到的缩放)  矩阵变换
  • Rotation    (旋转)                                                  矩阵变换
  • Swipe       (滑动,快速移动)
  • Pan          (拖移,慢速移动)                                     矩阵变换
  • LongPress (长按

 

注意:以下示例均把手势封装进一个View当中

UITapGestureRecognizer - 点击手势

GestureView.h + GestureView.m

#import <UIKit/UIKit.h>

@interface GestureView : UIView

@end
GestureView.h
#import "GestureView.h"

@interface GestureView ()
@property (nonatomic, strong) UITapGestureRecognizer *tapGesture;
@property (nonatomic, strong) CALayer                *colorLayer;
@end

@implementation GestureView

- (id)initWithFrame:(CGRect)frame {
    self = [super initWithFrame:frame];
    if (self) {
        // 初始化手势,给手势指定响应事件的对象
        _tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self
                                                              action:@selector(gestureEvent:)];
        _colorLayer = [CALayer layer];
        _colorLayer.frame = self.bounds;
        
        [self.layer addSublayer:_colorLayer];
        
        // 将手势与区域绑定
        [self addGestureRecognizer:_tapGesture];
    }
    return self;
}

- (void)gestureEvent:(UIGestureRecognizer *)sender {
    _colorLayer.backgroundColor = [UIColor colorWithRed:arc4random() % 255 / 255.f
                                                  green:arc4random() % 255 / 255.f
                                                   blue:arc4random() % 255 / 255.f
                                                  alpha:1.0f].CGColor;
}

@end
GestureView.m

- (void)addGestureRecognizer:(UIGestureRecognizer *)gestureRecognizer

Attaching a gesture recognizer to a view defines the scope of the represented gesture, causing it to receive touches hit-tested to that view and all of its subviews. The view establishes a strong reference to the gesture recognizer.

将手势识别器附着在一个view上,实际上定义了一个手势接收的区域,会将接收到的触摸事件传递给这个view以及这个view的说有的subviews.这个view会对这个手势识别器强引用.

可以总结两点:

1. 手势会传递给这个view中所有的subviews

2. view会强引用手势识别器

使用如下:

点击手势有两个参数可以设置:

numberOfTapsRequired         点击几次触发事件(默认是1)

numberOfTouchesRequired    需要几个手指点击(默认是1)

 

UIPinchGestureRecognizer - 缩放

GestureView.h + GestureView.m

#import <UIKit/UIKit.h>

@interface GestureView : UIView

@end
GestureView.h
#import "GestureView.h"

@interface GestureView ()
@property (nonatomic, strong) UIPinchGestureRecognizer *pinchGesture;
@end

@implementation GestureView

- (id)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self)
    {
        // 初始化手势,给手势指定响应事件的对象
        _pinchGesture =             [[UIPinchGestureRecognizer alloc] initWithTarget:self
                                                      action:@selector(gestureEvent:)];
        
        // 将手势与区域绑定
        [self addGestureRecognizer:_pinchGesture];
    }
    return self;
}

- (void)gestureEvent:(UIPinchGestureRecognizer *)sender
{
    //
    self.transform = CGAffineTransformScale(self.transform, sender.scale, sender.scale);
    sender.scale = 1;
}

@end
GestureView.m

缩放手势会用到矩阵变换.

 

UIRotationGestureRecognizer - 旋转

GestureView.h + GestureView.m

#import <UIKit/UIKit.h>

@interface GestureView : UIView

@end
GestureView.h
#import "GestureView.h"

@interface GestureView ()
@property (nonatomic, strong) UIRotationGestureRecognizer *rotationGesture;
@end

@implementation GestureView

- (id)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self)
    {
        // 初始化手势,给手势指定响应事件的对象
        _rotationGesture =             [[UIRotationGestureRecognizer alloc] initWithTarget:self
                                                         action:@selector(gestureEvent:)];
        
        // 将手势与区域绑定
        [self addGestureRecognizer:_rotationGesture];
    }
    return self;
}

- (void)gestureEvent:(UIRotationGestureRecognizer *)sender
{
    // 此处用到了矩阵变换
    self.transform = CGAffineTransformRotate(self.transform, sender.rotation);
    sender.rotation = 0;
}
GestureView.m

 

UISwipeGestureRecognizer - 滑动

GestureView.h + GestureView.m

#import <UIKit/UIKit.h>

@interface GestureView : UIView

@end
GestureView.h
#import "GestureView.h"

@interface GestureView ()
@property (nonatomic, strong) UISwipeGestureRecognizer *swipeGesture;
@end

@implementation GestureView

- (id)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self)
    {
        // 初始化手势,给手势指定响应事件的对象
        _swipeGesture =             [[UISwipeGestureRecognizer alloc] initWithTarget:self
                                                      action:@selector(gestureEvent:)];
        _swipeGesture.direction =             UISwipeGestureRecognizerDirectionLeft | UISwipeGestureRecognizerDirectionRight;
        
        // 将手势与区域绑定
        [self addGestureRecognizer:_swipeGesture];
    }
    return self;
}

- (void)gestureEvent:(UISwipeGestureRecognizer *)sender
{
    NSLog(@"left or right");
}

@end
GestureView.m

 

UIPanGestureRecognizer - 平移

GestureView.h + GestureView.m

#import <UIKit/UIKit.h>

@interface GestureView : UIView

@end
GestureView.h
#import "GestureView.h"

@interface GestureView ()
@property (nonatomic, strong) UIPanGestureRecognizer *panGesture;
@end

@implementation GestureView

- (id)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self)
    {
        // 初始化手势,给手势指定响应事件的对象
        _panGesture =             [[UIPanGestureRecognizer alloc] initWithTarget:self
                                                    action:@selector(gestureEvent:)];

        // 将手势与区域绑定
        [self addGestureRecognizer:_panGesture];
    }
    return self;
}

- (void)gestureEvent:(UIPanGestureRecognizer *)sender
{
    // 此处用到了矩阵变换
    CGPoint translation = [sender translationInView:self];
    
    self.center = CGPointMake(self.center.x + translation.x,
                              self.center.y + translation.y);
    
    [sender setTranslation:CGPointZero
                    inView:self];
}

@end
GestureView.m

 

UILongPressGestureRecognizer - 长按手势

GestureView.h + GestureView.m

#import <UIKit/UIKit.h>

@interface GestureView : UIView

@end
GestureView.h
#import "GestureView.h"

@interface GestureView ()
@property (nonatomic, strong) UILongPressGestureRecognizer *longPressGesture;
@end

@implementation GestureView

- (id)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self)
    {
        // 初始化手势,给手势指定响应事件的对象
        _longPressGesture =             [[UILongPressGestureRecognizer alloc] initWithTarget:self
                                                          action:@selector(gestureEvent:)];
        _longPressGesture.minimumPressDuration = 2.0f;

        // 将手势与区域绑定
        [self addGestureRecognizer:_longPressGesture];
    }
    return self;
}

- (void)gestureEvent:(UILongPressGestureRecognizer *)sender
{
    NSLog(@"触发事件");
}

@end
GestureView.m

 

 

问题:如何处理一个view中添加了两个手势,1个是单击的手势,一个是双击的手势呢?

可以使用这个方法requireGestureRecognizerToFail:

#import "GestureView.h"

@interface GestureView ()
@property (nonatomic, strong) UITapGestureRecognizer *tapGesture1;
@property (nonatomic, strong) UITapGestureRecognizer *tapGesture2;
@end

@implementation GestureView

- (id)initWithFrame:(CGRect)frame {
    self = [super initWithFrame:frame];
    if (self) {
        // 单击手势
        _tapGesture1 =             [[UITapGestureRecognizer alloc] initWithTarget:self
                                                    action:@selector(gesture1Event:)];
        _tapGesture1.numberOfTapsRequired = 1;
        
        // 双击手势
        _tapGesture2 =             [[UITapGestureRecognizer alloc] initWithTarget:self
                                                    action:@selector(gesture2Event:)];
        _tapGesture2.numberOfTapsRequired = 2;

        // 注意: 判断双击手势需要时间,也就是说会有延时
        
        // 有事件触发时,先判断是不是 双击手势,如果不是就执行 单击手势
        [_tapGesture1 requireGestureRecognizerToFail:_tapGesture2];
        
        // 将手势与区域绑定
        [self addGestureRecognizer:_tapGesture1];
        [self addGestureRecognizer:_tapGesture2];
    }
    return self;
}

- (void)gesture1Event:(UIGestureRecognizer *)sender {
    NSLog(@"1");
}

- (void)gesture2Event:(UIGestureRecognizer *)sender {
    NSLog(@"2");
}

@end
GestureView.m

实际上,这种方式会有延时感-_-!!!!

 

问题:如何将长按手势和拖拽手势合并在一起呢? 

我们需要用代理实现,实现以下的方法:

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer  shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer

Asks the delegate if two gesture recognizers should be allowed to recognize gestures simultaneously.

询问这个代理,是否允许两个手势同时触发.

#import "GestureView.h"

@interface GestureView ()<UIGestureRecognizerDelegate>

{
    BOOL  shouldAllowPan;
}

@property (nonatomic, strong) UIPanGestureRecognizer       *panGesture;
@property (nonatomic, strong) UILongPressGestureRecognizer *longPressGesture;
@end

@implementation GestureView

- (id)initWithFrame:(CGRect)frame {
    self = [super initWithFrame:frame];
    if (self) {
        // 初始化时不允许拖拽
        shouldAllowPan = NO;
        
        _panGesture =         [[UIPanGestureRecognizer alloc] initWithTarget:self
                                                action:@selector(panEvent:)];
        [self addGestureRecognizer:_panGesture];
        _panGesture.delegate = self;
        
        _longPressGesture =         [[UILongPressGestureRecognizer alloc] initWithTarget:self
                                                      action:@selector(longPressEvent:)];
        _longPressGesture.minimumPressDuration = 1.0f;
        [self addGestureRecognizer:_longPressGesture];
        _longPressGesture.delegate = self;
    }
    return self;
}

- (void)panEvent:(UIPanGestureRecognizer *)sender {
    
    if(shouldAllowPan == YES)
    {
        // 移动的操作
        CGPoint translation = [sender translationInView:self];
        self.center = CGPointMake(self.center.x + translation.x,
                                  self.center.y + translation.y);
        
        [sender setTranslation:CGPointZero
                        inView:self];
    }
    else if(sender.state == UIGestureRecognizerStateEnded ||             sender.state == UIGestureRecognizerStateFailed ||             sender.state == UIGestureRecognizerStateCancelled)
    {
        shouldAllowPan = NO;
    }
}

- (void)longPressEvent:(UIGestureRecognizer *)sender
{
    // 长按开始
    if(UIGestureRecognizerStateBegan == sender.state)
    {
        NSLog(@"长按开始");
        self.backgroundColor = [UIColor redColor];
        shouldAllowPan = NO;
    }
    
    // 长按进行中
    if(UIGestureRecognizerStateChanged == sender.state)
    {
        NSLog(@"长按进行中");
        shouldAllowPan = YES;
    }
    
    // 长按结束
    if(UIGestureRecognizerStateEnded == sender.state)
    {
        NSLog(@"长按结束");
        self.backgroundColor = [UIColor blackColor];
        shouldAllowPan = NO;
    }
}

// 是否允许多个手势同时触发
- (BOOL) gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer
shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
{
    // 允许
    return YES;
}

// 是否允许继续跟踪触摸事件
- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer
{
    // 条件满足的手势会被传递进来(如果是移动手势,)
    if([gestureRecognizer isKindOfClass:[UIPanGestureRecognizer class]] && shouldAllowPan == NO)
    {
        return NO;
    }
    
    return YES;
}


@end
GestureView.m

根据手势状态来识别手势触发事件的全称细节是十分重要的.