IOS 照片编辑的view封装的实例详解

IOS 照片编辑的view封装

该控件有旋转,缩放,拖动,剪裁的功能,封装成了一个ImageCropperView类

需要导入的库:QuartzCore.framework

ImageCopperView.h

#import <UIKit/UIKit.h>

@protocol ImageCropperDelegate;

@interface ImageCropperView : UIView {
  UIImageView *imageView;

  id <ImageCropperDelegate> delegate;
}

@property (nonatomic, retain) UIImage *image;
@property (nonatomic, retain) UIImage *croppedImage;

@property (nonatomic, assign) id <ImageCropperDelegate> delegate;

@property (nonatomic, assign) BOOL enable;
@property (nonatomic, assign) BOOL isPaning;

- (void)setup;
- (void)finishCropping;
- (void)reset;

@end

@protocol ImageCropperDelegate <NSObject>
- (void)changeMoveStateWithCropper:(UIPanGestureRecognizer*)gesture Crop:(ImageCropperView*)imageCrop;
@end

ImageCopperView.m

#import "ImageCropperView.h"
#import <QuartzCore/QuartzCore.h>
#include <math.h>
#import "UIImage+Rotation.h"

@interface ImageCropperView()
{
  @private
  CGSize _originalImageViewSize;
}

@property (nonatomic, retain) UIImageView *imageView;
@end

@implementation ImageCropperView

@synthesize imageView, image = _image, delegate, croppedImage;

- (void)setup
{
  _enable = YES;
  self.clipsToBounds = YES;
  self.backgroundColor = [UIColor clearColor];

  self.imageView = [[[UIImageView alloc] initWithFrame:CGRectMake(0.0, 0.0, self.frame.size.width, self.frame.size.height)] autorelease];
  imageView.userInteractionEnabled = YES;
  [self addSubview:imageView];

  UIRotationGestureRecognizer *rotateGes = [[UIRotationGestureRecognizer alloc] initWithTarget:self action:@selector(rotateImage:)];
  [imageView addGestureRecognizer:rotateGes];
  [rotateGes release];

  UIPinchGestureRecognizer *scaleGes = [[UIPinchGestureRecognizer alloc] initWithTarget:self action:@selector(scaleImage:)];
  [imageView addGestureRecognizer:scaleGes];
  [scaleGes release];

  UIPanGestureRecognizer *moveGes = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(moveImage:)];
  [moveGes setMinimumNumberOfTouches:1];
  [moveGes setMaximumNumberOfTouches:1];
  [imageView addGestureRecognizer:moveGes];
  [moveGes release];
}

- (id)initWithFrame:(CGRect)frame {
  self = [super initWithFrame:frame];

  if (self) {
    self.frame = frame;
    [self setup];
  }

  return self;
}

float _lastTransX = 0.0, _lastTransY = 0.0;
- (void)moveImage:(UIPanGestureRecognizer *)sender
{
  _isPaning = YES;
  if (delegate&&[delegate respondsToSelector:@selector(changeMoveStateWithCropper:Crop:)]) {
    [delegate changeMoveStateWithCropper:sender Crop:self];
  }else{
    return;
  }
  if (sender.numberOfTouches != 1||_enable == NO) {
    return;
  }
  //获取在视图中手势的触点位置
  CGPoint translatedPoint = [sender translationInView:self];

  if([sender state] == UIGestureRecognizerStateBegan) {
    _lastTransX = 0.0;
    _lastTransY = 0.0;
  }

  CGAffineTransform trans = CGAffineTransformMakeTranslation(translatedPoint.x - _lastTransX, translatedPoint.y - _lastTransY);
  //CGAffineTransformConcat将imageView.transform和trans两个动画连续起来
  CGAffineTransform newTransform = CGAffineTransformConcat(imageView.transform, trans);
  _lastTransX = translatedPoint.x;
  _lastTransY = translatedPoint.y;
  NSLog(@"_lastTransX==%f,_lastTransY==%f",_lastTransX,_lastTransY);
  imageView.transform = newTransform;
}

