【iOS】API之UIGestureRecognizer及自定義手勢

在unity開發(fā)中灌旧,觸屏手勢在我看來是以手指ID號(hào)(手指的身份)以及手指狀態(tài)為標(biāo)準(zhǔn)界定判斷手勢的類型的动猬;一些插件感覺和iOS中蘋果封裝的差不多阶剑,因此就UIGestureRecognizer這個(gè)類關(guān)于API使用進(jìn)行一下初步探索普筹。冒版。筐乳。


一歌殃、UIGestureRecognizer

  1. 給手勢添加多個(gè)Target; 觸發(fā)后執(zhí)行順序按照添加的順序執(zhí)行


  2. 獲取手指相對(duì)于某一個(gè)view位置




    第二個(gè)方法中touchIndex是手指的序號(hào)(比如手勢是兩個(gè)手指觸發(fā),手指接觸屏幕肯定有先后蝙云,第一個(gè)接觸的系統(tǒng)內(nèi)部自動(dòng)給其標(biāo)為第0個(gè)氓皱,第二個(gè)手指給其標(biāo)記為第1個(gè))

  3. 不太明白的東西

  4. UIGestureRecognizer.state介紹

  • UIGestureRecognizerStatePossible 尚未識(shí)別是何種手勢操作(但可能已經(jīng)觸發(fā)了觸摸事件),默認(rèn)狀態(tài)
  • UIGestureRecognizerStateCancelled 這個(gè)狀態(tài)可以在手勢被觸發(fā)之后:1.當(dāng)前手指沒發(fā)生變化勃刨,又增加了手指(這個(gè)手勢沒有結(jié)束的時(shí)候又多了手勢進(jìn)來)2.結(jié)束之前設(shè)置手勢enable屬性為NO觸發(fā)
  • UIGestureRecognizerStateFailed 不知道怎么觸發(fā)這個(gè)狀態(tài)波材,但是利用方法requireGestureRecognizerToFail:可以保證在other Gesture failed狀態(tài)下this Gesture才可以觸發(fā),代理方法gestureRecognizerShouldBegin:亦與之相關(guān)
  • UIGestureRecognizerStateRecognized 在識(shí)別結(jié)束之后,end狀態(tài)結(jié)束以后身隐,個(gè)人理解為表示這個(gè)手勢結(jié)束
    +示例: 對(duì)于離散型手勢 UITapGestureRecgnizer 要么被識(shí)別廷区,要么失敗,點(diǎn)按(假設(shè)點(diǎn)按次數(shù)設(shè)置為1贾铝,并且沒有添加長按手勢)下去一次不松開則此時(shí)什么也不會(huì)發(fā)生隙轻,松開手指立即識(shí)別并調(diào)用操作事件,并且狀態(tài)為3(已完成)垢揩。但是連續(xù)型手勢要復(fù)雜一些玖绿,就拿旋轉(zhuǎn)手勢來說,如果兩個(gè)手指點(diǎn)下去不做任何操作叁巨,此時(shí)并不能識(shí)別手勢(因?yàn)槲覀冞€沒旋轉(zhuǎn))但是其實(shí)已經(jīng)觸發(fā)了觸摸開始事件斑匪,此時(shí)處于狀態(tài)0;如果此時(shí)旋轉(zhuǎn)會(huì)被識(shí)別锋勺,也就會(huì)調(diào)用對(duì)應(yīng)的操作事件蚀瘸,同時(shí)狀態(tài)變成1(手勢開始),但是狀態(tài)1只有一瞬間庶橱;緊接著狀態(tài)變?yōu)?(因?yàn)槲覀兊男D(zhuǎn)需要持續(xù)一會(huì))苍姜,并且重復(fù)調(diào)用操作事件(如果在事件中打印狀態(tài)會(huì)重復(fù)打印2);松開手指悬包,此時(shí)狀態(tài)變?yōu)?衙猪,并調(diào)用1次操作事件。
  1. UIGestureRecognizer.delegate介紹
  • gestureRecognizerShouldBegin: 在手勢想要從UIGestureRecognizerStatePossible狀態(tài)變化的時(shí)候調(diào)用,返回NO則表示手勢狀態(tài)是UIGestureRecognizerStateFailed
  • gestureRecognizer: shouldRecognizeSimultaneouslyWithGestureRecognizer: 在手勢沖突時(shí)候調(diào)用垫释,默認(rèn)返回NO表示默認(rèn)不能同時(shí)識(shí)別兩個(gè)手勢
  • gestureRecognizer: shouldRequireFailureOfGestureRecognizer:每次嘗試識(shí)別手勢都會(huì)調(diào)用丝格,一般只會(huì)在手勢沖突時(shí)候,比如同時(shí)添加輕擊手勢和長按手勢棵譬,輕擊手勢設(shè)置代理显蝌,在區(qū)分是輕擊手勢或者長按手勢還沒有確定結(jié)果的時(shí)候調(diào)用,而不設(shè)置輕擊手勢代理而設(shè)置長按代理的時(shí)候就不會(huì)調(diào)用
  • gestureRecognizer: shouldBeRequiredToFailByGestureRecognizer:調(diào)用時(shí)機(jī)同上
  • gestureRecognizer: shouldReceiveTouch:在上面兩個(gè)方法之前調(diào)用
  • gestureRecognizer: shouldReceivePress:同上

