zoukankan      html  css  js  c++  java
  • UIGestureRecognizer

      继续上篇UITouch - BNR。该篇将实现线条选择、移动和删除操作。

      UIGestureRecognizer有一系列子类,每一个子类都用于识别特定的手势。当识别出一个手势时,手势识别器会拦截视图的触摸事件。

      使用UITapGestureRecognizer类,实现当用户连续点击屏幕两次时,全部线条都被清空。

    修改BNRDrawView类的initWithFrame:方法如下:

     1 - (instancetype)initWithFrame:(CGRect)r {
     2     self = [super initWithFrame:r];
     3     if (self) {
     4         self.linesInProgress = [[NSMutableDictionary alloc] init];
     5         self.finishedLines = [[NSMutableArray alloc] init];
     6         self.backgroundColor = [UIColor grayColor];
     7         self.multipleTouchEnabled = YES;
     8         
     9         UITapGestureRecognizer *doubleTapRecognizer = [[UITapGestureRecognizer alloc]
    10                                                        initWithTarget:self
    11                                                        action:@selector(doubleTap:)];
    12         doubleTapRecognizer.numberOfTapsRequired = 2;
    13         doubleTapRecognizer.delaysTouchesBegan = YES;  //使第一次点击不产生红点14         [self addGestureRecognizer:doubleTapRecognizer];
    15     }
    16     return self;
    17 }

    实现连续两次点击的doubleTap:响应事件,如下:

    1 - (void)doubleTap:(UIGestureRecognizer *)gr {
    2     NSLog(@"Recognized Double Tap");
    3     [self.linesInProgress removeAllObjects];
    4     [self.finishedLines removeAllObjects];
    5     [self setNeedsDisplay];
    6 }

    添加手势识别,允许用户删除一条选定的线条。修改BNRDrawView类的initWithFrame:方法如下:

     1 - (instancetype)initWithFrame:(CGRect)r {
     2     self = [super initWithFrame:r];
     3     if (self) {
     4         self.linesInProgress = [[NSMutableDictionary alloc] init];
     5         self.finishedLines = [[NSMutableArray alloc] init];
     6         self.backgroundColor = [UIColor grayColor];
     7         self.multipleTouchEnabled = YES;
     8         
     9         UITapGestureRecognizer *doubleTapRecognizer = [[UITapGestureRecognizer alloc]
    10                                                        initWithTarget:self
    11                                                        action:@selector(doubleTap:)];
    12         doubleTapRecognizer.numberOfTapsRequired = 2;
    13         doubleTapRecognizer.delaysTouchesBegan = YES;
    14         [self addGestureRecognizer:doubleTapRecognizer];
    15         
    16         UITapGestureRecognizer *tapRecognizer = [[UITapGestureRecognizer alloc]
    17                                                  initWithTarget:self
    18                                                  action:@selector(tap:)];
    19         tapRecognizer.delaysTouchesBegan = YES;
    20         [tapRecognizer requireGestureRecognizerToFail:doubleTapRecognizer]; //区别点击与双击手势
    21         [self addGestureRecognizer:tapRecognizer];
    22     }
    23     return self;
    24 }

    实现单击的tap:响应事件,如下:

    1 - (void)tap:(UIGestureRecognizer *)gr {
    2     NSLog(@"Recognized Tap");
    3 }

    在BNRDrawView.m文件的类扩展中声明selectedLine属性,用来表示被用户选中的线条,如下:

    @property (nonatomic, weak) BNRLine *selectedLine;

      其中,finishedLines数组拥有对selectedLine的强引用。当selectedLine从finishedLines中被删除时,将变为nil。

      修改BNRDrawView类中drawRect:方法,使selectedLine显示绿色,如下:

     1 - (void)drawRect:(CGRect)rect {
     2     [[UIColor blackColor] set];
     3     for (BNRLine *line in self.finishedLines) {
     4         [self strokeLike:line];
     5     }
     6     [[UIColor redColor] set];
     7     for (NSValue *key in self.linesInProgress) {
     8         [self strokeLike:self.linesInProgress[key]];
     9     }
    10     if(self.selectedLine) {
    11         [[UIColor greenColor] set];
    12         [self strokeLike:self.selectedLine];
    13     }
    14 }

    获取接近点击处的线条:

     1 - (BNRLine *)lineAtPoint:(CGPoint)p {
     2     for(BNRLine *l in self.finishedLines) {
     3         CGPoint start = l.begin;
     4         CGPoint end = l.end;
     5         for (float t = 0.0; t <= 1.0; t += 0.05) {
     6             float x = start.x + t * (end.x - start.x);
     7             float y = start.y + t * (end.y - start.y);
     8             if (hypot(x - p.x, y - p.y) < 20.0) {
     9                 return l;
    10             }
    11         }
    12     }
    13     return nil;
    14 }

    最后修改点击手势响应事件tap:方法如下:

    1 - (void)tap:(UIGestureRecognizer *)gr {
    2     NSLog(@"Recognized Tap");
    3     
    4     CGPoint point = [gr locationInView:self];
    5     self.selectedLine = [self lineAtPoint:point];
    6     [self setNeedsDisplay];
    7 }

      接下来实现,当用户已经选中一条线条之后,将显示一个菜单提供对该线条的删除操作。

    修改tap:方法如下:

     1 - (void)tap:(UIGestureRecognizer *)gr {
     2     NSLog(@"Recognized Tap");
     3     CGPoint point = [gr locationInView:self];
     4     self.selectedLine = [self lineAtPoint:point];
     5     
     6     if(self.selectedLine) {
     7         //使该视图成为menu item响应方法的目标
     8         [self becomeFirstResponder];
     9         UIMenuController *menu = [UIMenuController sharedMenuController];
    10         //创建一个新的删除UIMenuItem
    11         UIMenuItem *deleteItem = [[UIMenuItem alloc] initWithTitle:@"Delete" action:@selector(deleteLine:)];
    12         menu.menuItems = @[deleteItem];
    13         //设置menu的显示位置
    14         [menu setTargetRect:CGRectMake(point.x, point.y, 2, 2) inView:self];
    15         [menu setMenuVisible:YES animated:YES];
    16     } else {
    17         [[UIMenuController sharedMenuController] setMenuVisible:NO animated:YES];
    18     }
    19     
    20     [self setNeedsDisplay];
    21 }

      当自定义的视图类需要成为第一响应者时,必须重载canBecomeFirstResponder方法,在BNRDrawView.m中添加如下方法:

    1 - (BOOL)canBecomeFirstResponder {
    2     return YES;
    3 }

      如果menu items的响应方法没有实现,该menu就不会显示。实现deleteLine:方法如下:

    1 - (void)deleteLine:(id)sender {
    2     [self.finishedLines removeObject:self.selectedLine];
    3     [self setNeedsDisplay];
    4 }

    运行程序,效果如下:


      接下来,实现如下功能,当用户长按住一条线时,该线将被选中,用户能够用手指拖动该线条。修改initWithFrame:方法如下:

     1 - (instancetype)initWithFrame:(CGRect)r {
     2     self = [super initWithFrame:r];
     3     if (self) {
     4         self.linesInProgress = [[NSMutableDictionary alloc] init];
     5         self.finishedLines = [[NSMutableArray alloc] init];
     6         self.backgroundColor = [UIColor grayColor];
     7         self.multipleTouchEnabled = YES;
     8         
     9         UITapGestureRecognizer *doubleTapRecognizer = [[UITapGestureRecognizer alloc]
    10                                                        initWithTarget:self
    11                                                        action:@selector(doubleTap:)];
    12         doubleTapRecognizer.numberOfTapsRequired = 2;
    13         doubleTapRecognizer.delaysTouchesBegan = YES;
    14         [self addGestureRecognizer:doubleTapRecognizer];
    15         
    16         UITapGestureRecognizer *tapRecognizer = [[UITapGestureRecognizer alloc]
    17                                                  initWithTarget:self
    18                                                  action:@selector(tap:)];
    19         tapRecognizer.delaysTouchesBegan = YES;
    20         [tapRecognizer requireGestureRecognizerToFail:doubleTapRecognizer]; //区别点击与双击手势
    21         [self addGestureRecognizer:tapRecognizer];
    22         
    23         UILongPressGestureRecognizer *pressRecognizer = [[UILongPressGestureRecognizer alloc]
    24                                                          initWithTarget:self
    25                                                          action:@selector(longPress:)];
    26         [self addGestureRecognizer:pressRecognizer];
    27     }
    28     return self;
    29 }

      手势识别器处理长按时,其state属性会经历三种变化,其为 UIGestureRecognizerStatePossible  UIGestureRecognizerStateBegan  UIGestureRecognizerStateEnded 。

    实现长按的响应方法longPress:如下:

     1 - (void)longPress:(UIGestureRecognizer *)gr {
     2     if (gr.state == UIGestureRecognizerStateBegan) {
     3         CGPoint point = [gr locationInView:self];
     4         self.selectedLine = [self lineAtPoint:point];
     5         if (self.selectedLine) {
     6             [self.linesInProgress removeAllObjects];
     7         }
     8     } else if (gr.state == UIGestureRecognizerStateEnded) {
     9             self.selectedLine = nil;
    10     }
    11     [self setNeedsDisplay];
    12 }

     让BNRDrawView遵守UIGestureRecognizerDelegate协议,并添加一个UIPanGestureRecognizer属性,如下:

    1 @interface BNRDrawView () <UIGestureRecognizerDelegate>
    2 
    3 @property (nonatomic, strong) UIPanGestureRecognizer *moveRecognizer;
    4 @property (nonatomic, strong) NSMutableDictionary *linesInProgress;
    5 @property (nonatomic, strong) NSMutableArray *finishedLines;
    6 @property (nonatomic, weak) BNRLine *selectedLine;
    7 
    8 @end

    修改initWithFrame:方法。此处cancelsTouchesInView属性默认为YES,意味着手势识别器将吞没任何它识别出的手势,这样的话,视图将没有机会对UIRespnder方法做出响应。将其设置为NO,这样,手势识别器能识别的触摸事件也会被UIResponder识别。代码如下:

     1 - (instancetype)initWithFrame:(CGRect)r {
     2     self = [super initWithFrame:r];
     3     if (self) {
     4         self.linesInProgress = [[NSMutableDictionary alloc] init];
     5         self.finishedLines = [[NSMutableArray alloc] init];
     6         self.backgroundColor = [UIColor grayColor];
     7         self.multipleTouchEnabled = YES;
     8         
     9         UITapGestureRecognizer *doubleTapRecognizer = [[UITapGestureRecognizer alloc]
    10                                                        initWithTarget:self
    11                                                        action:@selector(doubleTap:)];
    12         doubleTapRecognizer.numberOfTapsRequired = 2;
    13         doubleTapRecognizer.delaysTouchesBegan = YES;
    14         [self addGestureRecognizer:doubleTapRecognizer];
    15         
    16         UITapGestureRecognizer *tapRecognizer = [[UITapGestureRecognizer alloc]
    17                                                  initWithTarget:self
    18                                                  action:@selector(tap:)];
    19         tapRecognizer.delaysTouchesBegan = YES;
    20         [tapRecognizer requireGestureRecognizerToFail:doubleTapRecognizer]; //区别点击与双击手势
    21         [self addGestureRecognizer:tapRecognizer];
    22         
    23         UILongPressGestureRecognizer *pressRecognizer = [[UILongPressGestureRecognizer alloc]
    24                                                          initWithTarget:self
    25                                                          action:@selector(longPress:)];
    26         [self addGestureRecognizer:pressRecognizer];
    27         
    28         self.moveRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(moveLine:)];
    29         self.moveRecognizer.delegate = self;
    30         self.moveRecognizer.cancelsTouchesInView = NO;
    31         [self addGestureRecognizer:self.moveRecognizer];
    32     }
    33     return self;
    34 }

    实现UIGestureRecognizerDelegate协议的gestureRecognizer:shouldRecognizeSimultaneouslyWithGestureRecognizer:方法,如果该方法返回YES,手势识别器将与其它的识别器分享该触摸事件。如下:

    1 - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {
    2     if (gestureRecognizer == self.moveRecognizer) {
    3         return YES;
    4     }
    5     return NO;
    6 }

    实现移动手势的响应事件moveLine:,如下:

     1 - (void)moveLine:(UIPanGestureRecognizer *)gr {
     2     if (!self.selectedLine) {
     3         return;
     4     }
     5     if (gr.state == UIGestureRecognizerStateChanged) {
     6         CGPoint translation = [gr translationInView:self];
     7         CGPoint begin = self.selectedLine.begin;
     8         CGPoint end = self.selectedLine.end;
     9         begin.x += translation.x;
    10         begin.y += translation.y;
    11         end.x += translation.x;
    12         end.y += translation.y;
    13         self.selectedLine.begin = begin;
    14         self.selectedLine.end = end;
    15         [self setNeedsDisplay];
    16         [gr setTranslation:CGPointZero inView:self];
    17     }
    18 }

     程序代码链接:http://pan.baidu.com/s/1o6Dz5xg

  • 相关阅读:
    笔试题
    js在IE和FF下的兼容性问题
    Textarea高度随内容自适应地增长,无滚动条
    请让页面中的一个元素(10px*10px)围绕坐标(200, 300) 做圆周运动
    一次点击两次触发addEventListener
    html 1.0 鼠标放上去会亮 onmouseover onmouseout 以及this标签的使用
    提示框一段时间以后消失setTimeout
    两种定时器 setInterval(一直执行) setTimeout(只执行一次)
    其他标签a实现提交功能
    创建标签的两种方法insertAdjacentHTML 和 createElement 创建标签 setAttribute 赋予标签类型 appendChild 插入标签
  • 原文地址:https://www.cnblogs.com/wjq-Law/p/4904215.html
Copyright © 2011-2022 走看看