float _lastScale = 1.0;
- (void)scaleImage:(UIPinchGestureRecognizer *)sender
{
  _isPaning = NO;
  if (sender.numberOfTouches != 2||_enable == NO) {
    return;
  }

  if([sender state] == UIGestureRecognizerStateBegan) {

    _lastScale = 1.0;
    return;
  }

  CGFloat scale = [sender scale]/_lastScale;

  CGAffineTransform currentTransform = imageView.transform;
  CGAffineTransform newTransform = CGAffineTransformScale(currentTransform, scale, scale);
  [imageView setTransform:newTransform];

  _lastScale = [sender scale];
}

float _lastRotation = 0.0;
- (void)rotateImage:(UIRotationGestureRecognizer *)sender
{
  _isPaning = NO;
  if (sender.numberOfTouches != 2||_enable == NO) {
    return;
  }

  if([sender state] == UIGestureRecognizerStateEnded) {

    _lastRotation = 0.0;
    return;
  }

  CGFloat rotation = -_lastRotation + [sender rotation];

  CGAffineTransform currentTransform = imageView.transform;
  CGAffineTransform newTransform = CGAffineTransformRotate(currentTransform,rotation);
  [imageView setTransform:newTransform];

  _lastRotation = [sender rotation];

}

- (void)setImage:(UIImage *)image
{
  if (_image != image) {
    _image = [image retain];
  }

  float _imageScale = self.frame.size.width / image.size.width;
  self.imageView.frame = CGRectMake(0, 0, image.size.width*_imageScale, image.size.height*_imageScale);
  _originalImageViewSize = CGSizeMake(image.size.width*_imageScale, image.size.height*_imageScale);
  imageView.image = image;
  imageView.center = CGPointMake(self.frame.size.width/2.0, self.frame.size.height/2.0);
}

- (void)finishCropping {
  float zoomScale = [[self.imageView.layer valueForKeyPath:@"transform.scale.x"] floatValue];
  float rotate = [[self.imageView.layer valueForKeyPath:@"transform.rotation.z"] floatValue];

  float _imageScale = _image.size.width/_originalImageViewSize.width;
  CGSize cropSize = CGSizeMake(self.frame.size.width/zoomScale, self.frame.size.height/zoomScale);
  CGPoint cropperViewOrigin = CGPointMake((0.0 - self.imageView.frame.origin.x)/zoomScale,
                      (0.0 - self.imageView.frame.origin.y)/zoomScale);

  if((NSInteger)cropSize.width % 2 == 1)
  {
    cropSize.width = ceil(cropSize.width);
  }
  if((NSInteger)cropSize.height % 2 == 1)
  {
    cropSize.height = ceil(cropSize.height);
  }

  CGRect CropRectinImage = CGRectMake((NSInteger)(cropperViewOrigin.x*_imageScale) ,(NSInteger)( cropperViewOrigin.y*_imageScale), (NSInteger)(cropSize.width*_imageScale),(NSInteger)(cropSize.height*_imageScale));

  UIImage *rotInputImage = [self.image imageRotatedByRadians:rotate];
  CGImageRef tmp = CGImageCreateWithImageInRect([rotInputImage CGImage], CropRectinImage);
  self.croppedImage = [UIImage imageWithCGImage:tmp scale:self.image.scale orientation:self.image.imageOrientation];
  CGImageRelease(tmp);
}

- (void)reset
{
  self.imageView.transform = CGAffineTransformIdentity;
}

- (void)dealloc {
  self.image = nil;
  self.croppedImage = nil;
  self.imageView = nil;

  [super dealloc];
}

@end

对UIImage添加了一个category

UIImage+Rotation.h

#import <UIKit/UIKit.h>

@interface UIImage (Rotation)

- (UIImage *)imageRotatedByRadians:(CGFloat)radians;
- (UIImage *)imageRotatedByDegrees:(CGFloat)degrees;

@end

UIImage+Rotation.m

#import "UIImage+Rotation.h"

/************
 角度=弧度/Pi*180
 弧度=角度/180*Pi
 *************/

CGFloat DegreesToRadians(CGFloat degrees) {return degrees * M_PI / 180;};
CGFloat RadiansToDegrees(CGFloat radians) {return radians * 180/M_PI;};