二订咸、UIGestureRecognizer子類

UIGestureRecognizer子類有:

UITapGestureRecognizer(輕擊)
UILongPressGestureRecognizer(長按)
?UISwipeGestureRecognizer(輕掃)
UIPanGestureRecognizer(拖動(dòng))
UIPinchGestureRecognizer(捏合)
UIRotationGestureRecognizer(旋轉(zhuǎn))
UIScreenEdgePanGestureRecognizer(屏幕邊緣手勢繼承于pan手勢)

輕擊UITapGestureRecognizer
  • 可以通過修改numberOfTapsRequired(敲擊次數(shù)默認(rèn)1)和numberOfTouchesRequired(手指個(gè)數(shù)默認(rèn)1)兩個(gè)參數(shù)進(jìn)行輕擊手勢的配置,不配置按默認(rèn).
長按UILongPressGestureRecognizer
  • 長按手勢numberOfTapsRequired手指敲擊次數(shù)默認(rèn)為0曼尊,如果設(shè)置1需要先輕吉再長按才能觸發(fā)
  • 長按手勢可以通過numberOfTouchesRequired(手指個(gè)數(shù))minimumPressDuration(最小按壓時(shí)間)allowableMovement(允許移動(dòng)像素單位范圍:在識(shí)別手勢期間移動(dòng)超過范圍手勢識(shí)別失效)進(jìn)行配置
輕掃?UISwipeGestureRecognizer
  • 可以配置的有numberOfTouchesRequired(手指的個(gè)數(shù)默認(rèn)1) direction(輕掃的方向:上下左右;默認(rèn)右表示手指在屏幕從左到右滑動(dòng)和邊緣滑動(dòng)手勢方向不同)
拖動(dòng)UIPanGestureRecognizer
  • minimumNumberOfTouches最少可以識(shí)別到的手指數(shù)量>=1 maximumNumberOfTouches最多可以識(shí)別到的手指數(shù)量
  • - (CGPoint)translationInView:(nullable UIView *)view; 相對(duì)于began時(shí)候手指位置的偏移量
  • - (void)setTranslation:(CGPoint)translation inView:(nullable UIView *)view; 作用用于重新定位手指 偏移量的計(jì)算
  • - (CGPoint)velocityInView:(nullable UIView *)view; 手指移動(dòng)速度
