分享一下我老师大神的人工智能教程!零基础,通俗易懂!http://blog.youkuaiyun.com/jiangjunshow
也欢迎大家转载本篇文章。分享知识,造福人民,实现我们中华民族伟大复兴!
因为种种原因,需要在iphone应用中实现图片查看功能,由于iphone屏幕支持多点触摸,于是是想到用“手势”来实现图片的实时缩放和移动。借鉴无所不在的internet网络资料之后,终于实现此一功能,过程如下。
为方便大家下载,示例代码已上传到资源:http://download.youkuaiyun.com/detail/kmyhy/4095890
一、 首先实现原图显示(不缩放)
新建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* [[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里。我这样做的理由,是因为这是最简单、最容易的实现方式。我参考过网上的几种实现方式,发现基本上都需要使用Quartz2DAPI,并且实现起来要复杂得多。最终从闭路电视监控系统中得到了启发(想象一下,安保人员通过移动鼠标控制镜头移动的场景)。
我们设计了一个矩形框,用它作为模拟的镜头:
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=[touchlocationInView: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方法中,这样处理:
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张图片现在是一台苹果电脑):

当然,把小图片“捏合”放大成大图片也是可以的。此外通过手指的移动,能查看图片的不同部分。
给我老师的人工智能教程打call!http://blog.youkuaiyun.com/jiangjunshow
功能快捷键
撤销:Ctrl/Command + Z
重做:Ctrl/Command + Y
加粗:Ctrl/Command + B
斜体:Ctrl/Command + I
标题:Ctrl/Command + Shift + H
无序列表:Ctrl/Command + Shift + U
有序列表:Ctrl/Command + Shift + O
检查列表:Ctrl/Command + Shift + C
插入代码:Ctrl/Command + Shift + K
插入链接:Ctrl/Command + Shift + L
插入图片:Ctrl/Command + Shift + G
合理的创建标题,有助于目录的生成
直接输入1次#,并按下space后,将生成1级标题。
输入2次#,并按下space后,将生成2级标题。
以此类推,我们支持6级标题。有助于使用TOC语法后生成一个完美的目录。
如何改变文本的样式
强调文本 强调文本
加粗文本 加粗文本
标记文本
删除文本
引用文本
H2O is是液体。
210 运算结果是 1024.
插入链接与图片
链接: link.
图片: ![]()
带尺寸的图片: ![]()
当然,我们为了让用户更加便捷,我们增加了图片拖拽功能。
如何插入一段漂亮的代码片
去博客设置页面,选择一款你喜欢的代码片高亮样式,下面展示同样高亮的 代码片.
// An highlighted block var foo = 'bar';
生成一个适合你的列表
- 项目
- 项目
- 项目
- 项目
- 项目1
- 项目2
- 项目3
- 计划任务
- 完成任务
创建一个表格
一个简单的表格是这么创建的:
| 项目 | Value |
|---|---|
| 电脑 | $1600 |
| 手机 | $12 |
| 导管 | $1 |
设定内容居中、居左、居右
使用:---------:居中
使用:----------居左
使用----------:居右
| 第一列 | 第二列 | 第三列 |
|---|---|---|
| 第一列文本居中 | 第二列文本居右 | 第三列文本居左 |
SmartyPants
SmartyPants将ASCII标点字符转换为“智能”印刷标点HTML实体。例如:
| TYPE | ASCII | HTML |
|---|---|---|
| Single backticks | 'Isn't this fun?' | ‘Isn’t this fun?’ |
| Quotes | "Isn't this fun?" | “Isn’t this fun?” |
| Dashes | -- is en-dash, --- is em-dash | – is en-dash, — is em-dash |
创建一个自定义列表
-
Markdown
- Text-to- HTML conversion tool Authors
- John
- Luke
如何创建一个注脚
一个具有注脚的文本。1
注释也是必不可少的
Markdown将文本转换为 HTML。
KaTeX数学公式
您可以使用渲染LaTeX数学表达式 KaTeX:
Gamma公式展示 Γ ( n ) = ( n − 1 ) ! ∀ n ∈ N \Gamma(n) = (n-1)!\quad\forall n\in\mathbb N Γ(n)=(n−1)!∀n∈N 是通过欧拉积分
Γ ( z ) = ∫ 0 ∞ t z − 1 e − t d t   . \Gamma(z) = \int_0^\infty t^{z-1}e^{-t}dt\,. Γ(z)=∫0∞tz−1e−tdt.
你可以找到更多关于的信息 LaTeX 数学表达式here.
新的甘特图功能,丰富你的文章
gantt
dateFormat YYYY-MM-DD
title Adding GANTT diagram functionality to mermaid
section 现有任务
已完成 :done, des1, 2014-01-06,2014-01-08
进行中 :active, des2, 2014-01-09, 3d
计划一 : des3, after des2, 5d
计划二 : des4, after des3, 5d
- 关于 甘特图 语法,参考 这儿,
UML 图表
可以使用UML图表进行渲染。 Mermaid. 例如下面产生的一个序列图::
这将产生一个流程图。:
- 关于 Mermaid 语法,参考 这儿,
FLowchart流程图
我们依旧会支持flowchart的流程图:
- 关于 Flowchart流程图 语法,参考 这儿.
导出与导入
导出
如果你想尝试使用此编辑器, 你可以在此篇文章任意编辑。当你完成了一篇文章的写作, 在上方工具栏找到 文章导出 ,生成一个.md文件或者.html文件进行本地保存。
导入
如果你想加载一篇你写过的.md文件或者.html文件,在上方工具栏可以选择导入功能进行对应扩展名的文件导入,
继续你的创作。
注脚的解释 ↩︎
本文介绍如何在iPhone应用中使用UIImageView实现图片的移动和缩放功能。通过手势识别,实现了单点触摸的拖动和多点触摸的捏合缩放。详细介绍了相关代码实现过程,包括设置原图显示、识别手势以及处理移动和缩放的操作。

被折叠的 条评论
为什么被折叠?



