iOS 手势识别与操作 UIGestureRecognizer

来源:互联网 发布:淘宝客服绩效管理 编辑:程序博客网 时间:2024/05/16 07:03

1.UIGestureRecognizer

  • UITapGestureRecognizer 轻拍手势
  • UIPinchGestureRecognizer 捏合缩放
  • UIRotationGestureRecognizer 旋转
  • UISwipeGestureRecognizer 轻扫滑动
  • UIPanGestureRecognizer 拖移
  • UILongPressGestureRecognizer 长按

2.注意事项

当为手势添加回调方法时,手势开始,改变、或结束时,回调方法被调用,有的时候只需要调用一次方法,可以用gesture的state属性来判断,如if (gesture.state == UIGestureRecognizerStateBegan) {}

3.TapGesture

    self.view.backgroundColor = [UIColor lightGrayColor];    self.gv.layer.borderWidth = 2;    self.gv.layer.cornerRadius = 6;    // 设置gv控件支持用户交互    self.gv.userInteractionEnabled = YES;    // 设置gv控件支持多点触碰    self.gv.multipleTouchEnabled = YES;    for (int i = 1 ; i < 6 ; i++)    {        // 创建手势处理器,指定使用该控制器的handleTap:方法处理手势        UITapGestureRecognizer* gesture = [[UITapGestureRecognizer alloc]            initWithTarget:self action:@selector(handleTap:)];        // 设置该点击手势处理器只处理i次连击事件        gesture.numberOfTapsRequired = i;        // 设置该点击手势处理器只处理2个手指的触碰事件        gesture.numberOfTouchesRequired = 1;        // 为gv控件添加手势处理器。        [self.gv addGestureRecognizer:gesture];    }}// 实现手势处理器的方法,该方法应该声明一个形参。// 当该方法被激发时,手势处理器会作为参数传给该方法的参数。- (void) handleTap:(UITapGestureRecognizer*)gesture{    NSUInteger touchNum = gesture.numberOfTouches;    NSUInteger tapNum = gesture.numberOfTapsRequired;    self.label.text = [NSString stringWithFormat:        @"用户使用%lu个手指进行触碰,触碰次数为:%lu" , touchNum , tapNum];    // 指定2秒后清除label的文本    [self.label performSelector:@selector(setText:)        withObject:@"" afterDelay:2];}

4.PinchGesture