//拖動(dòng)觸發(fā)后調(diào)用
- (void)panGes:(UIPanGestureRecognizer *)ges{
    if (ges.state == UIGestureRecognizerStateChanged) {
        //注意這個(gè)方法獲取到的偏移量是相對(duì)于開始識(shí)別到時(shí)候手指的位置并不會(huì)實(shí)時(shí)更新需要再調(diào)用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只讀 表示單位時(shí)間scale改變的速度
//捏合觸發(fā)后調(diào)用
- (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;//取消一切形變
        }];
    }
}
旋轉(zhuǎn)UIRotationGestureRecognizer
//旋轉(zhuǎn)觸發(fā)后調(diào)用
- (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手勢脏嚷,一般用于左側(cè)滑動(dòng)返回骆撇。吐槽一下,給自定義View添加之后父叙,我發(fā)現(xiàn)觸發(fā)特別難神郊,貌似只有UIRectEdgeLeftUIRectEdgeRight觸發(fā)情況會(huì)好一點(diǎn),不知道為啥趾唱。涌乳。。甜癞。我絕對(duì)沒有說fuck

三夕晓、自定義手勢

好吧,我觸發(fā)不了系統(tǒng)屏幕邊緣手勢我自己寫一個(gè)行了吧悠咱。蒸辆。。乔煞。自定義手勢需要注意幾點(diǎn):

  • 繼承自UIGestureRecognizer
  • .m文件包含頭文件#import <UIKit/UIGestureRecognizerSubclass.h>
  • 手勢判斷之后設(shè)置self.state = UIGestureRecognizerStateEnded;就會(huì)自動(dòng)調(diào)用對(duì)應(yīng)方法了

WPGesture.h

#import <UIKit/UIKit.h>

@interface WPGesture : UIGestureRecognizer
//需要幾根手指
@property(nonatomic, assign)NSUInteger wpNumberOfTouchesRequired;//默認(rèn)1
//需要幾次點(diǎn)擊
@property(nonatomic, assign)NSUInteger wpNumberOfTapsRequired;//默認(rèn)0
//哪一邊
@property (readwrite, nonatomic, assign) UIRectEdge wpEdges;//默認(rèn)UIRectEdgeAll

@end

WPGesture.m

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

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

@end
@implementation WPGesture
//在began里面識(shí)別手勢是否正確: 幾根手指 幾次點(diǎn)擊 哪一邊  此方法左右:作為手勢識(shí)別器
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    [super touchesBegan:touches withEvent:event];
    NSArray *allTouches = [touches allObjects];
    //0.判斷手指的tapCount  是否在move里面給出移動(dòng)初始點(diǎn)
    for (UITouch * tou in allTouches) {
        if(tou.tapCount != (self.wpNumberOfTapsRequired + 1)){
            //不符合的話直接return 不再繼續(xù)判斷
            return;
        }
    }

    //1.判斷手指個(gè)數(shù)是否符合
    if(allTouches.count != self.wpNumberOfTouchesRequired){
        //忽略不正確手指 防止干擾到識(shí)別
        for (UITouch * tou in allTouches) {
            [self ignoreTouch:tou forEvent:event];
        }
        return;
    }
    
    //2.手指個(gè)數(shù)滿足情況下 判斷手指位置是否滿足條件
    //用臨時(shí)變量topM等接收 防止屏幕旋轉(zhuǎn)出現(xiàn)
    CGFloat topM = self.view.bounds.size.height *0.25;//滿足點(diǎn)要小于top
    CGFloat butM = self.view.bounds.size.height *0.75;//滿足點(diǎn)要大于but
    CGFloat lefM = self.view.bounds.size.width *0.25;//滿足點(diǎn)要小于lef
    CGFloat rigM = self.view.bounds.size.width *0.75;//滿足點(diǎn)要大于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;
        }//貌似能觸發(fā)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;//識(shí)別正確,當(dāng)前狀態(tài)UIGestureRecognizerStatePossible
}

