UIGestureRecognizer - BNR

  繼續上篇UITouch - BNR。該篇將實現線條選擇、移動和刪除操做。html

  UIGestureRecognizer有一系列子類,每個子類都用於識別特定的手勢。當識別出一個手勢時,手勢識別器會攔截視圖的觸摸事件。數組

 

  使用UITapGestureRecognizer類,實現當用戶連續點擊屏幕兩次時,所有線條都被清空。ui

修改BNRDrawView類的initWithFrame:方法以下:atom

 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:響應事件,以下:spa

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

添加手勢識別,容許用戶刪除一條選定的線條。修改BNRDrawView類的initWithFrame:方法以下:code

 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:響應事件,以下:htm

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

在BNRDrawView.m文件的類擴展中聲明selectedLine屬性,用來表示被用戶選中的線條,以下:blog

@property (nonatomic, weak) BNRLine *selectedLine;

  其中,finishedLines數組擁有對selectedLine的強引用。當selectedLine從finishedLines中被刪除時,將變爲nil。事件

  修改BNRDrawView類中drawRect:方法,使selectedLine顯示綠色,以下:ip

 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

相關文章
相關標籤/搜索