@implementation UIImage (Rotation)

- (UIImage *)imageRotatedByRadians:(CGFloat)radians
{
  return [self imageRotatedByDegrees:RadiansToDegrees(radians)];
}

- (UIImage *)imageRotatedByDegrees:(CGFloat)degrees
{
  /*****
   CGAffineTransformMakeRotation
   通过指定角度来创建一个旋转矩阵
   CGAffineTransformRotate
   在已存在的矩阵中使用旋转
   *****/
  UIView *rotatedViewBox = [[UIView alloc] initWithFrame:CGRectMake(0,0,self.size.width, self.size.height)];
  CGAffineTransform t = CGAffineTransformMakeRotation(DegreesToRadians(degrees));
  //给view旋转角度
  rotatedViewBox.transform = t;
  CGSize rotatedSize = rotatedViewBox.frame.size;
  [rotatedViewBox release];
  //开始编辑图形上下文
  UIGraphicsBeginImageContext(rotatedSize);
  //定义一个图形上下文
  CGContextRef bitmap = UIGraphicsGetCurrentContext();
  //沿x轴移动rotatedSize.width/2,y轴移动rotatedSize.height
  CGContextTranslateCTM(bitmap, rotatedSize.width/2, rotatedSize.height/2);
  //以原点(左下角)为中心旋转DegreesToRadians(degrees)弧度,正角度逆时针,负角度顺时针
  CGContextRotateCTM(bitmap, DegreesToRadians(degrees));
  //缩放x轴,y轴方向
  CGContextScaleCTM(bitmap, 1.0, -1.0);
  //绘制位图
  CGContextDrawImage(bitmap, CGRectMake(-self.size.width / 2, -self.size.height / 2, self.size.width, self.size.height), [self CGImage]);
  //赋值给UIImage
  UIImage *resImage = UIGraphicsGetImageFromCurrentImageContext();
  //结束绘制
  UIGraphicsEndImageContext();
  return resImage;
}

@end;

ViewController.m

#import "ViewController.h"
#import <QuartzCore/QuartzCore.h>
#import "ImagecropperView.h"

@interface ViewController ()<ImageCropperDelegate>{
}

@property (nonatomic, retain) IBOutlet ImageCropperView *cropper;
@property (nonatomic, retain) IBOutlet UIImageView *result;
@property (retain, nonatomic) IBOutlet UIImageView *resultSecond;
@property (nonatomic, retain) IBOutlet UIButton *btn;
@property (retain, nonatomic) IBOutlet ImageCropperView *cropperSecond;
@property (retain, nonatomic) IBOutlet UIButton *cropButton;

@end

@implementation ViewController
//@synthesize cropper, result, btn;

- (void)viewDidLoad
{
  [super viewDidLoad];
  // Do any additional setup after loading the view, typically from a nib.
  _cropper.layer.borderWidth = 1.0;
  _cropper.layer.borderColor = [UIColor blueColor].CGColor;
  _cropper.delegate = self;
  [_cropper setup];
  _cropper.image = [UIImage imageNamed:@"2.jpg"];
  [_btn addTarget:self action:@selector(buttonClicked) forControlEvents:UIControlEventTouchUpInside];

  _cropperSecond.layer.borderColor = [UIColor blackColor].CGColor;
  _cropperSecond.layer.borderWidth = 2.0;
  _cropperSecond.delegate = self;
  [_cropperSecond setup];
  _cropperSecond.image = [UIImage imageNamed:@"1.jpg"];
  [_cropButton addTarget:self action:@selector(tapCropButton) forControlEvents:UIControlEventTouchUpInside];
}

- (void)buttonClicked
{
  if ([_btn.currentTitle isEqualToString:@"Crop1"]) {
    [_cropper finishCropping];//保存
    _result.image = _cropper.croppedImage;
    _cropper.hidden = YES;
    [_btn setTitle:@"Back" forState:UIControlStateNormal];
    [_btn setTitle:@"Back" forState:UIControlStateHighlighted];
  }else
  {
    [_cropper reset];
    _cropper.hidden = NO;
    [_btn setTitle:@"Crop1" forState:UIControlStateNormal];
    [_btn setTitle:@"Crop1" forState:UIControlStateHighlighted];
    _result.image = nil;
  }
  _cropperSecond.enable = YES;
  _cropper.enable = YES;
}

