这两天在做图像剪裁功能。一致在尝试不同的解决方案,包括从cocoachina查找的资料创意,一直不满意最终的效果。经过2天努力,终于完美实现。
方案实现功能如下:
1、可拖拽、缩放选区,截取所选区域部分图像
2、可缩放被裁剪图像,移动被裁剪图像,方便用户精确裁剪。
使用注意事项:
1、不要将代码实现的视图类实例添加为UIScrollView类实例的子视图。因为UIScrollView类实例会屏蔽子视图的拖拽事件(除非您自己实现一个子类,继承UIScrollView类,并按照苹果官方指南重写指定的几个方法。个人认为比较麻烦,而且不方便)。
2、若要获取选区对应的区域部分图像。使用
- PictureCutView * pictureCutView;
- pictureCutView.choosenImage; //获取选取区域部分图像对应的UIImage对象
方案已尽我最大努力实现优化,如果您有更好的优化意见,欢迎留言提出。
附注:
1、本代码部分参考网上资料,部分代码来源与网上。
2、本人保留代码的版权,如需使用代码,请保留版权说明。
- //
- // PictureCutView.h
- // Taonan
- //
- // Created by zengconggen on 11-9-19.
- // Copyright 2011 yongmengsoft. All rights reserved.
- //
- #import <UIKit/UIKit.h>
- @interface PictureCutView : UIView {
- @public
- UIImage * sourceImage;
- UIImage * choosenImage;
- @private
- UIImageView *bgImageView; //要编辑的图片视图
- UIImageView *imageView; //选择区域框的图片视图
- CGPoint mouseXY; //鼠标单击坐标
- CGFloat sx,sy,w,h,ex,ey,sxm,sym;
- /*
- sx:imageView的起始X坐标
- sy:imageView的起始y坐标
- w:imageView的width:宽
- h:imageView的height:高
- ex:imageView的右下角坐标endX:终止X坐标
- ey:imageView的endY:终止Y坐标
- sxm:触摸点距离imageView的起始X坐标的位置
- sym:触摸点距离imageView的起始Y坐标的位置
- */
- NSInteger number; //记录触摸点不同位置的不同处理方案
- UIImage * originImage;
- CGFloat originSpace;
- CGFloat scale;
- CGFloat totalScale;
- UITouch * currentTouch;
- }
- @property(nonatomic,retain, setter=setSourceImage:) UIImage * sourceImage;
- @property(nonatomic,readonly, getter=getChoosenImage) UIImage * choosenImage;
- @property(nonatomic,retain) UIImageView *bgImageView;
- @property(nonatomic,retain) UIImageView *imageView;
- @property(nonatomic) CGPoint mouseXY;
- @property(nonatomic) CGFloat sx,sy,w,h,ex,ey,sxm,sym;
- @property(nonatomic) NSInteger number;
- @property(nonatomic,retain) UIImage * originImage;
- @property(nonatomic) CGFloat scale;
- @property(nonatomic) CGFloat totalScale;
- @property(nonatomic) CGFloat originSpace;
- @property(nonatomic,retain) UITouch * currentTouch;
- - (void)setSourceImage:(UIImage *)image;
- - (UIImage *)getChoosenImage;
- //裁剪图片
- -(UIImage *)imageFromImage:(UIImage *)image inRect:(CGRect)rect;
- //改变图片的大小
- -(UIImage *)scaleFromImage:(UIImage *)image toSize:(CGSize)size;
- -(CGFloat)spaceToPoint:(CGPoint)first FromPoint:(CGPoint)two;
- -(void)scaleTo:(CGFloat)x;
- @end
- //
- // PictureCutView.m
- // Taonan
- //
- // Created by zengconggen on 11-9-19.
- // Copyright 2011 yongmengsoft. All rights reserved.
- //
- #import "PictureCutView.h"
- #define CONTROL_WIDTH 20
- #define MIN_OFFSET 5
- @implementation PictureCutView
- @synthesize sourceImage, choosenImage;
- @synthesize bgImageView,imageView,mouseXY,sx,sy,w,h,ex,ey,sxm,sym,number,originImage,scale,totalScale,originSpace;
- @synthesize currentTouch;
- //触摸事件
- -(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
- {
- //NSLog(@"touchesBegan count:%d", [touches count]);
- [super touchesBegan:touches withEvent:event];
- if ([touches count] ==2) {
- NSArray* twoTouches=[touches allObjects];
- originSpace=[self spaceToPoint:[[twoTouches objectAtIndex:0] locationInView:self]
- FromPoint:[[twoTouches objectAtIndex:1]locationInView:self]];
- }else if ([touches count] ==1 && currentTouch == nil) {
- imageView.alpha = 1.0;
- //CGRect bgRect = bgImageView.frame;
- //获取触摸点
- UITouch *touch = [touches anyObject];
- self.currentTouch = touch;
- mouseXY = [touch locationInView:self];
- //NSLog(@"++++ mouseXY in touchesBegan(Touch:%@): (%f, %f)", touch,mouseXY.x, mouseXY.y);
- //获取触摸时的各个参数
- sx = imageView.frame.origin.x;
- sy = imageView.frame.origin.y;
- w = imageView.frame.size.width;
- h = imageView.frame.size.height;
- ex = sx+w;
- ey = sy+h;
- //记录触摸点的所在位置
- if(mouseXY.x>sx+CONTROL_WIDTH&&mouseXY.x<ex-CONTROL_WIDTH&&mouseXY.y>sy+CONTROL_WIDTH&&mouseXY.y<ey-CONTROL_WIDTH){
- //NSLog(@"启动时已经进入");
- sxm = mouseXY.x-sx;
- sym = mouseXY.y-sy;
- number = 1;
- }else if(mouseXY.x>=ex-CONTROL_WIDTH && mouseXY.x<=ex+CONTROL_WIDTH && mouseXY.y>=ey-CONTROL_WIDTH && mouseXY.y<=ey+CONTROL_WIDTH){
- number = 2;
- }else if(mouseXY.x>=ex-CONTROL_WIDTH && mouseXY.x<=ex+CONTROL_WIDTH && mouseXY.y>=sy-CONTROL_WIDTH && mouseXY.y<=sy+CONTROL_WIDTH){
- number = 3;
- }else if(mouseXY.x>=sx-CONTROL_WIDTH && mouseXY.x<=sx+CONTROL_WIDTH && mouseXY.y>=ey-CONTROL_WIDTH && mouseXY.y<=ey+CONTROL_WIDTH){
- number = 4;
- }else if(mouseXY.x>=sx-CONTROL_WIDTH && mouseXY.x<=sx+CONTROL_WIDTH && mouseXY.y>=sy-CONTROL_WIDTH && mouseXY.y<=sy+CONTROL_WIDTH){
- number = 5;
- }else {
- number = 6;
- }
- }else {
- [super touchesBegan:touches withEvent:event];
- }
- }
- //拖动事件
- -(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
- {
- NSAutoreleasePool * aPool = [[NSAutoreleasePool alloc] init];
- //NSLog(@"touchesMoved count:%d", [touches count]);
- [super touchesMoved:touches withEvent:event];
- if ([touches count] ==2) {
- @synchronized(self)
- {
- NSArray* twoTouches=[touches allObjects];
- CGFloat currSpace=[self spaceToPoint:[[twoTouches objectAtIndex:0] locationInView:self]
- FromPoint:[[twoTouches objectAtIndex:1]locationInView:self]];
- //如果先触摸一根手指,再触摸另一根手指,则触发touchesMoved方法而不是touchesBegan方法
- //此时originSpace应该是0,我们要正确设置它的值为当前检测到的距离,否则可能导致0除错误
- if (originSpace==0) {
- originSpace=currSpace;
- }
- if (fabsf(currSpace-originSpace)>=MIN_OFFSET) {//两指间移动距离超过min_offset,识别为手势“捏合”
- CGFloat s=currSpace/originSpace;//计算缩放比例
- //NSLog(@"++++ scale: %f", s);
- [self scaleTo:s];
- originSpace = currSpace;
- }
- }
- }else if ([touches count] ==1) {
- //NSLog(@"Moveimageview:%@,poing的坐标:%@,sx:%f,sy:%f,w:%f,h:%f,ex:%f,ey:%f,sxm:%f,sym:%f,number:%i",imageView,NSStringFromCGPoint(mouseXY),sx,sy,w,h,ex,ey,sxm,sym,number);
- //NSLog(@"imageView的X坐标:%f,Y坐标:%f",imageView.frame.origin.x,imageView.frame.origin.y);
- UITouch *touch = [touches anyObject];
- if ([touch isEqual:currentTouch]) {
- CGPoint point = [touch locationInView:self];
- CGRect bgRect = bgImageView.frame;
- CGFloat x,y,width,height;
- if(number == 1){
- //NSLog(@"修改前的边界:%@,X:%f,Y:%f,width:%f,height:%f",NSStringFromCGPoint(mouseXY),x,y,width,height);
- //触摸点在imageview中
- x = point.x-sxm;
- y = point.y-sym;
- width = w;
- height = h;
- }else if (number == 2) {
- //触摸点在imageview的右下角
- x = sx;
- y = sy;
- width = point.x-sx;
- height = point.y-sy;
- }else if(number == 3){
- //触摸点在imageview的右上角
- x = sx;
- y = point.y;
- if(point.y<sy){
- height = h+sy-point.y;
- }else {
- height = ey-point.y;
- }
- if(point.x<ex){
- width = w-(ex-point.x);
- }else {
- width = w+point.x-ex;
- }
- }else if(number == 4){
- //触摸点在imageview的左下角
- x = point.x;
- y = sy;
- if(point.y<ey){
- height = h-(ey-point.y);
- }else {
- height = h+point.y-ey;
- }
- width = ex-point.x;
- }else if(number == 5){
- //触摸点在imageview的左上角
- x = point.x;
- y = point.y;
- height = ey-point.y;
- width = ex-point.x;
- }else {
- //触摸点不在imageview上
- //挪动整个视图
- CGFloat offsetX = point.x-mouseXY.x;
- CGFloat offsetY = point.y-mouseXY.y;
- CGFloat bgSX = bgRect.origin.x+offsetX;
- CGFloat bgSY = bgRect.origin.y+offsetY;
- CGFloat bgEX = bgSX+bgRect.size.width;
- CGFloat bgEY = bgSY+bgRect.size.height;
- if (offsetX > 0) {
- if (bgRect.size.width > self.frame.size.width) {
- //判断左点是否入界,控制左边不显示空白
- if (bgSX > 0) {
- bgSX = 0;
- offsetX = bgSX-bgRect.origin.x;
- }
- }else {
- //控制右点是否出界,控制图片完整显示
- if (bgEX > self.frame.size.width) {
- bgEX = self.frame.size.width;
- offsetX = bgEX - bgRect.origin.x-bgRect.size.width;
- }
- }
- }else {
- if (bgRect.size.width > self.frame.size.width) {
- //判断右点是否入界,控制左边不显示空白
- if (bgEX < self.frame.size.width) {
- bgEX = self.frame.size.width;
- offsetX = bgEX - bgRect.origin.x-bgRect.size.width;
- }
- }else {
- //控制左点是否出界,控制图片完整显示
- if (bgSX < 0) {
- bgSX = 0;
- offsetX = bgSX-bgRect.origin.x;
- }
- }
- }
- if (offsetY > 0) {
- if (bgRect.size.height > self.frame.size.height) {
- //判断上点是否入界,控制左边不显示空白
- if (bgSY > 0) {
- bgSY = 0;
- offsetY = bgSY-bgRect.origin.y;
- }
- }else {
- //控制下点是否出界,控制图片完整显示
- if (bgEY > self.frame.size.height) {
- bgEY = self.frame.size.height;
- offsetY = bgEY - bgRect.origin.y-bgRect.size.height;
- }
- }
- }else {
- if (bgRect.size.height > self.frame.size.height) {
- //判断下点是否入界,控制左边不显示空白
- if (bgEY < self.frame.size.height) {
- bgEY = self.frame.size.height;
- offsetY = bgEY - bgRect.origin.y-bgRect.size.height;
- }
- }else {
- //控制上点是否出界,控制图片完整显示
- if (bgSY < 0) {
- bgSY = 0;
- offsetY = bgSY-bgRect.origin.y;
- }
- }
- }
- x = imageView.frame.origin.x+offsetX;
- y = imageView.frame.origin.y+offsetY;
- width = w;
- height = h;
- CGRect newBgRect = CGRectMake(bgRect.origin.x+offsetX, bgRect.origin.y+offsetY, bgRect.size.width, bgRect.size.height);
- bgImageView.frame = newBgRect;
- }
- if(x-bgRect.origin.x<0){
- width = imageView.frame.size.width;
- x=bgRect.origin.x;
- }
- if(y-bgRect.origin.y<0){
- height = imageView.frame.size.height;
- y=bgRect.origin.y;
- }
- CGFloat xL,yL;
- xL = x+width;
- if(xL>bgRect.origin.x+bgRect.size.width){
- x = bgRect.origin.x+bgRect.size.width-width;
- }
- yL = y+height;
- if(yL>bgRect.origin.y+bgRect.size.height){
- y = bgRect.origin.y+bgRect.size.height-height;
- }
- imageView.frame = CGRectMake(x, y, width, height);//容器大小的改变放在setimage前边,因为imageview的contentmode为scallapsecttofit模式
- if (number !=6) {
- UIImage *endImage = [self imageFromImage:sourceImage inRect:CGRectMake(x-bgRect.origin.x, y-bgRect.origin.y, width, height)];
- imageView.image = endImage;
- }
- mouseXY = point;
- //NSLog(@"++++ mouseXY in touchesMoved(Touch:%@): (%f, %f)", touch,mouseXY.x, mouseXY.y);
- }
- }else {
- [super touchesMoved:touches withEvent:event];
- }
- [aPool release];
- }
- - (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
- {
- //NSLog(@"touchesEnded count:%d", [touches count]);
- if ([[event allTouches] containsObject:currentTouch])
- {
- self.currentTouch = nil;
- }
- self.originSpace = 0;
- if ([touches count] ==2) {
- /*
- NSArray* twoTouches=[touches allObjects];
- CGFloat currSpace=[self spaceToPoint:[[twoTouches objectAtIndex:0] locationInView:self]
- FromPoint:[[twoTouches objectAtIndex:1]locationInView:self]];
- //如果先触摸一根手指,再触摸另一根手指,则触发touchesMoved方法而不是touchesBegan方法
- //此时originSpace应该是0,我们要正确设置它的值为当前检测到的距离,否则可能导致0除错误
- if (originSpace==0) {
- originSpace=currSpace;
- }
- if (fabsf(currSpace-originSpace)>=MIN_OFFSET) {//两指间移动距离超过min_offset,识别为手势“捏合”
- CGFloat s=currSpace/originSpace;//计算缩放比例
- [self scaleTo:s];
- }
- */
- }else {
- [super touchesEnded:touches withEvent:event];
- }
- }
- - (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
- {
- //NSLog(@"touchesCanceled count:%d", [touches count]);
- if ([[event allTouches] containsObject:currentTouch])
- {
- self.currentTouch = nil;
- }
- self.originSpace = 0;
- [super touchesCancelled:touches withEvent:event];
- }
- -(void)scaleTo:(CGFloat)x{
- scale=x;
- //totalScale *=scale;
- //重设imageView的frame
- self.sourceImage = originImage;
- }
- - (id)initWithFrame:(CGRect)aframe {
- if (self = [super initWithFrame:aframe]) {
- self.contentMode = UIViewContentModeCenter;
- self.clipsToBounds = YES;
- self.exclusiveTouch = YES;
- self.bgImageView = [[[UIImageView alloc] initWithFrame:CGRectMake(0, 0, aframe.size.width, aframe.size.height)] autorelease];
- bgImageView.contentMode =UIViewContentModeScaleAspectFit;
- bgImageView.center = CGPointMake(aframe.size.width/2, aframe.size.height/2);
- bgImageView.alpha = 0.5;
- self.imageView = [[[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)] autorelease];
- imageView.contentMode = UIViewContentModeScaleAspectFit;
- imageView.center = CGPointMake(aframe.size.width/2, aframe.size.height/2);
- [self addSubview:bgImageView];
- [self addSubview:imageView];
- [self setUserInteractionEnabled:YES];
- [self setMultipleTouchEnabled:YES];
- [bgImageView setUserInteractionEnabled:NO];
- //[bgImageView setMultipleTouchEnabled:YES];
- [imageView setUserInteractionEnabled:NO];
- //[imageView setMultipleTouchEnabled:YES];
- scale = 1.0f;
- }
- return self;
- }
- - (id)init {
- return [self initWithFrame:CGRectMake(0, 44, 320, 416)];
- }
- - (void)setSourceImage:(UIImage *)image
- {
- NSAutoreleasePool * aPool = [[NSAutoreleasePool alloc] init];
- BOOL firstDraw = YES;
- if (self.originImage != nil) {
- firstDraw = NO;
- }else {
- self.originImage = image;
- }
- CGSize newSize = CGSizeMake(bgImageView.frame.size.width*scale, bgImageView.frame.size.height*scale);
- CGFloat widthRate = newSize.width/originImage.size.width;
- CGFloat heightRate = newSize.height/originImage.size.height;
- if (widthRate < 0.5 || heightRate < 0.5) {
- CGFloat lastTotalScale = totalScale;
- totalScale = 0.5;
- scale = totalScale/lastTotalScale;
- newSize = CGSizeMake(originImage.size.width*totalScale, originImage.size.height*totalScale);
- }else if (widthRate > 5 && heightRate > 5) {
- CGFloat lastTotalScale = totalScale;
- totalScale = 5;
- scale = totalScale/lastTotalScale;
- newSize = CGSizeMake(originImage.size.width*totalScale, originImage.size.height*totalScale);
- }
- sourceImage = [[self scaleFromImage:image toSize:newSize] retain];
- totalScale = sourceImage.size.width/originImage.size.width;
- newSize = sourceImage.size;
- CGRect newBgRect;
- if (widthRate != heightRate) {
- //适应bgimageview大小
- newBgRect = CGRectMake((self.frame.size.width*scale-newSize.width)/2, (self.frame.size.height*scale-newSize.height)/2, newSize.width, newSize.height);
- }else {
- newBgRect = CGRectMake(bgImageView.frame.origin.x*scale, bgImageView.frame.origin.y*scale, newSize.width, newSize.height);
- }
- bgImageView.frame = newBgRect;
- CGRect newChooseRect = CGRectMake(imageView.frame.origin.x*scale, imageView.frame.origin.y*scale, imageView.frame.size.width*scale, imageView.frame.size.height*scale);
- imageView.frame = newChooseRect;
- if (firstDraw) {
- bgImageView.image = sourceImage;
- }
- sx = (imageView.frame.origin.x > newBgRect.origin.x)? imageView.frame.origin.x : newBgRect.origin.x;
- sy = (imageView.frame.origin.y > newBgRect.origin.y)? imageView.frame.origin.y : newBgRect.origin.y;
- w = imageView.frame.size.width;
- h = imageView.frame.size.height;
- ex = sx+w;
- ey = sy+h;
- if (ex > newBgRect.origin.x+newBgRect.size.width) {
- sx = newBgRect.origin.x+newBgRect.size.width - w;
- }
- if (ey > newBgRect.origin.y+newBgRect.size.height) {
- sy = newBgRect.origin.x+newBgRect.size.height - h;
- }
- UIImage *endImage = [self imageFromImage:sourceImage inRect:CGRectMake(sx-newBgRect.origin.x, sy-newBgRect.origin.y, w, h)];
- imageView.image = endImage;
- [aPool release];
- }
- - (UIImage *)getChoosenImage
- {
- return imageView.image;
- }
- -(UIImage *)imageFromImage:(UIImage *)image inRect:(CGRect)rect{
- //CGRect realRect = CGRectMake(rect.origin.x/totalScale , rect.origin.y/totalScale, rect.size.width/totalScale, rect.size.height/totalScale);
- CGRect realRect = rect;
- CGImageRef sourceImageRef = [image CGImage];
- CGImageRef newImageRef = CGImageCreateWithImageInRect(sourceImageRef, realRect);
- UIImage *newImage = [[[UIImage alloc] initWithCGImage:newImageRef] autorelease];
- CGImageRelease(newImageRef);
- return newImage;
- }
- -(UIImage *)scaleFromImage:(UIImage *)image toSize:(CGSize)size
- {
- CGFloat imageRate = image.size.width/image.size.height;
- CGFloat newRate = size.width/size.height;
- if (imageRate > newRate) {
- size.height = image.size.height * size.width/image.size.width;
- }else {
- size.width = image.size.width * size.height/image.size.height;
- }
- //scale = size.width/image.size.width * scale;
- UIGraphicsBeginImageContext(size);
- [image drawInRect:CGRectMake(0, 0, size.width, size.height)];
- UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
- UIGraphicsEndImageContext();
- //CGSize imgSize = newImage.size;
- return newImage;
- }
- -(CGFloat)spaceToPoint:(CGPoint)first FromPoint:(CGPoint)two{//计算两点之间的距离
- float x = first.x - two.x;
- float y = first.y - two.y;
- return sqrt(x * x + y * y);
- }
- - (void)dealloc {
- [bgImageView removeFromSuperview];
- [imageView removeFromSuperview];
- [sourceImage release];
- [bgImageView release];
- [imageView release];
- [originImage release];
- [currentTouch release];
- [super dealloc];
- }
- @end