手势识别

  • 所有的手势操作都继承于UIGestureRecognizer,这个类本身是一个虚类,不能直接使用。这个类中定义了这几种手势共有的一些属性和方法(下表仅列出常用属性和方法):
名称 说明
属性
@property(nonatomic,readonly) UIGestureRecognizerState state; 手势状态
@property(nonatomic, getter=isEnabled) BOOL enabled; 手势是否可用
@property(nonatomic,readonly) UIView *view; 触发手势的视图(一般在触摸执行操作中我们可以通过此属性获得触摸视图进行操作)
@property(nonatomic) BOOL delaysTouchesBegan; 手势识别失败前不执行触摸开始事件,默认为NO;如果为YES,那么成功识别则不执行触摸开始事件,失败则执行触摸开始事件;如果为NO,则不管成功与否都执行触摸开始事件;
方法
- (void)addTarget:(id)target action:(SEL)action; 添加触摸执行事件
- (void)removeTarget:(id)target action:(SEL)action; 移除触摸执行事件
- (NSUInteger)numberOfTouches; 触摸点的个数(同时触摸的手指数)
- (CGPoint)locationInView:(UIView*)view; 在指定视图中的相对位置
- (CGPoint)locationOfTouch:(NSUInteger)touchIndex inView:(UIView*)view; 触摸点相对于指定视图的位置
- (void)requireGestureRecognizerToFail:(UIGestureRecognizer *)otherGestureRecognizer; 指定一个手势需要另一个手势执行失败才会执行
代理方法 UIGestureRecognizerDelegate
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer; 一个控件的手势识别后是否阻断手势识别继续向下传播,默认返回NO;如果为YES,响应者链上层对象触发手势识别后,如果下层对象也添加了手势并成功识别也会继续执行,否则上层对象识别后则不再继续传播;
  • 我们能直接使用的是UIGestureRecognizer类的子类,它的子类有:
手势 说明
UITapGestureRecognizer 点击手势
UIPinchGestureRecognizer 捏合手势
UIPanGestureRecognizer 拖动手势
UISwipeGestureRecognizer 轻扫手势,支持四个方向,但多个方向需要添加多个手势
UIRotationGestureRecognizer 旋转手势
UILongPressGestureRecognizer 长按手势
  • 手势的状态
    • 在iOS中将手势状态分为如下几种:
typedef NS_ENUM(NSInteger, UIGestureRecognizerState) {
    UIGestureRecognizerStatePossible,   // 尚未识别是何种手势操作(但可能已经触发了触摸事件),默认状态
    UIGestureRecognizerStateBegan,      // 手势已经开始,此时已经被识别,但是这个过程中可能发生变化,手势操作尚未完成
    UIGestureRecognizerStateChanged,    // 手势状态发生转变
    UIGestureRecognizerStateEnded,      // 手势识别操作完成(此时已经松开手指)
    UIGestureRecognizerStateCancelled,  // 手势被取消,恢复到默认状态
    UIGestureRecognizerStateFailed,     // t手势识别失败,恢复到默认状态
    UIGestureRecognizerStateRecognized = UIGestureRecognizerStateEnded // 手势识别完成,同UIGestureRecognizerStateEnded
};
  • 在iOS中添加手势比较简单,可以归纳为以下几个步骤:
  1. 创建对应的手势对象;
  2. 设置手势识别属性【可选】;
  3. 附加手势到指定的对象;
  4. 编写手势操作方法;

参考【iOS_成才录】【iOS --苹果自带的UIMenuController功能扩展】自定义了Label,给Label加上UIMenuController菜单栏,这里就用到了点击手势。

#import "QSLabel.h"

@implementation QSLabel
/**
 *  初始化
 */
- (void)awakeFromNib
{
    [super awakeFromNib];
    // 添加手势
    UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(theLabelClick)];
    [self addGestureRecognizer:tap];
    [tap setNumberOfTapsRequired:2];
    
    // 这里一定要设置为YES,不然接收不到手势事件
    self.userInteractionEnabled = YES;
}