- (void)tapCropButton{
  if ([_cropButton.currentTitle isEqualToString:@"Crop2"]) {
    [_cropperSecond finishCropping];
    _cropperSecond.enable = NO;
    _resultSecond.image = _cropperSecond.croppedImage;
    _cropperSecond.hidden = YES;
    [_cropButton setTitle:@"Back" forState:UIControlStateNormal];
    [_cropButton setTitle:@"Back" forState:UIControlStateHighlighted];
  }else
  {
    [_cropperSecond reset];

    _cropperSecond.hidden = NO;
    [_cropButton setTitle:@"Crop2" forState:UIControlStateNormal];
    [_cropButton setTitle:@"Crop2" forState:UIControlStateHighlighted];
    _resultSecond.image = nil;
  }
  _cropperSecond.enable = YES;
  _cropper.enable = YES;
}

#pragma mark - ImageCropperDelegate
- (void)changeMoveStateWithCropper:(UIPanGestureRecognizer*)gesture Crop:(ImageCropperView*)imageCrop{
  if (gesture.state == UIGestureRecognizerStateEnded) {
    NSLog(@"点击编辑器结束,两个_cropper都可以进行编辑");
    _cropperSecond.enable = YES;
    _cropper.enable = YES;
  }
}

- (void)touchesBegan:(NSSet*)touches withEvent:(UIEvent*)event;
{
//判断点击在控件上
  UITouch *touch = [touches anyObject];
  if ([_cropper pointInside:[touch locationInView:_cropper] withEvent:nil]) {
    NSLog(@"_cropper1 被触摸,禁用_cropper2");
    _cropperSecond.enable = NO;
  }else if ([_cropperSecond pointInside:[touch locationInView:_cropperSecond] withEvent:nil]){
    NSLog(@"_cropper2 被触摸,禁用_cropper1");
    _cropper.enable = NO;
  }
}

- (void)dealloc {
  [_cropperSecond release];
  [_cropButton release];
  [_resultSecond release];
  [super dealloc];
}
- (void)viewDidUnload {
  [self setCropperSecond:nil];
  [self setCropButton:nil];
  [self setResultSecond:nil];
  [super viewDidUnload];
}
@end

截图:

最后要注意,因为我是用xib做的,拖上去的UIView要将其Class改成ImageCropperView

如有疑问请留言或者到本站社区交流讨论,感谢阅读,希望能帮助到大家,谢谢大家对本站的支持!

(0)

