原文网址:http://blog.csdn.net/totogo2010/article/details/8615940

1、UIGestureRecognizer介绍

手势识别在iOS上非常重要,手势操作移动设备的重要特征,极大的增加了移动设备使用便捷性。
iOS系统在3.2以后,为方便开发这使用一些常用的手势,提供了UIGestureRecognizer类。手势识别UIGestureRecognizer类是个抽象类,下面的子类是具体的手势,开发这可以直接使用这些手势识别。
  • Tap(点一下)
  • Pinch(二指往內或往外拨动,平时经常用到的缩放)
  • Rotation(旋转)
  • Swipe(滑动,快速移动)
  • Pan (拖移,慢速移动)
  • LongPress(长按)
UIGestureRecognizer的继承关系如下:
 
 

2、使用手势的步骤

使用手势很简单,分为两步:
  1. 创建手势实例。当创建手势时,指定一个回调方法,当手势开始,改变、或结束时,回调方法被调用。
  2. 添加到需要识别的View中。每个手势只对应一个View,当屏幕触摸在View的边界内时,如果手势和预定的一样,那就会回调方法。
ps:一个手势只能对应一个View,但是一个View可以有多个手势。
建议在真机上运行这些手势,模拟器操作不太方便,可能导致你认为手势失效。

3、Pan 拖动手势:

  1. UIImageView *snakeImageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"snake.png"]];
  2. snakeImageView.frame = CGRectMake(50, 50, 100, 160);
  3. UIPanGestureRecognizer *panGestureRecognizer = [[UIPanGestureRecognizer alloc]
  4. initWithTarget:self
  5. action:@selector(handlePan:)];
  6. [snakeImageView addGestureRecognizer:panGestureRecognizer];
  7. [self.view setBackgroundColor:[UIColor whiteColor]];
  8. [self.view addSubview:snakeImageView];

新建一个ImageView,然后添加手势

回调方法:
  1. - (void) handlePan:(UIPanGestureRecognizer*) recognizer
  2. {
  3. CGPoint translation = [recognizer translationInView:self.view];
  4. recognizer.view.center = CGPointMake(recognizer.view.center.x + translation.x,
  5. recognizer.view.center.y + translation.y);
  6. [recognizer setTranslation:CGPointZero inView:self.view];
  7. }

4、Pinch缩放手势

  1. UIPinchGestureRecognizer *pinchGestureRecognizer = [[UIPinchGestureRecognizer alloc]
  2. initWithTarget:self
  3. action:@selector(handlePinch:)];<p class="p1">[<span class="s1">snakeImageView</span> <span class="s2">addGestureRecognizer</span>:pinchGestureRecognizer];</p>
  1. - (void) handlePinch:(UIPinchGestureRecognizer*) recognizer
  2. {
  3. recognizer.view.transform = CGAffineTransformScale(recognizer.view.transform, recognizer.scale, recognizer.scale);
  4. recognizer.scale = 1;
  5. }

5、Rotation旋转手势

  1. UIRotationGestureRecognizer *rotateRecognizer = [[UIRotationGestureRecognizer alloc]
  2. initWithTarget:self
  3. action:@selector(handleRotate:)];
  4. [snakeImageView addGestureRecognizer:rotateRecognizer];
 
  1. - (void) handleRotate:(UIRotationGestureRecognizer*) recognizer
  2. {
  3. recognizer.view.transform = CGAffineTransformRotate(recognizer.view.transform, recognizer.rotation);
  4. recognizer.rotation = 0;
  5. }
 

 

 
