码迷,mamicode.com
首页 > 移动开发 > 详细

结合UIImageView实现图片的移动和缩放

时间:2015-08-28 15:28:33      阅读:166      评论:0      收藏:0      [点我收藏+]

标签:

iphone ipad 开发:结合UIImageView实现图片的移动和缩放 因为种种原因,需要在iphone应用中实现图片查看功能,由于iphone屏幕支持多点触摸,于是是想到用“手势”来实现图片的 实时缩放和移动。借鉴无所不在的internet网络资料之后,终于实现此一功能,过程如下。 一、首先实现原图显示(不缩放) 新建MoveScaleImageView类,继承uiview。用于加载一个UIImage。它有两个主要的成员, 一个UIImage对象用于指定一个内存图片,一个UIImageView控件用于显示图片。 @interface MoveScaleImageView : UIView { UIImage* originImage; UIImageView* imageView; } -(void)setImage:(UIImage*)_image; @end @implementation MoveScaleImageView -(id)initWithFrame:(CGRect)frame { if(self=[super initWithFrame:frame]) { imageView=[[UIImageView alloc]init]; [self addSubview:imageView]; //使图片视图支持交互和多点触摸 [imageView setUserInteractionEnabled:YES]; [imageView setMultipleTouchEnabled:YES]; } return self; } -(void)dealloc { originImage=nil; imageView=nil; [super dealloc]; } -(void)setImage:(UIImage *)_image { originImage=[[UIImage alloc]initWithCGImage:_image.CGImage]; [imageView setImage:originImage]; [imageView setFrame:CGRectMake(0, 0, _image.size.width, _image.size.height)]; //[imageView setNeedsLayout]; } @end 最主要的就是setImage方法。 MoveScaleImageView的使用很简单。在ViewController中构造一个MoveScaleImageView, 然后用一个加载了图片文件的UIImage对象设置其image成员: UIImage* image=[UIImage imageNamed:@"df.jpg"]; MoveScaleImageView*fileContent = [[MoveScaleImageView alloc]initWithFrame:CGRectMake(0, 44, 320, 436)]; [fileContent setImage:image]; 由于在这里我们没有对图片进行任何的缩放处理,对于小图片会位于屏幕的左上角,并在其他地方留下空白; 对于尺寸大于屏幕的图片,则图片不能完全显示: 一、识别手势(单点触摸与多点触摸) 要想识别手势(gesture),必须响应4个手势的通知方法(参考“iphone3开发基础教程”第13章的内容): touchesBegan,touchesMoved,touchesEnded和touchesCancelled。 首先,我们先来考虑单点触摸情况,这比较简单一些。在单点触摸情况下,移动手指,imageView中的图片可以被拖动, 这样,对于比较大的图片,我们可以通过拖动来浏览图片的各个部分,当然,对于能一次显示下全部的图片就不需要拖动了。 修改类MoveScaleImageView,在.h中增加一些声明: @interface MoveScaleImageView : UIView { UIImage* originImage; UIImageView* imageView; CGPoint gestureStartPoint;//手势开始时起点 CGFloat offsetX,offsetY;//移动时x,y方向上的偏移量 CGFloat curr_X,curr_Y;//现在截取的图片内容的原点坐标 } -(void)setImage:(UIImage*)_image; -(void)moveToX:(CGFloat)x ToY:(CGFloat)y; @end 然后实现touchesBegan和touchesMoved方法。 touchesBegan方法比较简单,记录下手指第一次触摸的位置。因为任何一个拖动都必然有一个起点和终点。 -(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { UITouch *touch=[touches anyObject]; gestureStartPoint=[touch locationInView:self]; //NSLog(@”touch:%f,%f”,gestureStartPoint.x,gestureStartPoint.y); } 然后是手指移动后回调的touchesMoved方法: -(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event { UITouch* touch=[touches anyObject]; CGPoint curr_point=[touch locationInView:self]; //分别计算x,和y方向上的移动 offsetX=curr_point.x-gestureStartPoint.x; offsetY=curr_point.y-gestureStartPoint.y; //只要在任一方向上移动的距离超过Min_offset,判定手势有效 if(fabsf(offsetX)>= min_offset||fabsf(offsetY)>=min_offset) { [self moveToX:offsetX ToY:offsetY]; gestureStartPoint.x=curr_point.x; gestureStartPoint.y=curr_point.y; } } 在这里我们做了一个简单的判断,只有手指移动了超过一定像素(min_offset常量)后,才识别为拖动手势, 并调用moveToX方法。在这个方法中,需要不断的更新手指移动的坐标,因为这是一个连续的过程。 -(void)moveToX:(CGFloat)x ToY:(CGFloat)y { //计算移动后的矩形框,原点x,y坐标,矩形宽高 CGFloat destX,destY,destW,destH; curr_X=destX=curr_X-x; curr_Y=destY=curr_Y-y; destW=self.frame.size.width; destH=self.frame.size.height; if(destX<0) {//左边界越界处理 curr_X=destX=0; } if(destY<0) {//上边界越界处理 curr_Y=destY=0; } if(destX+destW>originImage.size.width) {//右边界越界处理 curr_X=destX=originImage.size.width-destW; } if(destY+destH>originImage.size.height) {//右边界越界处理 curr_Y=destY=originImage.size.height-destH; } //创建矩形框为本fame CGRect rect = CGRectMake(destX, destY, self.frame.size.width, self.frame.size.height); imageView.image=[UIImage imageWithCGImage:CGImageCreateWithImageInRect([originImage CGImage], rect)]; } 在这个方法中,我们采用了一种特殊的处理方式:截取大图片的一部分,并将截取部分显示在imageView里。 我这样做的理由,是因为这是最简单、最容易的实现方式。我参考过网上的几种实现方式, 发现基本上都需要使用Quartz2D API,并且实现起来要复杂得多。最终从闭路电视监控系统中 得到了启发(想象一下,安保人员通过移动鼠标控制镜头移动的场景)。 我们设计了一个矩形框,用它作为模拟的镜头: CGRect lensRect; //设置镜头的大小 同时还设计了一个全局变量用于记录图片缩放过程中的缩放倍率: CGFloat scale; //缩放比例 当跟踪到手指移动时,让“镜头”做反向运动(为什么是反向运动?因为我们模拟的是“拖动”效果,而不是“跟踪”效果, 二者是恰恰相反的)。并通过 UIImage imageWithCGImage:CGImageCreateWithImageInRect方法, 将镜头中的图像捕捉到imageView中。 这样,移动操作实际上转换成了计算矩形框的位置。当然,我们也要做好边界判断,否则当矩形框超出图片原来的范围时, 会发生扭曲缩放的现象。 接下来看怎样识别多点触摸。识别单点触摸和多点触摸其实非常简单,判断touchesBegan的touches参数的count属性即可: -(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)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) { UITouch *touch=[touches anyObject]; gestureStartPoint=[touch locationInView:self]; } } 在上面的方法中,我们根据touches的count判断是否是单点触摸并进行分别的处理。 对于2点触摸,我们记录了两指间的距离并记录在全局的CGFloat变量originSpace中。 spaceToPoint方法是一个简单函数,使用中学中学过的3角函数计算2点间距离: -(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); } 在两点触摸中,需要识别2个手势:外向捏合、内向捏合。通常前者使图像放大,而后者可使图像缩小。 在touchesMoved方法中,这样处理: -(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event { 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 if([touches count]==1) { …… (省略了部分代码) } } 先简单判断了是否为有效捏合(我们为此定义了一个常量min_offset),如果是,则计算手指有效移动长度和 手势开始时的两指间距的商,以此作为缩放比例。然后调用scaleTo方法: -(void)scaleTo:(CGFloat)x { scale*=x; //缩放限制:>=0.1,<=10 scale=(scale<0.1)?0.1:scale; scale=(scale>10)?10:scale; //重设imageView的frame [self moveToX:0 ToY:0]; } 这里,为防止用户无限制的对图像进行“捏合”操作,我们限制了scale的值在0.1-10之间 (当然你可以将这个阀值定义为常量)。然后调用了一个原地的移动操作,即前面的moveTo方法。 然而为支持缩放下的图片移动,这个方法被我们更改了: -(void)moveToX:(CGFloat)x ToY:(CGFloat)y { CGPoint point=CGPointMake(x, y); //重设镜头 [self resetLens:point]; imageView.image=[UIImage imageWithCGImage: CGImageCreateWithImageInRect([originImage CGImage], lensRect)]; [imageView setFrame: CGRectMake(0, 0, lensRect.size.width*scale, vlensRect.size.height*scale)]; } 其中更多的代码被我们移到了另一个方法resetLens中: -(void)resetLens:(CGPoint)point {//设置镜头大小和位置 CGFloat x,y,width,height; //===========镜头初始大小========= width=self.frame.size.width/scale; height=self.frame.size.height/scale; //===========调整镜大小不得超过图像实际大小========== if(width>originImage.size.width) { width=originImage.size.width; } if(height>originImage.size.height) { height=originImage.size.height; } //计算镜头移动的位置(等比缩放) x=lensRect.origin.x-point.x/scale; y=lensRect.origin.y-point.y/scale; //左边界越界处理 x=(x<0)?0:x; //上边界越界处理 y=(y<0)?0:y; //右边界越界 x=(x+width>originImage.size.width)?originImage.size.width-width:x; //下边界越界处理 y=(y+height>originImage.size.height)?originImage.size.height-height:y; //镜头等比缩放 lensRect=CGRectMake(x, y, width, height); } 这些代码跟原来moveToX方法中的代码有些许的不同,主要是增加了对scale变量的引入,因为在缩放模式下, 镜头的移动都是被scale系数缩放过的。通代码中的注释,我们不难理解整个代码。 这样,大图片经过“捏合”操作可以在屏幕上完全显示出来(上面原来基本看不清楚的第2张图片现在是一台苹果电脑): 当然,把小图片“捏合”放大成大图片也是可以的。此外通过手指的移动,能查看图片的不同部分。

结合UIImageView实现图片的移动和缩放

标签:

原文地址:http://www.cnblogs.com/QQ765286788/p/4766387.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!