- (void)viewDidLoad{    [super viewDidLoad];    self.view.backgroundColor = [UIColor lightGrayColor];    self.gv.layer.borderWidth = 2;    self.gv.layer.cornerRadius = 6;    // 设置gv控件支持用户交互    self.gv.userInteractionEnabled = YES;    // 设置gv控件支持多点触碰    self.gv.multipleTouchEnabled = YES;    // 创建手势处理器,指定使用该控制器的handlePinch:方法处理手势    UIPinchGestureRecognizer* gesture = [[UIPinchGestureRecognizer alloc]        initWithTarget:self action:@selector(handlePinch:)];    // 为gv控件添加手势处理器。    [self.gv addGestureRecognizer:gesture];}// 实现手势处理器的方法,该方法应该声明一个形参。// 当该方法被激发时,手势处理器会作为参数传给该方法的参数。- (void) handlePinch:(UIPinchGestureRecognizer*)gesture{    // 获取用户捏合的速度和比例    CGFloat velocity = gesture.velocity;    CGFloat scale = gesture.scale;    self.label.text = [NSString stringWithFormat:        @"用户捏合的速度为%g、比例为%g" , velocity , scale];    // 指定2秒后清除label的文本    //[self.label performSelector:@selector(setText:)    //  withObject:@"" afterDelay:2];}

5.RotationGesture 仿iPhone相册图片捏合旋转

1.扩展UIImage:UIImage+CWCategory
UIImage+CWCategory.h

@interface UIImage (CWCategory)// 对指定UI控件进行截图+ (UIImage*)captureView:(UIView *)targetView;//+ (UIImage*)captureScreen;// 定义一个方法用于“挖取”图片的指定区域- (UIImage *)imageAtRect:(CGRect)rect;// 保持图片纵横比缩放,最短边必须匹配targetSize的大小// 可能有一条边的长度会超过targetSize指定的大小- (UIImage *)imageByScalingAspectToMinSize:(CGSize)targetSize;// 保持图片纵横比缩放,最长边匹配targetSize的大小即可// 可能有一条边的长度会小于targetSize指定的大小- (UIImage *)imageByScalingAspectToMaxSize:(CGSize)targetSize;// 不保持图片纵横比缩放- (UIImage *)imageByScalingToSize:(CGSize)targetSize;// 对图片按弧度执行旋转- (UIImage *)imageRotatedByRadians:(CGFloat)radians;// 对图片按角度执行旋转- (UIImage *)imageRotatedByDegrees:(CGFloat)degrees;- (void) saveToDocuments:(NSString*)fileName;@end

UIImage+CWCategory.m:

#import "UIImage+CWCategory.h"#import <QuartzCore/QuartzCore.h>//extern CGImageRef UIGetScreenImage();@implementation UIImage (CWCategory)//+ (UIImage*)captureScreen//{//  // 需要先声明该外部函数//  extern CGImageRef UIGetScreenImage();//  // 调用UIGetScreenImage()函数执行截屏//  CGImageRef screen = UIGetScreenImage();//  // 获取截屏得到的图片//  UIImage* newImage = [UIImage imageWithCGImage:screen];//  return newImage;//}+ (UIImage*)captureView:(UIView *)targetView{    // 获取目标UIView的所在的区域    CGRect rect = targetView.frame;    // 开始绘图    UIGraphicsBeginImageContext(rect.size);    // 获取当前的绘图Context    CGContextRef context = UIGraphicsGetCurrentContext();    // 调用CALayer的方法将当前控件绘制到绘图Context中    [targetView.layer renderInContext:context];    // 获取该绘图Context中的图片    UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();    UIGraphicsEndImageContext();    return newImage;}-(UIImage *)imageAtRect:(CGRect)rect{    // 获取该UIImage图片对应的CGImageRef对象    CGImageRef srcImage = [self CGImage];    // 从srcImage中“挖取”rect区域    CGImageRef imageRef = CGImageCreateWithImageInRect(srcImage, rect);    // 将“挖取”出来的CGImageRef转换为UIImage对象    UIImage* subImage = [UIImage imageWithCGImage: imageRef];    CGImageRelease(srcImage);    CGImageRelease(imageRef);    return subImage;}- (UIImage *)imageByScalingAspectToMinSize:(CGSize)targetSize{    // 获取源图片的宽和高    CGSize imageSize = self.size;    CGFloat width = imageSize.width;    CGFloat height = imageSize.height;    // 获取图片缩放目标大小的宽和高    CGFloat targetWidth = targetSize.width;    CGFloat targetHeight = targetSize.height;    // 定义图片缩放后的宽度    CGFloat scaledWidth = targetWidth;    // 定义图片缩放后的高度    CGFloat scaledHeight = targetHeight;    CGPoint anchorPoint = CGPointZero;    // 如果源图片的大小与缩放的目标大小不相等    if (!CGSizeEqualToSize(imageSize, targetSize))    {        // 计算水平方向上的缩放因子        CGFloat xFactor = targetWidth / width;        // 计算垂直方向上的缩放因子        CGFloat yFactor = targetHeight / height;        // 定义缩放因子scaleFactor,为两个缩放因子中较大的一个        CGFloat scaleFactor = xFactor > yFactor? xFactor : yFactor;        // 根据缩放因子计算图片缩放后的宽度和高度        scaledWidth  = width * scaleFactor;        scaledHeight = height * scaleFactor;        // 如果横向上的缩放因子大于纵向上的缩放因子,那么图片在纵向上需要裁切        if (xFactor > yFactor)        {            anchorPoint.y = (targetHeight - scaledHeight) * 0.5;        }        // 如果横向上的缩放因子小于纵向上的缩放因子,那么图片在横向上需要裁切        else if (xFactor < yFactor)        {            anchorPoint.x = (targetWidth - scaledWidth) * 0.5;        }    }    // 开始绘图    UIGraphicsBeginImageContext(targetSize);    // 定义图片缩放后的区域    CGRect anchorRect = CGRectZero;    anchorRect.origin = anchorPoint;    anchorRect.size.width  = scaledWidth;    anchorRect.size.height = scaledHeight;    // 将图片本身绘制到auchorRect区域中    [self drawInRect:anchorRect];    // 获取绘制后生成的新图片    UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();    UIGraphicsEndImageContext();    // 返回新图片    return newImage ;}- (UIImage *)imageByScalingAspectToMaxSize:(CGSize)targetSize{    // 获取源图片的宽和高    CGSize imageSize = self.size;    CGFloat width = imageSize.width;    CGFloat height = imageSize.height;    // 获取图片缩放目标大小的宽和高       CGFloat targetWidth = targetSize.width;    CGFloat targetHeight = targetSize.height;    // 定义图片缩放后的实际的宽和高度    CGFloat scaledWidth = targetWidth;    CGFloat scaledHeight = targetHeight;    CGPoint anchorPoint = CGPointZero;    // 如果源图片的大小与缩放的目标大小不相等      if (!CGSizeEqualToSize(imageSize, targetSize))    {        CGFloat xFactor = targetWidth / width;        CGFloat yFactor = targetHeight / height;        // 定义缩放因子scaleFactor,为两个缩放因子中较小的一个        CGFloat scaleFactor = xFactor < yFactor ? xFactor:yFactor;        // 根据缩放因子计算图片缩放后的宽度和高度              scaledWidth  = width * scaleFactor;        scaledHeight = height * scaleFactor;        // 如果横向的缩放因子小于纵向的缩放因子,图片在上面、下面有空白        // 那么图片在纵向上需要下移一段距离,保持图片在中间        if (xFactor < yFactor)        {            anchorPoint.y = (targetHeight - scaledHeight) * 0.5;        }        // 如果横向的缩放因子小于纵向的缩放因子,图片在左边、右边有空白        // 那么图片在横向上需要右移一段距离,保持图片在中间        else if (xFactor > yFactor)        {            anchorPoint.x = (targetWidth - scaledWidth) * 0.5;        }    }    // 开始绘图    UIGraphicsBeginImageContext(targetSize);    // 定义图片缩放后的区域    CGRect anchorRect = CGRectZero;    anchorRect.origin = anchorPoint;    anchorRect.size.width  = scaledWidth;    anchorRect.size.height = scaledHeight;    // 将图片本身绘制到auchorRect区域中    [self drawInRect:anchorRect];    // 获取绘制后生成的新图片    UIImage* newImage = UIGraphicsGetImageFromCurrentImageContext();    UIGraphicsEndImageContext();    // 返回新图片    return newImage ;}- (UIImage *)imageByScalingToSize:(CGSize)targetSize{    // 开始绘图    UIGraphicsBeginImageContext(targetSize);    // 定义图片缩放后的区域,因此无需保持纵横比,所以直接缩放    CGRect anchorRect = CGRectZero;    anchorRect.origin = CGPointZero;    anchorRect.size = targetSize;    // 将图片本身绘制到auchorRect区域中    [self drawInRect:anchorRect];    // 获取绘制后生成的新图片    UIImage* newImage = UIGraphicsGetImageFromCurrentImageContext();    UIGraphicsEndImageContext();    // 返回新图片    return newImage;}// 图片旋转角度- (UIImage *)imageRotatedByRadians:(CGFloat)radians{    // 定义一个执行旋转的CGAffineTransform结构体    CGAffineTransform t = CGAffineTransformMakeRotation(radians);    // 对图片的原始区域执行旋转,获取旋转后的区域    CGRect rotatedRect = CGRectApplyAffineTransform(        CGRectMake(0.0 , 0.0, self.size.width, self.size.height) , t);    // 获取图片旋转后的大小    CGSize rotatedSize = rotatedRect.size;    // 创建绘制位图的上下文    UIGraphicsBeginImageContext(rotatedSize);    CGContextRef ctx = UIGraphicsGetCurrentContext();    // 指定坐标变换,将坐标中心平移到图片的中心    CGContextTranslateCTM(ctx, rotatedSize.width/2, rotatedSize.height/2);    // 执行坐标变换,旋转过radians弧度    CGContextRotateCTM(ctx , radians);    // 执行坐标变换,执行缩放    CGContextScaleCTM(ctx, 1.0, -1.0);    // 绘制图片    CGContextDrawImage(ctx, CGRectMake(-self.size.width / 2            , -self.size.height / 2,            self.size.width,            self.size.height), self.CGImage);    // 获取绘制后生成的新图片    UIImage* newImage = UIGraphicsGetImageFromCurrentImageContext();    UIGraphicsEndImageContext();    // 返回新图片    return newImage;}- (UIImage *)imageRotatedByDegrees:(CGFloat)degrees{    return [self imageRotatedByRadians:degrees * M_PI / 180];}- (void) saveToDocuments:(NSString*)fileName{    // 获取当前应用路径下的Documents目录下的指定文件名对应的文件路径    NSString *path = [[NSHomeDirectory()        stringByAppendingPathComponent:@"Documents"]        stringByAppendingPathComponent:fileName];    // 保存PNG图片    [UIImagePNGRepresentation(self)        writeToFile:path atomically:YES];//  // 保存JPG图片  //  [UIImageJPEGRepresentation(self, 1.0) // 1.0代表图片压缩比率//      writeToFile:path atomically:YES];}@end

ViewController:

#import "UIImage+CWCategory.h"#define PI 3.141592653#define degtorad(X) ((X)/180*PI)#define radtodeg(X) ((X)*180/PI)@interface ViewController ()@property (strong, nonatomic) IBOutlet UIImageView *imageView;@end@implementation ViewControllerUIImage* srcImage;CGFloat currentScale;CGFloat currentRotation;- (void)viewDidLoad{    [super viewDidLoad];    [UIApplication sharedApplication].statusBarHidden = YES;    srcImage = [UIImage imageNamed:@"seashore.png"];    // 设置图片直接显示在中间(不进行任何缩放)    self.view.contentMode = UIViewContentModeCenter;    // 设置imageView初始显示的图片    self.imageView.image = srcImage;    // 设置初始的缩放比例    currentScale = 1;    currentRotation = 0;    // 设置imageView允许用户交互,支持多点触碰    self.imageView.userInteractionEnabled = YES;    self.imageView.multipleTouchEnabled = YES;    // 创建UIPinchGestureRecognizer手势处理器,该手势处理器激发scaleImage:方法    UIPinchGestureRecognizer* gesture = [[UIPinchGestureRecognizer alloc]        initWithTarget:self action:@selector(scaleImage:)];    // 为imageView添加手势处理器    [self.imageView addGestureRecognizer:gesture];    // 创建UIRotationGestureRecognizer手势处理器,该手势处理器激发rotateImage:方法    UIRotationGestureRecognizer* rotateGesture =        [[UIRotationGestureRecognizer alloc]        initWithTarget:self action:@selector(rotateImage:)];    // 为imageView添加手势处理器    [self.imageView addGestureRecognizer:rotateGesture];}- (void) scaleImage:(UIPinchGestureRecognizer*)gesture{    CGFloat scale = gesture.scale;    // 根据手势处理器的缩放比计算图片缩放后的目标大小    CGSize targetSize = CGSizeMake(srcImage.size.width * scale * currentScale,        srcImage.size.height * scale * currentScale);    // 对图片进行缩放、旋转    self.imageView.image = [[srcImage imageByScalingToSize:targetSize]        imageRotatedByRadians:currentRotation];    // 如果手势结束    if(gesture.state == UIGestureRecognizerStateEnded)    {        // 计算结束时候图片的缩放比        currentScale = scale * currentScale;    }}- (void) rotateImage:(UIRotationGestureRecognizer*)gesture{    // 获取手势旋转的弧度    CGFloat rotation = gesture.rotation;    //NSLog(@"rotation %f",rotation);    //NSLog(@"du %f",radtodeg(rotation));    // 根据当前缩放比计算图片缩放后的目标大小    CGSize targetSize = CGSizeMake(srcImage.size.width * currentScale,                                   srcImage.size.height * currentScale);    // 对图片进行缩放、旋转    self.imageView.image = [[srcImage imageByScalingToSize:targetSize]        imageRotatedByRadians:currentRotation + rotation];    // 如果旋转手势结束    if(gesture.state == UIGestureRecognizerStateEnded)    {        currentRotation = currentRotation + rotation;    }}@end

6.SwipeGesture

- (void)viewDidLoad{    [super viewDidLoad];    self.view.backgroundColor = [UIColor lightGrayColor];    self.gv.layer.borderWidth = 2;    self.gv.layer.cornerRadius = 6;    // 设置gv控件支持用户交互    self.gv.userInteractionEnabled = YES;    // 设置gv控件支持多点触碰    self.gv.multipleTouchEnabled = YES;    for (int i = 0 ; i < 4 ; i++)    {        // 创建手势处理器,指定使用该控制器的handleSwipe:方法处理轻扫手势        UISwipeGestureRecognizer* gesture = [[UISwipeGestureRecognizer alloc]                                             initWithTarget:self action:@selector(handleSwipe:)];        // 设置该点击手势处理器只处理i个手指的轻扫手势        gesture.numberOfTouchesRequired = 1;        // 指定该手势处理器只处理1 << i方向的轻扫手势        gesture.direction = 1<<i;        // 为gv控件添加手势处理器。        [self.gv addGestureRecognizer:gesture];    }}// 实现手势处理器的方法,该方法应该声明一个形参。// 当该方法被激发时,手势处理器会作为参数传给该方法的参数。- (void) handleSwipe:(UISwipeGestureRecognizer*)gesture{    // 获取轻扫手势的方向    NSUInteger direction = gesture.direction;    // 根据手势方向的值得到方向字符串    NSString* dirStr = direction == UISwipeGestureRecognizerDirectionRight        ? @"向右" : (direction == UISwipeGestureRecognizerDirectionLeft        ? @"向左" : (direction == UISwipeGestureRecognizerDirectionUp        ? @"向上" :@"向下"));    NSUInteger touchNum = gesture.numberOfTouchesRequired;    self.label.text = [NSString stringWithFormat:        @"用户使用%lu个手指进行轻扫,方向为:%@" , (unsigned long)touchNum , dirStr];    // 指定2秒后清除label的文本    [self.label performSelector:@selector(setText:)        withObject:@"" afterDelay:2];}

7.PanGesture

  • (void)viewDidLoad
    {
    [super viewDidLoad];
    self.view.backgroundColor = [UIColor lightGrayColor];
    self.gv.layer.borderWidth = 2;
    self.gv.layer.cornerRadius = 6;
    // 设置gv控件支持用户交互
    self.gv.userInteractionEnabled = YES;
    // 设置gv控件支持多点触碰
    self.gv.multipleTouchEnabled = YES;
    // 创建手势处理器,指定使用该控制器的handlePan:方法处理手势
    UIPanGestureRecognizer* gesture = [[UIPanGestureRecognizer alloc]
    initWithTarget:self action:@selector(handlePan:)];
    // 设置该拖动手势处理器至少需要1个手指
    gesture.minimumNumberOfTouches = 1;
    // 设置该拖动手势处理器最多需要2个手指
    gesture.maximumNumberOfTouches = 2;
    // 为gv控件添加手势处理器。
    [self.gv addGestureRecognizer:gesture];
    }
    // 实现手势处理器的方法,该方法应该声明一个形参。
    // 当该方法被激发时,手势处理器会作为参数传给该方法的参数。
  • (void) handlePan:(UIPanGestureRecognizer*)gesture
    {
    CGPoint velocity = [gesture velocityInView:self.gv];
    CGPoint translation = [gesture translationInView:self.gv];
    self.label.text = [NSString stringWithFormat:
    @”水平速度为:%g, 垂直速度为:%g, 水平位移为:%g, 垂直位移为:%g”
    , velocity.x , velocity.y
    , translation.x , translation.y];
    // 指定2秒后清除label的文本
    //[self.label performSelector:@selector(setText:)
    // withObject:@”” afterDelay:2];
    }

8.LongPressGesture

#define BUTTON_WIDTH 80#define BUTTON_HEIGHT 30@implementation ViewControllerNSInteger bnIndex;- (void)viewDidLoad{    [super viewDidLoad];    self.view.backgroundColor = [UIColor lightGrayColor];    // 创建一个手势处理器,用于检测、处理长按手势    UILongPressGestureRecognizer* gesture = [[UILongPressGestureRecognizer        alloc]initWithTarget:self action:@selector(longPress:)];    // 为该控件添加手势处理器    [self.view addGestureRecognizer:gesture];}- (void) panAction:(UIPanGestureRecognizer*)gesture {   CGPoint point = [gesture locationInView:self.view];    UIButton * btn = (UIButton*)[gesture view];    [btn setFrame:CGRectMake(point.x - BUTTON_WIDTH/2 , point.y - BUTTON_HEIGHT/2, BUTTON_WIDTH, BUTTON_HEIGHT)];}- (void) longPress:(UILongPressGestureRecognizer*)gesture{    CGPoint point = [gesture locationInView:self.view];    ,        UIButton* bn = [UIButton buttonWithType:UIButtonTypeRoundedRect];        // 为该按钮设置文本        [bn setTitle:[NSString stringWithFormat:@"按钮%ld", (long)bnIndex]            forState:UIControlStateNormal];        // 设置该bn按钮的大小和位置        bn.frame = CGRectMake( point.x,point.y, BUTTON_WIDTH , BUTTON_HEIGHT);        // 为该按钮添加事件处理方法        [bn addTarget:self action:@selector(remove:)     forControlEvents:UIControlEventTouchUpInside];        // 将按钮添加到应用界面的UIView控件中        [self.view addSubview:bn];        UIPanGestureRecognizer * panGesture = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(panAction:)];        [bn addGestureRecognizer:panGesture];        bnIndex ++;    }}- (void) remove:(UIButton*)sender{    for (UIGestureRecognizer * ges in [sender gestureRecognizers]) {        [sender removeGestureRecognizer:ges];    }    // 删除事件源控件(激发该事件的按钮)    [sender removeFromSuperview];}@end

9.手势的依赖

[A requireGestureRecognizerToFail:B]函数,它可以指定当A手势发生时,即便A已经滿足条件了,也不会立刻触发,会等到指定的手势B确定失败之后才触发。

- (void)viewDidLoad {    [super viewDidLoad];    // 轻拍1次    UITapGestureRecognizer* tap1 = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(SingleTap:)];    //点击的次数    tap1.numberOfTapsRequired = 1;    //给self.view添加一个手势监测;    [self.view addGestureRecognizer:tap1];    // 轻拍2次    UITapGestureRecognizer* tap2 = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(DoubleTap:)];    tap2 = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(DoubleTap:)];    tap2.numberOfTapsRequired = 2;    [self.view addGestureRecognizer:tap2];    // 手势互斥或依赖,双击手势确定监测失败才会触发单击手势的相应操作    [tap1 requireGestureRecognizerToFail:tap2];}-(void)SingleTap:(UITapGestureRecognizer*)recognizer{    NSLog(@"轻拍1次");}-(void)DoubleTap:(UITapGestureRecognizer*)recognizer{    NSLog(@"轻拍2次");}
0 0
原创粉丝点击