- (instancetype)initWithFrame:(CGRect)frame
{
    if (self = [super initWithFrame:frame]) {
        // 添加手势
        UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(theLabelClick)];
        [self addGestureRecognizer:tap];
        [tap setNumberOfTapsRequired:2];
        self.userInteractionEnabled = YES;
    }
    return self;
}

/**
 *  点击label
 */
- (void)theLabelClick
{
    // 成为第一响应者
    [self becomeFirstResponder];
    
    // 创建菜单
    UIMenuController *menuController = [UIMenuController sharedMenuController];
    // 设置菜单显示的内容
    menuController.menuItems = @[
                                 [[UIMenuItem alloc] initWithTitle:@"复制" action:@selector(myCopy:)],
                                 [[UIMenuItem alloc] initWithTitle:@"剪切" action:@selector(myCut:)],
                                 [[UIMenuItem alloc] initWithTitle:@"粘贴" action:@selector(myPaste:)]
                                ];
    
    // 设置菜单显示的位置
    [menuController setTargetRect:self.bounds inView:self];
    
    // 显示菜单
    [menuController setMenuVisible:YES animated:YES];
}

#pragma mark - UIMenuController相关
/**
 *  让label具备第一响应者的资格
 */
- (BOOL)canBecomeFirstResponder
{
    return YES;
}

/**
 * 通过第一响应者的这个方法告诉UIMenuController可以显示什么内容
 */
- (BOOL)canPerformAction:(SEL)action withSender:(id)sender
{
    if ((action == @selector(myCopy:) && self.text)
        || (action == @selector(myCut:) && self.text)
        || (action == @selector(myPaste:))) {
        return YES;
    }
    
    return NO;
}

#pragma mark - 监听MenuItem的点击事件
/**
 *  复制
 */
- (void)myCopy:(UIMenuController *)menu
{
    // 将文字存储到剪贴板
    [UIPasteboard generalPasteboard].string = self.text;
}

/**
 *  剪切
 */
- (void)myCut:(UIMenuController *)menu
{
    // 将文字存储到剪贴板
    [UIPasteboard generalPasteboard].string = self.text;
    // 删除文字
    self.text = nil;
}

/**
 *  粘贴
 */
- (void)myPaste:(UIMenuController *)menu
{
    self.text = [UIPasteboard generalPasteboard].string;
}
@end
  • 手势冲突

同一个控件添加多个手势的时候,有时就会产生冲突,例如:轻扫和拖动;长按和拖动等

在iOS中,如果一个手势A的识别部分是另一个手势B的子部分时,默认情况下A就会先识别,B就无法识别了。要解决这个冲突可以利用- (void)requireGestureRecognizerToFail:(UIGestureRecognizer *)otherGestureRecognizer;方法来完成。正是前面表格中UIGestureRecognizer的最后一个方法,这个方法可以指定某个手势执行的前提是另一个手势失败才会识别执行。也就是说如果我们指定拖动手势的执行前提为轻扫手势失败才执行,这样一来当我们手指轻轻滑动时,系统会优先考虑轻扫手势,如果最后发现该操作不是轻扫,那么才会执行拖动。

  • 两个不同的控件上的手势同时执行

有时,同一个手势我们需要多个控件同时执行。

我们知道在iOS的触摸事件中,事件触发是根据响应者链进行的,上层触摸事件执行后就不再向下传播。默认情况下手势也是类似的,先识别的手势会阻断手势识别操作继续传播。那么如何让两个有层次关系并且都添加了手势的控件都能正确识别手势呢?答案就是利用代理的-(BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer方法。这个代理方法默认返回NO,会阻断继续向下识别手势,如果返回YES则可以继续向下传播识别。

这篇文章参考至:【Kenshin Cui's Blog】【iOS开发系列--触摸事件、手势识别、摇晃事件、耳机线控】

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

推荐阅读更多精彩内容