添加了这几个手势后,运行看效果,程序中的imageView放了一个
                    /^\/^\
                  _|__|  O|
         \/     /~     \_/ \
          \____|__________/  \
                 \_______      \
                         `\     \                 \
                           |     |                  \
                          /      /                    \
                         /     /                       \\
                       /      /                         \ \
                      /     /                            \  \
                    /     /             _----_            \   \
                   /     /           _-~      ~-_         |   |
                  (      (        _-~    _--_    ~-_     _/   |
                   \      ~-____-~    _-~    ~-_    ~-_-~    /
                     ~-_           _-~          ~-_       _-~  
                        ~--______-~                ~-___-~
的图片,在模拟器上拖动是没问题的。缩放和旋转有点问题,估计是因为在模拟器上的模拟的两个接触点距离在imageView的边界外了,所以操作无效果。
建议在真机上运行这个手势。
在模拟器上缩放和选择的操作技巧:
可以把imageView的frame值设置大一点,按住alt键,按下触摸板(不按下不行),这样就可以旋转和缩放了。

6、添加第二个ImagView并添加手势

记住:一个手势只能添加到一个View,两个View当然要有两个手势的实例了
  1. - (void)viewDidLoad
  2. {
  3. [super viewDidLoad];
  4. UIImageView *snakeImageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"snake.png"]];
  5. UIImageView *dragonImageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"dragon.png"]];
  6. snakeImageView.frame = CGRectMake(120, 120, 100, 160);
  7. dragonImageView.frame = CGRectMake(50, 50, 100, 160);
  8. [self.view addSubview:snakeImageView];
  9. [self.view addSubview:dragonImageView];
  10. for (UIView *view in self.view.subviews) {
  11. UIPanGestureRecognizer *panGestureRecognizer = [[UIPanGestureRecognizer alloc]
  12. initWithTarget:self
  13. action:@selector(handlePan:)];
  14. UIPinchGestureRecognizer *pinchGestureRecognizer = [[UIPinchGestureRecognizer alloc]
  15. initWithTarget:self
  16. action:@selector(handlePinch:)];
  17. UIRotationGestureRecognizer *rotateRecognizer = [[UIRotationGestureRecognizer alloc]
  18. initWithTarget:self
  19. action:@selector(handleRotate:)];
  20. [view addGestureRecognizer:panGestureRecognizer];
  21. [view addGestureRecognizer:pinchGestureRecognizer];
  22. [view addGestureRecognizer:rotateRecognizer];
  23. [view setUserInteractionEnabled:YES];
  24. }
  25. [self.view setBackgroundColor:[UIColor whiteColor]];
  26. }

多添加了一条龙的view,两个view都能接收上面的三种手势。运行效果如下:

 

7、拖动(pan手势)速度(以较快的速度拖放后view有滑行的效果)

如何实现呢?
  1. 监视手势是否结束
  2. 监视触摸的速度
  1. - (void) handlePan:(UIPanGestureRecognizer*) recognizer
  2. {
  3. CGPoint translation = [recognizer translationInView:self.view];
  4. recognizer.view.center = CGPointMake(recognizer.view.center.x + translation.x,
  5. recognizer.view.center.y + translation.y);
  6. [recognizer setTranslation:CGPointZero inView:self.view];
  7. if (recognizer.state == UIGestureRecognizerStateEnded) {
  8. CGPoint velocity = [recognizer velocityInView:self.view];
  9. CGFloat magnitude = sqrtf((velocity.x * velocity.x) + (velocity.y * velocity.y));
  10. CGFloat slideMult = magnitude / 200;
  11. NSLog(@"magnitude: %f, slideMult: %f", magnitude, slideMult);
  12. float slideFactor = 0.1 * slideMult; // Increase for more of a slide
  13. CGPoint finalPoint = CGPointMake(recognizer.view.center.x + (velocity.x * slideFactor),
  14. recognizer.view.center.y + (velocity.y * slideFactor));
  15. finalPoint.x = MIN(MAX(finalPoint.x, 0), self.view.bounds.size.width);
  16. finalPoint.y = MIN(MAX(finalPoint.y, 0), self.view.bounds.size.height);
  17. [UIView animateWithDuration:slideFactor*2 delay:0 options:UIViewAnimationOptionCurveEaseOut animations:^{
  18. recognizer.view.center = finalPoint;
  19. } completion:nil];
  20. }

代码实现解析:

  1. 计算速度向量的长度(估计大部分都忘了)这些知识了。
  2. 如果速度向量小于200,那就会得到一个小于的小数,那么滑行会很短
  3. 基于速度和速度因素计算一个终点
  4. 确保终点不会跑出父View的边界
  5. 使用UIView动画使view滑动到终点
运行后,快速拖动图像view放开会看到view还会在原来的方向滑行一段路。
 

8、同时触发两个view的手势

手势之间是互斥的,如果你想同时触发蛇和龙的view,那么需要实现协议

UIGestureRecognizerDelegate,

  1. @interface ViewController : UIViewController<UIGestureRecognizerDelegate>
  2. @end

并在协议这个方法里返回YES。

  1. -(BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
  2. {
  3. return YES;
  4. }

把self作为代理设置给手势:

  1. panGestureRecognizer.delegate = self;
  2. pinchGestureRecognizer.delegate = self;
  3. rotateRecognizer.delegate = self;

这样可以同时拖动或旋转缩放两个view了。

9、tap点击手势

这里为了方便看到tap的效果,当点击一下屏幕时,播放一个声音。

为了播放声音,我们加入AVFoundation.framework这个框架。

  1. - (AVAudioPlayer *)loadWav:(NSString *)filename {
  2. NSURL * url = [[NSBundle mainBundle] URLForResource:filename withExtension:@"wav"];
  3. NSError * error;
  4. AVAudioPlayer * player = [[AVAudioPlayer alloc] initWithContentsOfURL:url error:&error];
  5. if (!player) {
  6. NSLog(@"Error loading %@: %@", url, error.localizedDescription);
  7. } else {
  8. [player prepareToPlay];
  9. }
  10. return player;
  11. }

我会在最后例子代码给出完整代码,添加手势的步骤和前面一样的。

  1. #import <UIKit/UIKit.h>
  2. #import <AVFoundation/AVFoundation.h>
  3. @interface ViewController : UIViewController<UIGestureRecognizerDelegate>
  4. @property (strong) AVAudioPlayer * chompPlayer;
  5. @property (strong) AVAudioPlayer * hehePlayer;
  6. @end
  1. - (void)handleTap:(UITapGestureRecognizer *)recognizer {
  2. [self.chompPlayer play];
  3. }

运行,点一下某个图,就会播放一个咬东西的声音。

不过这个点击播放声音有点缺陷,就是在慢慢拖动的时候也会播放。这使得两个手势重合了。怎么解决呢?使用手势的:requireGestureRecognizerToFail方法。

10、手势的依赖性

在viewDidLoad的循环里添加这段代码:

  1. [tapRecognizer requireGestureRecognizerToFail:panGestureRecognizer];

意思就是,当如果pan手势失败,就是没发生拖动,才会出发tap手势。这样如果你有轻微的拖动,那就是pan手势发生了。tap的声音就不会发出来了。

11、自定义手势

自定义手势继承:UIGestureRecognizer,实现下面的方法:

  1. – touchesBegan:withEvent:
  2. – touchesMoved:withEvent:
  3. – touchesEnded:withEvent:
  4. - touchesCancelled:withEvent:

新建一个类,继承UIGestureRecognizer,代码如下:

.h文件

  1. #import <UIKit/UIKit.h>
  2. typedef enum {
  3. DirectionUnknown = 0,
  4. DirectionLeft,
  5. DirectionRight
  6. } Direction;
  7. @interface HappyGestureRecognizer : UIGestureRecognizer
  8. @property (assign) int tickleCount;
  9. @property (assign) CGPoint curTickleStart;
  10. @property (assign) Direction lastDirection;
  11. @end

.m文件

  1. #import "HappyGestureRecognizer.h"
  2. #import <UIKit/UIGestureRecognizerSubclass.h>
  3. #define REQUIRED_TICKLES        2
  4. #define MOVE_AMT_PER_TICKLE     25
  5. @implementation HappyGestureRecognizer
  6. - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
  7. UITouch * touch = [touches anyObject];
  8. self.curTickleStart = [touch locationInView:self.view];
  9. }
  10. - (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
  11. // Make sure we've moved a minimum amount since curTickleStart
  12. UITouch * touch = [touches anyObject];
  13. CGPoint ticklePoint = [touch locationInView:self.view];
  14. CGFloat moveAmt = ticklePoint.x - self.curTickleStart.x;
  15. Direction curDirection;
  16. if (moveAmt < 0) {
  17. curDirection = DirectionLeft;
  18. } else {
  19. curDirection = DirectionRight;
  20. }
  21. if (ABS(moveAmt) < MOVE_AMT_PER_TICKLE) return;
  22. // 确认方向改变了
  23. if (self.lastDirection == DirectionUnknown ||
  24. (self.lastDirection == DirectionLeft && curDirection == DirectionRight) ||
  25. (self.lastDirection == DirectionRight && curDirection == DirectionLeft)) {
  26. // 挠痒次数
  27. self.tickleCount++;
  28. self.curTickleStart = ticklePoint;
  29. self.lastDirection = curDirection;
  30. // 一旦挠痒次数超过指定数,设置手势为结束状态
  31. // 这样回调函数会被调用。
  32. if (self.state == UIGestureRecognizerStatePossible && self.tickleCount > REQUIRED_TICKLES) {
  33. [self setState:UIGestureRecognizerStateEnded];
  34. }
  35. }
  36. }
  37. - (void)reset {
  38. self.tickleCount = 0;
  39. self.curTickleStart = CGPointZero;
  40. self.lastDirection = DirectionUnknown;
  41. if (self.state == UIGestureRecognizerStatePossible) {
  42. [self setState:UIGestureRecognizerStateFailed];
  43. }
  44. }
  45. - (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
  46. {
  47. [self reset];
  48. }
  49. - (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
  50. {
  51. [self reset];
  52. }
  53. @end

调用自定义手势和上面一样,回到这样写:

  1. - (void)handleHappy:(HappyGestureRecognizer *)recognizer{
  2. [self.hehePlayer play];
  3. }

手势成功后播放呵呵笑的声音。
在真机上运行,按住某个view,快速左右拖动,就会发出笑的声音了。

代码解析:
先获取起始坐标:curTickleStart
通过和ticklePoint的x值对比,得出当前的放下是向左还是向右。再算出移动的x的值是否比MOVE_AMT_PER_TICKLE距离大,如果太则返回。
再判断是否有三次是不同方向的动作,如果是则手势结束,回调。
 
参考:http://www.raywenderlich.com/6567/uigesturerecognizer-tutorial-in-ios-5-pinches-pans-and-more
 

容芳志 (http://blog.csdn.net/totogo2010)

本文遵循“署名-非商业用途-保持一致”创作公用协议

【转】iOS手势识别的详细使用(拖动,缩放,旋转,点击,手势依赖,自定义手势) -- 不错不错的更多相关文章

  1. iOS手势识别的详细使用(拖动,缩放,旋转,点击,手势依赖,自定义手势)

    iOS手势识别的详细使用(拖动,缩放,旋转,点击,手势依赖,自定义手势)       1.UIGestureRecognizer介绍 手势识别在iOS上非常重要,手势操作移动设备的重要特征,极大的增加 ...

  2. ios iOS手势识别的详细使用(拖动,缩放,旋转,点击,手势依赖,自定义手势)

    iOS手势识别的详细使用(拖动,缩放,旋转,点击,手势依赖,自定义手势) 转自容芳志大神的博客:http://www.cnblogs.com/stoic/archive/2013/02/27/2940 ...

  3. iOS--------手势识别的详细使用:拖动、缩放、旋转、点击、手势依赖、自定义手势

    1.UIGestureRecognizer介绍 手势识别在iOS上非常重要,手势操作移动设备的重要特征,极大的增加了移动设备使用便捷性. iOS系统在3.2以后,为方便开发这使用一些常用的手势,提供了 ...

  4. 【iOS发展-89】UIGestureRecognizer完整的旋转手势识别、缩放和拖拽等效果

    (1)效果 (2)代码 http://download.csdn.net/detail/wsb200514/8261001 (3)总结 --先依据所需创建不同类型的手势识别.比方: UITapGest ...

  5. iOS,手势识别简单使用

    1.iOS目前支持的手势识别(6种) 2.点按手势和慢速拖动手势简单使用 iOS目前支持的手势识别(6种) UITapGestureRecognizer(点按) UIPinchGestureRecog ...

  6. UIGestureRecognizer ios手势识别温习

    1.UIGestureRecognizer介绍 手势识别在iOS上非常重要,手势操作移动设备的重要特征,极大的增加了移动设备使用便捷性. iOS系统在3.2以后,为方便开发这使用一些常用的手势,提供了 ...

  7. iOS 手势识别

    首先给大家解释一下为什么要学习手势识别? 如果想监听一个UIView上面的触摸事件,之前的做法是: 自定义一个UIView : 实现UIView的touches方法,在方法里面实现具体功能 透过tou ...

  8. iOS手势识别

    一.手势识别与触摸事件 1.如果想监听一个view上面的触摸事件,可选的做法是: (1)自定义一个view (2)实现view的touches方法,在方法内部实现具体处理代码 2.通过touches方 ...

  9. iOS开发之UIImage等比缩放

    iOS开发之UIImage等比缩放 评论功能真不错 评论开通后,果然有很多人吐槽.谢谢大家的支持和关爱,如果有做的不到的地方,还请海涵.毕竟我一个人的力量是有限的,我会尽自己最大的努力大家准备一些干货 ...

随机推荐

  1. GIS业务逻辑

    三维怎么加载数据文件? OpenFileDialog frm = new OpenFileDialog(); frm.Filter = "文件数据集|*.tile|多时相数据集|*.Temp ...

  2. vs2008+cmake2.8+OpenCV2.8.4配置过程中OpenCV.sln重编译部分工程失败

    解决方法来自此链接 http://www.tuicool.com/articles/qiQBb2N vs2008+cmake2.8+OpenCV2.8.4配置过程 1.解压opencv2.4.8 2. ...

  3. css3分栏

    <!DOCTYPE HTML> <meta charset="UTF-8"> <title>分栏</title> <style ...

  4. hdu 1085

    额    母函数 #include <cstdio> #include <cstring> int a[3],b[3]= {1,2,5}; int c1[10001],c2[1 ...

  5. Java中堆、栈、常量池分析

    栈用于存储局部变量,包括基本类型的变量(方法语句块内部定义的变量.方法中的形参).引用类型的变量,它们都是存储在各自的方法栈中,随着方法的执行完成而消失: 堆用于存储引用类型变量所指向的对象,包括普通 ...

  6. iOS开发之loadView、viewDidLoad及viewDidUnload的关系

    iOS开发之loadView.viewDidLoad及viewDidUnload的关系 iOS开发之loadView.viewDidLoad及viewDidUnload的关系    标题中所说的3个方 ...

  7. Nginx-location配置指南

    语法规则: location [=|~|~*|^~] /uri/ { … } = 开头表示精确匹配 ^~ 开头表示uri以某个常规字符串开头,理解为匹配 url路径即可.nginx不对url做编码,因 ...

  8. Android的三种网络通信方式

    Android平台有三种网络接口可以使用,他们分别是:java.net.*(标准Java接口).Org.apache接口和Android.net.*(Android网络接口).下面分别介绍这些接口的功 ...

  9. POJ1860——Currency Exchange(BellmanFord算法求最短路)

    Currency Exchange DescriptionSeveral currency exchange points are working in our city. Let us suppos ...

  10. POJ2109——Power of Cryptography

    Power of Cryptography DescriptionCurrent work in cryptography involves (among other things) large pr ...