【iOS】API之UIGestureRecognizer及自定义手势

在unity开发中,触屏手势在我看来是以手指ID号(手指的身份)以及手指状态为标准界定判断手势的类型的;一些插件感觉和iOS中苹果封装的差不多,因此就UIGestureRecognizer这个类关于API使用进行一下初步探索。。。


一、UIGestureRecognizer

  1. 给手势添加多个Target; 触发后执行顺序按照添加的顺序执行


  2. 获取手指相对于某一个view位置




    第二个方法中touchIndex是手指的序号(比如手势是两个手指触发,手指接触屏幕肯定有先后,第一个接触的系统内部自动给其标为第0个,第二个手指给其标记为第1个)

  3. 不太明白的东西

  4. UIGestureRecognizer.state介绍

  • UIGestureRecognizerStatePossible 尚未识别是何种手势操作(但可能已经触发了触摸事件),默认状态
  • UIGestureRecognizerStateCancelled 这个状态可以在手势被触发之后:1.当前手指没发生变化,又增加了手指(这个手势没有结束的时候又多了手势进来)2.结束之前设置手势enable属性为NO触发
  • UIGestureRecognizerStateFailed 不知道怎么触发这个状态,但是利用方法requireGestureRecognizerToFail:可以保证在other Gesture failed状态下this Gesture才可以触发,代理方法gestureRecognizerShouldBegin:亦与之相关
  • UIGestureRecognizerStateRecognized 在识别结束之后,end状态结束以后,个人理解为表示这个手势结束
    +示例: 对于离散型手势 UITapGestureRecgnizer 要么被识别,要么失败,点按(假设点按次数设置为1,并且没有添加长按手势)下去一次不松开则此时什么也不会发生,松开手指立即识别并调用操作事件,并且状态为3(已完成)。但是连续型手势要复杂一些,就拿旋转手势来说,如果两个手指点下去不做任何操作,此时并不能识别手势(因为我们还没旋转)但是其实已经触发了触摸开始事件,此时处于状态0;如果此时旋转会被识别,也就会调用对应的操作事件,同时状态变成1(手势开始),但是状态1只有一瞬间;紧接着状态变为2(因为我们的旋转需要持续一会),并且重复调用操作事件(如果在事件中打印状态会重复打印2);松开手指,此时状态变为3,并调用1次操作事件。
  1. UIGestureRecognizer.delegate介绍
  • gestureRecognizerShouldBegin: 在手势想要从UIGestureRecognizerStatePossible状态变化的时候调用,返回NO则表示手势状态是UIGestureRecognizerStateFailed
  • gestureRecognizer: shouldRecognizeSimultaneouslyWithGestureRecognizer: 在手势冲突时候调用,默认返回NO表示默认不能同时识别两个手势
  • gestureRecognizer: shouldRequireFailureOfGestureRecognizer:每次尝试识别手势都会调用,一般只会在手势冲突时候,比如同时添加轻击手势和长按手势,轻击手势设置代理,在区分是轻击手势或者长按手势还没有确定结果的时候调用,而不设置轻击手势代理而设置长按代理的时候就不会调用
  • gestureRecognizer: shouldBeRequiredToFailByGestureRecognizer:调用时机同上
  • gestureRecognizer: shouldReceiveTouch:在上面两个方法之前调用
  • gestureRecognizer: shouldReceivePress:同上

二、UIGestureRecognizer子类

UIGestureRecognizer子类有:

UITapGestureRecognizer(轻击)
UILongPressGestureRecognizer(长按)
​UISwipeGestureRecognizer(轻扫)
UIPanGestureRecognizer(拖动)
UIPinchGestureRecognizer(捏合)
UIRotationGestureRecognizer(旋转)
UIScreenEdgePanGestureRecognizer(屏幕边缘手势继承于pan手势)

轻击UITapGestureRecognizer
  • 可以通过修改numberOfTapsRequired(敲击次数默认1)和numberOfTouchesRequired(手指个数默认1)两个参数进行轻击手势的配置,不配置按默认.
长按UILongPressGestureRecognizer
  • 长按手势numberOfTapsRequired手指敲击次数默认为0,如果设置1需要先轻吉再长按才能触发
  • 长按手势可以通过numberOfTouchesRequired(手指个数)minimumPressDuration(最小按压时间)allowableMovement(允许移动像素单位范围:在识别手势期间移动超过范围手势识别失效)进行配置