相关推荐

  • 详解iOS开发中的转场动画和组动画以及UIView封装动画

    一.转场动画 CAAnimation的子类,用于做转场动画,能够为层提供移出屏幕和移入屏幕的动画效果.iOS比Mac OS X的转场动画效果少一点 UINavigationController就是通过CATransition实现了将控制器的视图推入屏幕的动画效果 属性解析: type:动画过渡类型 subtype:动画过渡方向 startProgress:动画起点(在整体动画的百分比) endProgress:动画终点(在整体动画的百分比) 转场动画代码示例 1.界面搭建 2.实现代码 复制代码

  • IOS 照片编辑的view封装的实例详解

    IOS 照片编辑的view封装 该控件有旋转,缩放,拖动,剪裁的功能,封装成了一个ImageCropperView类 需要导入的库:QuartzCore.framework ImageCopperView.h #import <UIKit/UIKit.h> @protocol ImageCropperDelegate; @interface ImageCropperView : UIView { UIImageView *imageView; id <ImageCropperDelega

  • IOS 开发之PickerView自定义视图的实例详解

    IOS 开发之PickerView自定义视图的实例详解 例如选择国家,左边是名称右边是国家,不应该使用两列,而是自定义PickerView的一列,可以通过xib来实现. 注意,虽然PickerView也是一列,但是数据源方法是@required,所以必须实现. 因此,核心思想就是一列,自定义PickerView的行视图. 使用viewForRow方法可以设定行视图. 这样的视图可以通过xib和它的控制器进行封装: Xib的控制器继承自UIView类即可. 控制器维护一个用于设置数据的模型对象fl

  • IOS 开发之swift中手势的实例详解

    IOS 开发之swift中手势的实例详解 手势操作主要包括如下几类 手势 属性 说明 点击 UITapGestureRecognizer numberOfTapsRequired:点击的次数:numberOfTouchesRequired:点击时有手指数量 设置属性 numberOfTapsRequired 可以实现单击,或双击的效果 滑动 UISwipeGestureRecognizer direction:滑动方向 direction 滑动方向分为上Up.下Down.左Left.右Right

  • Android Intent封装的实例详解

    Android Intent封装的实例详解 什么是Intent: Intent是协调应用间.组件之间的通讯和交互.通过Intent你可以启动Activity.Service.Broadcasts.更可以跨程序调用第三方组件.例如:启动拨打电话界面.音乐播放等. 组件     启动 Activity startActicity() Service startService(),bindService( ) Broadcasts sendBroadcast() 使用Intent: 栗子:在一个Act

  • SpringBoot学习系列之MyBatis Plus整合封装的实例详解

    前言 MyBatis-Plus是一款MyBatis的增强工具(简称MP),为简化开发.提高效率,但我们并没有直接使用MP的CRUD接口,而是在原来的基础上封装一层通用代码,单表继承我们的通用代码,实现了单表的基础get.save(插入/更新).list.page.delete接口,使用Vo去接收.传输数据,实体负责与数据库表映射. 这样做的目的是与我们之前的那套jpa保持编码风格上的一致,当我们的通用接口不能满足要求时,应当先考虑使用MP的Service层CRUD接口,然后是Mapper的接口,

  • IOS开发之字典转字符串的实例详解

    IOS开发之字典转字符串的实例详解 在实际的开发需求时,有时候我们需要对某些对象进行打包,最后拼接到参数中 例如,我们把所有的参数字典打包为一个 字符串拼接到参数中 思路:利用系统系统JSON序列化类即可,NSData作为中间桥梁 //1.字典转换为字符串(JSON格式),利用 NSData作为桥梁; NSDictionary *dic = @{@"name":@"Lisi",@"sex":@"m",@"tel&qu

  • IOS给xcode工程关联pod的实例详解

    IOS给xcode工程关联pod的实例详解 1. 新建Podfile文件 内容如下: platform :ios,'7.0' target :LJMediaPalyer do pod 'MQTTClient' end 2. cd 到当前工程的目录下 然后在控制台输入pod install命令 如有疑问请留言或者到本站社区交流讨论,本站关于IOS 开发的文章还有很多,还请大家多多搜索查阅,希望通过本文能帮助到大家,谢谢大家对本站的支持!

  • IOS中计算缓存文件的大小判断实例详解

    IOS中计算缓存文件的大小判断实例详解 IOS中计算缓存文件的大小判断,在这里分享一下自己的心得,希望和大家一起分享技术,如果有什么不足,还请大家指正.写出这篇目的,就是希望大家一起成长,我也相信技术之间没有高低,只有互补,只有分享,才能使彼此更加成长. 实例代码: //获取缓存文件路径 -(NSString *)getCachesPath{ // 获取Caches目录路径 NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCaches

  • IOS 调整内存中的图片大小实例详解

    IOS 调整内存中的图片大小实例详解 在从网路download图片,或者从相册读取图片的时候,如果ImageView的本身就是固定的300*200,那么载入2000*2000的图片是很浪费内存的. 2000*2000的内存占用是2000*2000*4bit 以下两个函数可以用来创建一个新的按照固定大小的图片.简单来说,就是Core Graphics来创建一个bitmap,然后生成一个图片. - (UIImage*)imageWithImage:(UIImage*)image scaledToSi

  • Android屏幕及view的截图实例详解

    Android屏幕及view的截图实例详解 屏幕可见区域的截图 整个屏幕截图的话可以用View view = getWindow().getDecorView(); public static Bitmap getNormalViewScreenshot(View view) { view.setDrawingCacheEnabled(true); view.buildDrawingCache(); return view.getDrawingCache(); } scrollview的整体截屏

随机推荐