- (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    [super touchesMoved:touches withEvent:event];
    if (!self.isCorrected) {
        //不滿足began的情況 直接返回
        return;
    }
    //識(shí)別正確 移動(dòng)既是began
    self.state = UIGestureRecognizerStateBegan;//自動(dòng)內(nèi)部會(huì)自動(dòng)改變狀態(tài)為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狀態(tài) 又識(shí)別到其他手勢的時(shí)候  多了手指出來
- (BOOL)canPreventGestureRecognizer:(UIGestureRecognizer *)preventedGestureRecognizer{
    BOOL res = [super canPreventGestureRecognizer:preventedGestureRecognizer];
    self.state = UIGestureRecognizerStateCancelled;
    [self reset];
    return res;
}

//第一次識(shí)別 touchBegan之后調(diào)用
- (BOOL)shouldRequireFailureOfGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {
    BOOL res = [super shouldRequireFailureOfGestureRecognizer:otherGestureRecognizer];
    return res;
}

#pragma mark - 默認(rèn)值設(shè)置
//重寫init 設(shè)置默認(rèn)值
- (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
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末柒室,一起剝皮案震驚了整個(gè)濱河市渡贾,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌雄右,老刑警劉巖空骚,帶你破解...
    沈念sama閱讀 210,978評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異擂仍,居然都是意外死亡囤屹,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,954評(píng)論 2 384
  • 文/潘曉璐 我一進(jìn)店門逢渔,熙熙樓的掌柜王于貴愁眉苦臉地迎上來肋坚,“玉大人,你說我怎么就攤上這事≈茄幔” “怎么了诲泌?”我有些...
    開封第一講書人閱讀 156,623評(píng)論 0 345
  • 文/不壞的土叔 我叫張陵,是天一觀的道長铣鹏。 經(jīng)常有香客問我敷扫,道長,這世上最難降的妖魔是什么诚卸? 我笑而不...
    開封第一講書人閱讀 56,324評(píng)論 1 282
  • 正文 為了忘掉前任葵第,我火速辦了婚禮,結(jié)果婚禮上合溺,老公的妹妹穿的比我還像新娘卒密。我一直安慰自己,他們只是感情好辫愉,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,390評(píng)論 5 384
  • 文/花漫 我一把揭開白布栅受。 她就那樣靜靜地躺著,像睡著了一般恭朗。 火紅的嫁衣襯著肌膚如雪屏镊。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,741評(píng)論 1 289
  • 那天痰腮,我揣著相機(jī)與錄音而芥,去河邊找鬼。 笑死膀值,一個(gè)胖子當(dāng)著我的面吹牛棍丐,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播沧踏,決...
    沈念sama閱讀 38,892評(píng)論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼歌逢,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了翘狱?” 一聲冷哼從身側(cè)響起秘案,我...
    開封第一講書人閱讀 37,655評(píng)論 0 266
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎潦匈,沒想到半個(gè)月后阱高,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,104評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡茬缩,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,451評(píng)論 2 325
  • 正文 我和宋清朗相戀三年赤惊,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片凰锡。...
    茶點(diǎn)故事閱讀 38,569評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡未舟,死狀恐怖圈暗,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情处面,我是刑警寧澤厂置,帶...
    沈念sama閱讀 34,254評(píng)論 4 328
  • 正文 年R本政府宣布,位于F島的核電站魂角,受9級(jí)特大地震影響昵济,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜野揪,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,834評(píng)論 3 312
  • 文/蒙蒙 一访忿、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧斯稳,春花似錦海铆、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,725評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至憎茂,卻和暖如春珍语,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背竖幔。 一陣腳步聲響...
    開封第一講書人閱讀 31,950評(píng)論 1 264
  • 我被黑心中介騙來泰國打工板乙, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人拳氢。 一個(gè)月前我還...
    沈念sama閱讀 46,260評(píng)論 2 360
  • 正文 我出身青樓募逞,卻偏偏與公主長得像,于是被迫代替她去往敵國和親馋评。 傳聞我的和親對(duì)象是個(gè)殘疾皇子放接,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,446評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容