轻扫​UISwipeGestureRecognizer
  • 可以配置的有numberOfTouchesRequired(手指的个数默认1) direction(轻扫的方向:上下左右;默认右表示手指在屏幕从左到右滑动和边缘滑动手势方向不同)
拖动UIPanGestureRecognizer
  • minimumNumberOfTouches最少可以识别到的手指数量>=1 maximumNumberOfTouches最多可以识别到的手指数量
  • - (CGPoint)translationInView:(nullable UIView *)view; 相对于began时候手指位置的偏移量
  • - (void)setTranslation:(CGPoint)translation inView:(nullable UIView *)view; 作用用于重新定位手指 偏移量的计算
  • - (CGPoint)velocityInView:(nullable UIView *)view; 手指移动速度
//拖动触发后调用
- (void)panGes:(UIPanGestureRecognizer *)ges{
    if (ges.state == UIGestureRecognizerStateChanged) {
        //注意这个方法获取到的偏移量是相对于开始识别到时候手指的位置并不会实时更新需要再调用setTranslation: inView:
        CGPoint p = [ges translationInView:self.redView];
        [ges setTranslation:CGPointZero inView:self.redView];
        self.redView.frame = CGRectMake(self.redView.frame.origin.x + p.x, self.redView.frame.origin.y + p.y, self.redView.frame.size.width, self.redView.frame.size.height);
    }
}
捏合UIPinchGestureRecognizer
  • scale表示形变量;velocity只读 表示单位时间scale改变的速度
//捏合触发后调用
- (void)pinchAction:(UIPinchGestureRecognizer *)ges{
    if (ges.state == UIGestureRecognizerStateChanged) {
        self.redView.transform = CGAffineTransformMakeScale(ges.scale, ges.scale);
    }
    if(ges.state == UIGestureRecognizerStateEnded)
    {
        [UIView animateWithDuration:0.5 animations:^{
            self.redView.transform = CGAffineTransformIdentity;//取消一切形变
        }];
    }
}
旋转UIRotationGestureRecognizer
//旋转触发后调用
- (void)rotaAction:(UIRotationGestureRecognizer *)ges{
    if (ges.state==UIGestureRecognizerStateChanged)
    {
        self.redView.transform=CGAffineTransformMakeRotation(ges.rotation);
    }
    if(ges.state==UIGestureRecognizerStateEnded)
    {
        [UIView animateWithDuration:1 animations:^{
            self.redView.transform=CGAffineTransformIdentity;//取消形变
        }];
    }
}
屏幕边缘手势UIScreenEdgePanGestureRecognizer
  • 继承于pan手势,一般用于左侧滑动返回。吐槽一下,给自定义View添加之后,我发现触发特别难,貌似只有UIRectEdgeLeftUIRectEdgeRight触发情况会好一点,不知道为啥。。。。我绝对没有说fuck

三、自定义手势

好吧,我触发不了系统屏幕边缘手势我自己写一个行了吧。。。。自定义手势需要注意几点:

  • 继承自UIGestureRecognizer
  • .m文件包含头文件#import <UIKit/UIGestureRecognizerSubclass.h>
  • 手势判断之后设置self.state = UIGestureRecognizerStateEnded;就会自动调用对应方法了

WPGesture.h

#import <UIKit/UIKit.h>

@interface WPGesture : UIGestureRecognizer
//需要几根手指
@property(nonatomic, assign)NSUInteger wpNumberOfTouchesRequired;//默认1
//需要几次点击
@property(nonatomic, assign)NSUInteger wpNumberOfTapsRequired;//默认0
//哪一边
@property (readwrite, nonatomic, assign) UIRectEdge wpEdges;//默认UIRectEdgeAll

@end

WPGesture.m

#import "WPGesture.h"
#import <UIKit/UIGestureRecognizerSubclass.h>

@interface WPGesture()
@property(nonatomic, assign)BOOL isCorrected;

