繼續上篇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 }