视图,绘图,文本以及图像,动画
Views
view是屏幕上的矩形区域,它有两个用途,第一个用途是绘画内容,第二个用途是做事件处理。事件的传递是依赖于视图的。UIView事实上是UIResponder类的子类,它实现了父类的事件处理机制。视图在iphone上是分层地排列的,你会有根视图,视图会子视图化它的超视图,所有视图都有单一的超视图,它只能从一个超视图那里产生,但是它可能有多个子视图,它可以有一整个排列的子视图,这个排列是有顺序的,这个顺序指明它们在屏幕上的显示顺序,如果你有一个视图,它覆盖了或者跟随了另外的视图,它是子视图列表的超视图。第二个会在第一个上面显示,实际上会隐藏了它。
视图是有等级结构的,它们必须有一些群体,一定会有一些视图,是不会有超视图的,实际上我们有一个独立的类来代表这些特殊类型的根视图,它是UIWindow,与其他平台中的窗口不同,UIWindow是完整独立于视图的东西。iphone上的UIWindow是UIView的一个子视图。它有一些额外添加的行为能使它知道如何成为没有超视图的视图群之一。唯一一个可以显示在屏幕上但没有超视图的视图就是UIWindow.实际上iphone应用程序一般只有一个UIWindow,你用在那里的所有视图来创建应用。
在IB或者利用UIView方法添加和删除视图
-(void)addSubview:(UIView *)view;
-(void)removeFromSuperview;
手动操作视图层级
-(void)insertSubview:(UIView *)view atIndex:(int)index;
-(void)insertSubview:(UIView *)view belowSubview:(UIView *)view ;
-(void)insertSubview:(UIView *)view aboveSubview:(UIView *)view ;
-(void)exchangeSubviewAtIndex:(int)index withSubviewAtIndex:(int )otherIndex;
视图层次结构所有权
超视图使拥有它的子视图的所有权,当你在超视图中添加一个子视图使,它对子视图对象取一个retain,当你调用视图的addSubview myotherView时,myotherView的retain count会被超视图增加,因此这个超视图拥有这个子视图对象的retain。如果你不需要保持对于子视图的指针,比如标签或一些在创建和设置后不再修改属性的东西,你不需要保持对它的指针,你可以立即释放它,因为现在超视图保持了retain count。你释放它会释放在它那里的retain count,超视图会在超视图消失的时候释放它。这个例子中内存管理是被超视图管理的。如果你不想在屏幕上绘制某些东西,你不需要从视图层级中移除它,在UIView中有一个hidden属性,如果你设置它为YES,它会告诉渲染器不需要绘制它,那视图的任何部分都不会在屏幕显示,因此你可以保留它在视图层级中,它仍被它的超视图所retain,它只是不会绘制和参与事件处理。
与视图有关的结构(CG是核心图形的意思)
CGPoint(定义了一个点)
空间上的定位:{x,y}所以的值都是浮点值。
CGSize
大小:{width,height},定义了大小,宽度和高度
CGRect
定位以及大小:{origin,size}CGRect结合了点和大小,同时包含了CGPoint和CGSize
iphone中的坐标体系
位置和大小
有两种方式可以表示位置和大小
Frame是在超视图中的坐标系统(也就是表达视图在超视图中的位置和大小时用Frame)
Bounds是在自身的坐标系统中(表达在自身坐标系统中的大小时用Bounds)
frame实际上是不会被保存在视图中的,frame被定义为UIView的一个属性,但是UIView实际上从不保存frame的数值,它存储了其他的一些数值,它们可以用来计算frame的值,当你向视图询问它的frame时,它提取这些数值来计算新的frame,再返回给你。它存储的第一个数值是bounds,另外一个数值就是这个视图的中心点被存储了下来。该中心点是再超视图的坐标系统中这个视图的精确中心位置。
Frame和bounds使用的选择
frame在用在超视图的坐标系统时,是非常容易使用的,只要你没有奇怪的旋转或比例或者其他什么东西,它是很清晰的,而且把东西定位在frame上是很容易的,它定义了在超视图的坐标系统中的位置和大小。如果你使用视图并且想把它定位在一些超视图上,你可能想要用frame,它基本上会工作的很好,但是它在你处理转换的时候会变成一个问题。因此如果你使用视图,你可能会用frame,如果你实现视图,你会更经常使用bounds。基本上,当你开始谈到hit test视图以及视图中的触摸时,你会谈论视图坐标空间的触摸,而视图的坐标空间本身就是bounds,在这种情况下,你可能会使用bounds。这些都是视角的问题。当你从外部看一个视图,你通常会设置frame,当你工作于实现这个视图并从内部看它,你会使用bounds。
创建视图
手动创建
Views利用-initWithFrame进行初始化
CGRect frame=CGRectMake(0,0,200,150);
UIView *myview=[[UIView alloc] initWithFrame:frame];
例子:
CGRect frame=V=CGRect(20,45,140,21);
UILabel *label=[[UILabel alloc] initWithFrame:frame];
[window addSubview:label];
[label setText:@”Number of sides:”];
[label release];
自定义视图
继承父类UIView,并且需要重载父类方法:-(void)drawRect:(CGRect)rect;
对于事件处理,你需要重载下列方法:(如果你要实现其中的一种,那么你需要实现全部)
-(void)touchesBegin:(NSSet *)touches withEvent:(UIEvent *)event;
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event;
-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event;
-(void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event;
绘制视图
-(void)drawRect:(CGRect)rect;当你实现了这个函数,你不能手动地调用该函数,系统会自动为你调用该函数。
当一个视图需要被重画时,使用:-(void)setNeedsDisplay;
例子。在你的控制器中:
-(void)setNumberOfSides:(int)sides{
numberOfSides=sides;
[polygonView setNeedsDisplay];//在polygonView中实现了-(void)drawRect:(CGRect)rect;
}
CoreGraphics和Quartz 2D(矢量图形库)
UIKit实际上自身只对图形绘制操作提供了非常有限的支持,它主要依赖于核心图像框架所提供的功能。而核心图形实际上并不是一个Objective-C框架,它是一个奇怪的c语言框。CoreGraphics和Quartz 2D绘制引擎定义了一些简单的但是功能强大的绘制工具,包括:图形环境(是你所有绘图的地方),Quartz 2D还提供了转换(使用了CGAffineTransform结构),Quartz 2D还提供了路径的概念,这让你定义可以绘画和填充的屏幕区域,Quartz 2D定义了颜色,这让你可以指定正在绘画的颜色,它还是有字体和一些其他东西。
图形环境(Graphics Context)
图形环境实际上是绘制你所有核心图形调用的地方,你不想直接调用drawRect的部分原因是UIKit在调用你的drawRect功能之前已经负责为你配置好一个图像环境。UIKit已经创建了一个给你在其中绘画的图形环境,所有绘图都在这不透明的图形环境中完成,这个不透明是语言上的,而不是图形上的,你在图形环境的元素中并没有可见性,但它可能是透明的,它可以用来绘制位图存储。
这是所有UIView的支持。它还可以用来绘给内存环境制PDF,因此如果你想要绘制一段用作屏幕外绘画的内存,你可以用context,你可以绘制PDF,然后保存一个PDF文件,context基本上是所有这些不同的支持存储的抽象。你可以调用UIGraphicsGetCurrentContext来进入drawRect中的图形环境,这会取回UIKit为你创建的CGContextRef,因此你能在所有核心图形中的调用中把它用作参数去确定你应该绘制在哪里,然后你用CoreGraphics调用去改变context的设置。对于取回的context,如果你在drawRect中调用它,只会在drawRect调用持续时有效。这个context会在drawRect返回时被UIKit撤销分配,它不会再有效。如果你保存了context,最好的情况下什么都没有在你绘制它时出现在屏幕上,最坏的情况时程序崩溃,所以不要保存context。
CG Wrappers
一些CG功能被UIKit所包装
UIColor
对于常用的方法很方便
当绘制的时候很容易去设置颜色
UIColor *redColor = [UIColor redColor];//取回的值是autoreleased
[redColor set];//更新到心图形环境中
UIFont
获得系统字体
通过名称获得字体
UIFont *font = [UIFont systemFontOfSize:14.0];
[mylabel setFont:font];
简单的drawRect例子:
-(void)drawRect:(CGRect)rect {
CGRect bounds = [self bounds];
[[UIColor grayColor] set];
UIRectFill (bounds);
CGRect squre = CGRectMake (10, 10, 50, 100);
[[UIColor redColor] set];
UIRectFill (squre );
[[UIColor blackColor] set];
UIRectFrame (squre );
}
绘制更加复杂的形状
对于drawRect的通常步骤:
1.获得图形环境
2.定义一个路径
3.设置颜色
4.绘制或填充颜色
5.如果必要的画就重复
路径
CoreGraphic路径定义形状
可以由直线,弧线,曲线以及矩形构成
创建和绘制路径是两种不同的操作
定义路径,然后再绘制路径
定义路径的是叫CGPath
对于使用路径有两种不同的功能集
1.CGContext上有一些便利的功能
2.CGPath方法用于创建可用的路径
一个简单的路径例子
-(void)drawRecr:(CGRect)rect {
CGContextRef context = UIGraphicsGetCurrentContext();
[[UIColor grayColor] set];
//告诉context我们想开始在context自身创建这个隐藏的路径
CGContextBeginPath (context);
CGContextMoveToPoint (context, 75, 10);
CGContextAddLineToPoint (context , 10, 150);
CGContextAddLineToPoint (context , 160, 150);
//封闭一条路径,那么调用函数 CGContextClosePath 把当前点和起点连接起来
CGContextClosePath (context);
[[UIColor redColor] setFill];
[[UIColor blackColor] setStrocke];
//利用上面设置的填充和绘制颜色,同时填充和绘制路径
CGContextDrawPath (context, KCGPathFillStrocke);}
Image & Text
创建图像:
1.[UIImage imageNamed:(NSString *)name]
当你使用iamgeNamed装载图像时,需要带上图像文件的后缀名
2.从磁盘上装载图像
[UIImage initWithContentsOfFile:(NSString *) path];
3.从内存中读取数据
[UIImage initWithData: (NSData *) data];
根据context创建图像
例子:
- (UIImage *) polygonImageOfSize:(CGSize )size {
UIImage *result = null;
UIGraphicsBeginImageContext (size);//创建一个指向与图形大小相同的一个内存块的CGContextRef
//调用你自己的绘制代码
result = UIGraphicsGetImageFromCurrentContext();
UIGraphicsEndImageContext();
Return result;
}
获得图像数据
NSData *UIImagePNGRepresentation (UIImage * image);
NSData *UIImageJPGRepresentation (UIImage * image);
绘制文本和图像
在drawRect中你可以绘制UIImage
- [UIImage drawAtPoint :(CGPoint)point]
- [UIImage drawInRect :(CGRect)rect]
- [UIImage drawAsPatternInRect :(CGRect)rect]//重复填充直至填满矩形
在drawRect中绘制NSString
-[NSString drawAtPoint:(CGPoint )point withFont:(UIFont *)font]
Text ,Images 以及UIKit 视图
UILabel
是UIView 的子类并知道如何去绘制自身。
属性包括:字体,字体颜色,阴影以及文本的对齐方式
UIImageView
属性包括:图像(image),动画图像的阵列(animatedImages),动画长度(animatedDuration)以及播放次数(animatedRepeatCount)。
还有另外一个属性叫contentmode,决定图像的适应尺寸,位置
视图的属性以及动画(View Properties & Animation)
View Animation例子:
-(void)showAdvancedOptions {
//开始动画时为该动画段取名为advancedAnimation
[UIView beginAnimation:@"advancedAnimation" context:nil];(context是传进来的)
[UIView setAnimationDuration:0.3];
optionView.alpha=1.0;
CGRect polygonFrame = polygonView.frame;
polygonFrame.orign.y+=20;
polygonView.frame=polygonFrame;
//结束该动画块
[UIView commitAnimations];
}
如果我们需要关心什么时候动画结束,我们可以设置一些委派功能,在setAnimation区块中你还可以设置另外一个属性,那个是setAnimation委派,它让你指定一些对象去接收当动画开始和结束时的委派信息。我们能设置的委派功能是animationWillStart context,这里的context是我们传递进来的context以及我们调用beginAnimations时传递进来的名字。
UIView 动画对于委托是允许的
[UIView setAnimationDelegate:myController];
myController在动画之前之后将会有一个回调
-(void)animationQillStart:(NSString *)animationID context:(void *)context;
-(void)animationDidStop:(NSString *)animationID finished:(NSNumber *) finished context:(void *)context;//当它在你动画的结束时被调用,它会告诉你是否这个动画运行到了结束,比如在相同的对象上开始,另外一个带着相同属性的动画,这个能中断前一个动画,如果它是因为这样被中断了,它会在finished参数中告诉你它没有结束,否则只要它顺利地运行到终点,finished会事yes。
可以自定义selector,如下:
[UIView setAnimationWillStartSelector:@selector(animationWillStart)];
[UIView setAnimationDidStopSelector:@selector(animationDidStop)];
注意:你的视图层级在这期间永远只会在这两种状态中的一个。永远不会有中间状态,具体来说,如果在这过程中你需要询问你的frame,它只会告诉你0或者200(最大值)中的一个,永远不会返回150等其他中间数值。
所有的动画都是建立在核心动画这个框架上的。核心动画框架驱动所有动画。
核心动画
它基本上是一个加速渲染引擎,在新的iPhone上,它是在底层使用OpenGL来完成,它以加速的方式使用OpenGL图形渲染来动画所有这些不同的层。所有的UIView都被一个独立的CALayer所支持。CALayer是核心动画创建来代表一个层的对象。你可以从视图取得CALayer,所有视图,所有CALayer都有自己的后台储存,在早期的系统中,应用程序时直接绘制帧缓冲中的,你有一大块的内存被用来代表屏幕上显示的东西,应用程序通过写入比特到内存去,直接绘制它,这会再屏幕上显示出来,在Mac OS X 和一些较新版本的windows中引入了这个新的复合模式,基本上,所有单独的窗口都有自己的后台储存,应用程序不是绘画到屏幕帧缓存,而是到窗口的后台储存,然后会有一个复合传递,其他进程会运行和取出所有那些后台存储混合它们到一起,最后产生屏幕图像。核心动画更进一步,现在不止每个单独窗口有自己的后台存储,而且每个单独视图也有自己的后台存储,你的所有视图都有一个单独的内存块去代表视图中会绘制什么。如果你有两个重叠的视图,drawRect会为每个视图都调一次去绘制整个内容,即使你谈出了一个或相对地移动它们,这改变了部分会重叠,drawRect永远不会再次被调用,因为它们两个都有自己的后台存储,在任何时间,核心动画都有每个视图看起来完整的副本,只要它没有被其他东西覆盖,那么它们就能相互移动。这些存储的缓存都是用来渲染前台的,它实际上是由一个单独的进程完成。Render Tree 实际上是所有这些的缓存结果。它是在另外一个进程中,它甚至不在你的应用程序中,即使你的应用程序卡死了,千万不要再你的主事件循环中做很长时间的处理以及成块地处理触摸等事件,但是如果你真的做了,你设置的动画会一直运行,因为它们实际是发生在另外一个进程中。
视图转换(View Transforms)
每一个视图都有一个transform属性
转换能让你旋转,调节比例和平移它
默认的属性是特性转换
CGAffineTransform结构式被核心图像定义来定义这些转换的结构。
CGAffineTransform 方法功能
CGAffineTransformScale(transform,xScale,yScale)
CGAffineTransformRotate(transform,angle)
CGAffineTransformTranslate(transform,xDelta,yDelta)