@end
@implementation WPGesture
//在began里面识别手势是否正确: 几根手指 几次点击 哪一边  此方法左右:作为手势识别器
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    [super touchesBegan:touches withEvent:event];
    NSArray *allTouches = [touches allObjects];
    //0.判断手指的tapCount  是否在move里面给出移动初始点
    for (UITouch * tou in allTouches) {
        if(tou.tapCount != (self.wpNumberOfTapsRequired + 1)){
            //不符合的话直接return 不再继续判断
            return;
        }
    }

    //1.判断手指个数是否符合
    if(allTouches.count != self.wpNumberOfTouchesRequired){
        //忽略不正确手指 防止干扰到识别
        for (UITouch * tou in allTouches) {
            [self ignoreTouch:tou forEvent:event];
        }
        return;
    }
    
    //2.手指个数满足情况下 判断手指位置是否满足条件
    //用临时变量topM等接收 防止屏幕旋转出现
    CGFloat topM = self.view.bounds.size.height *0.25;//满足点要小于top
    CGFloat butM = self.view.bounds.size.height *0.75;//满足点要大于but
    CGFloat lefM = self.view.bounds.size.width *0.25;//满足点要小于lef
    CGFloat rigM = self.view.bounds.size.width *0.75;//满足点要大于rig
    if (topM >= 100) {
        topM = 100;
        butM = self.view.bounds.size.height - 100;
    }
    if (lefM >= 100) {
        lefM = 100;
        rigM = self.view.bounds.size.width - 100;
    }
    // 判断手指位置是否满足条件
    for (UITouch * tou in allTouches) {
        if (!self.view) {
            return;
        }//貌似能触发began就肯定有view
        CGPoint touchP = [tou locationInView:self.view];//手指位置
        if (self.wpEdges == UIRectEdgeNone) {
            return;//UIRectEdgeNone什么也不做
        }
        if (self.wpEdges == UIRectEdgeTop) {
            if (touchP.y > topM) {
                [self ignoreTouch:tou forEvent:event];
                return;
            }
        }
        if (self.wpEdges == UIRectEdgeBottom) {
            if (touchP.y < butM) {
                [self ignoreTouch:tou forEvent:event];
                return;
            }
        }
        if (self.wpEdges == UIRectEdgeLeft) {
            if (touchP.x > lefM) {
                [self ignoreTouch:tou forEvent:event];
                return;
            }
        }
        if (self.wpEdges == UIRectEdgeRight) {
            if (touchP.x < rigM) {
                [self ignoreTouch:tou forEvent:event];
                return;
            }
        }
        if (self.wpEdges == UIRectEdgeAll) {
            if (touchP.y > topM && touchP.y < butM && touchP.x > lefM && touchP.x < rigM) {
                [self ignoreTouch:tou forEvent:event];
                return;
            }
        }
    }
    self.isCorrected = YES;//识别正确,当前状态UIGestureRecognizerStatePossible
}

- (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    [super touchesMoved:touches withEvent:event];
    if (!self.isCorrected) {
        //不满足began的情况 直接返回
        return;
    }
    //识别正确 移动既是began
    self.state = UIGestureRecognizerStateBegan;//自动内部会自动改变状态为move 
}

- (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    [super touchesEnded:touches withEvent:event];
    if(self.isCorrected){
        self.state = UIGestureRecognizerStateEnded;
        self.state = UIGestureRecognizerStateRecognized;
        [self reset];
    }
}

#pragma mark - 重写方法
- (void)reset{
    [super reset];
    self.isCorrected = NO;
}

//没有end状态 又识别到其他手势的时候  多了手指出来
- (BOOL)canPreventGestureRecognizer:(UIGestureRecognizer *)preventedGestureRecognizer{
    BOOL res = [super canPreventGestureRecognizer:preventedGestureRecognizer];
    self.state = UIGestureRecognizerStateCancelled;
    [self reset];
    return res;
}

//第一次识别 touchBegan之后调用
- (BOOL)shouldRequireFailureOfGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {
    BOOL res = [super shouldRequireFailureOfGestureRecognizer:otherGestureRecognizer];
    return res;
}

#pragma mark - 默认值设置
//重写init 设置默认值
- (instancetype)init{
    if(self = [super init]){
        self.wpEdges = UIRectEdgeAll;
        self.wpNumberOfTapsRequired = 0;
        self.wpNumberOfTouchesRequired = 1;
        self.isCorrected = NO;
    }
    return self;
}

- (instancetype)initWithTarget:(id)target action:(SEL)action{
    if (self = [super initWithTarget:target action:action]) {
        self.wpEdges = UIRectEdgeAll;
        self.wpNumberOfTapsRequired = 0;
        self.wpNumberOfTouchesRequired = 1;
        self.isCorrected = NO;
    }
    return self;
}
@end
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,015评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,262评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,727评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,986评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,363评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,610评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,871评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,582评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,297评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,551评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,053评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,385评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,035评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,079评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,841评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,648评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,550评论 2 270

推荐阅读更多精彩内容