您的位置:首页 > 产品设计 > UI/UE

iOS 笔记四:视图坐标、层次结构、自定义视图、UIBezierPath等

2015-08-26 23:04 603 查看
Views: 怎样绘制自定义内容到屏幕上?

一、视图定义

一个view代表一个矩形区域,定义了一个坐标空间。view会绘制这个矩形区域并响应事件。

一个view只有一个父view,可以有0或多个子view,一个view可以选择是否裁剪子view。

UIWindow

UIWindow是位于view层次结构中最顶层的view,一般在一个iOS应用中只有一个UIWindow.

UIWindow是view,不是窗口。

view层次结构一般都是以可视的方式在Xcode中构建,甚至自定义view也是如此。

但是也可以在代码中构建:

-(void)addSubview:(UIView *)aView;

-(void)removeFromSuperview;

在你的MVC中的视图结构中,最顶层(root)的view是 @property view!

即:UIViewController的 @property (strong, nonatomic) UIView * view;

理解这个简单的属性非常重要,比如说当自动旋转发生时,这个view的边界会发生变化。

你可以编程往这个view中增加子view, MVC中的view最终都以这个view作为父view,

即MVC中最顶层的view(root),区别于UIWindow,它是整个iOS 应用的最顶层view。

二. 坐标:View Coordinate

数据类型说明:

CGFloat: 浮点数,根据系统来决定是否是64位的,在图形编程中必须要使用CGFloat.

CGPoint: 有2个CGFloat的C struct:x和y。

CGSize: 有2个CGFloat的C struct: width和height.

CGRect: 包括一个CGPoint origin和一个CGSize size.

坐标:

1.左上角是坐标原点。

2.单位是“点”(points)而不是像素(pixel).

一般情况下你不需要关心你绘制的屏幕一个点有多少个像素

字体和图形会根据高度来自动适配。

但是如果你绘制一些表现很详细的图像,你可能想知道这个。

有一个UIView的属性会告诉你:

@property CGFloat contentScaleFactor; //returns pixels per point on the screen this view is on.

这个属性不是只读的。

3.视图(view)有3个属性与它的位置、大小相关:

@property CGRect bounds; //视图的绘制区域起点和大小

@property CGPoint center; //在父视图坐标空间中,你视图的中心点。

@preperty CGRect frame; //在父视图坐标空间中,一个能包括你的视图的bounds.size的矩形。

4.使用frame和center来确定视图的位置:

这2个属性是由父视图使用的,绝对不要把它们用在你的子类实现中。

你可能觉得frame.size一定一直等于bounds.size,但是你错了,

因为视图是可以旋转(缩放、平移)的,frame定义旋转、缩放、平移后视图所占矩形的大小。



以上视图中,

view B的bounds = ((0,0), (200, 250))

View B 的frame = ((140, 65), (320,320))

View B center = (300, 225)

三. 创建视图

1.一般情况下使用Xcode创建视图,如果是自定义视图,那么拖拽一个UIView到场景中,

在Identity Inspector中把类属性UIView更换为你的自定义类。

2.怎样在代码中创建视图:alloc然后initWithFrame:(UIView的指定初始化器).

也可以只使用init,这时frame是CGRectZero.

3.代码示例:

CGRect labelRect = CGRectMake(20, 20, 50, 30);
UILabel * label = [[UILable allc] initWithFrame:labelRect];
label.text = @"Hello!";
[self.view addSubview:label];


四. 自定义视图

1.什么情况下需要创建自定义视图:

A.需要自定义绘制图形;

B.需要以特殊的方式处理触摸事件;

2.实现自定义绘制非常简单,只需要创建一个UIView的子类,重写:

-(void)drawRect:(CGRect)aRect;

你可以选择是否需要绘制到aRect外的区域。

3.绝对不要自己调用drawRect!

相应的,使用以下方法,让iOS知道你的视图需要刷新了:

-(void)setNeedDisplay;

-(void)setNeedDisplayInRect:(CGRect);

4.怎样实现drawRect:方法

直接使用Core Graphics framework(C API,非面向对象的).

可以使用面向对象的UIBezierPath类。

5.Core Graphics 基本概念

A.获取一个内容(context)用来把图形绘制到里面去;

B.建立路径(paths);

C.设置颜色、字体、纹理、线宽、线距等;

D.描边或者填充刚刚创建的路径(path).

6.UIBezierPath

可以实现上面所有的功能

五. 内容(上下文? context)

1.内容决定你绘制图形的目标地点,如屏幕、不在屏幕上的Bitmat、PDF、打印机等。

2.一般正常的绘制,UIKit会给你建立当前内容,但是它只在drawRect调用期间有效。

每次调用drawRect都会新建一个内容,所以不要缓存drawRect中当前的图形内容。

3.怎么得到这个神奇的内容:

UIBezierPath绘制到当前内容,所以你不需要为它获取内容;

但是如果你直接使用Core Graphics C 函数,你需要指定内容,

调用以下函数获取当前图形内容:

CGContextRef context = UIGraphicsGetCurrentContext();

六. 定义路径(path)

开始定义路径

UIBezierPath * path = [[UIBezierPath alloc] init];

移动到指定点,增加直线或弧线

[path moveToPoint:CGPointMake(75, 10)];

[path addLineToPoint:CGPointMake(160, 150)];

[path addLineToPoint:CGPointMake(10, 150)];

关闭路径

[path closePath];

路径已经被创建,我们可以描边或者填充它,事实上这个时候还没有开始绘制。

[[UIColor greenColor] setFill];
[[UIColor redColor] setStroke];
[path fill];
[path stroke];


七. 图形状态

1.可以设置绘制状态(属性),如:

path.lineWidth = 2.0; //line width in points(not pixels)

2.也可以绘制圆角矩形、椭圆等。

UIBezierPath * roundedRect 
 = [[UIBizerPath bezierPathWithRoundRect:(CGRect)bounds 
               cornerRadius:(CGFloat)radius];
UIBezierPath * oval = [UIBezierPath bezierPathWithOvalInRect:(CGRect)bounds];
[roundedRect stroke];
[oval fill];
3.可以使用贝塞尔路径来裁剪你的绘制

[roundedRect addClip]; //this would clip all drawing to be inside the roundedRect

4.在UIView中使用透明、

UIColor可以使用alpha,这就是在drawRect中使用透明绘制的方式

UIView的backgroundColor属性也可以设置成透明值。

如果你的视图需要部分透明,确保@porperty BOOL opaque为NO。

UIView的@property CGFloat alpha可以使整个view部分透明。

5.为特殊处理定义绘制子函数(subroutine)

如果你想定义一个基础函数来绘制一些事物,又不想跟调用函数的图形状态混淆,

你可以使用保存和恢复内容(context)函数。

-(void)drawGreenCircle:(CGContextRef)ctxt
{
 CGContextSaveGSState(ctxt);
 [[UIColor greenColor] setFill];
 //draw my circle
 CGContextRestoreGState(ctxt);
}
-(void)drawRect:(CGRect)aRect 
{
 CGContextRef context = UIGraphicsGetCurrentContext();
 [[UIColor redColor] setFill];
 //do some stuff
 [self drawGreenCircle:context];
 //do more stuff and expect fill color to be red.
}


八. 绘制文字

1.一般情况下我们使用UILabel作为一个子视图来在我们的视图中绘制文字,

但是有时候我们需要在drawRect:中绘制文字。

2.使用NSAttributedString在drawRect:中绘制文字,

NSAttributedString * text = ...;

[text drawAtPoint:(CGPoint)p]; //NSAttrbutedString instance method added by UIKit.

绘制的时候,文字会占用多大的空间呢?

CGSize textSize = [text size]; //another UIKit NSAttributedString instance method.

你可能会困惑为什么Foundation(一个非UI框架)中会有绘制函数,

这些NSAttributedString方法通过一个叫做"categories"的机制定义在UIKit中。

"Categories"是一种Objective-C方式,用来给现有类增加方法却不需要派生子类。

九. 绘制图片

1.UIImageView就像绘制文字时的UILabel,它可以绘制图片,

但是有时候你也需要在drawRect中绘制图片。

2.从资源(Resources)目录的文件中创建一个UIImage:

UIImage * image = [UIImage imageNamed:@"foo.jpg"];

也可以从文件路径或raw data中创建:

UIImage * image = [[UIImage alloc] initWithContentOfFile:(NSString *)fullPath];

UIImage * image = [[UIImage alloc] initWithData:(NSData *)imageData];

甚至你也可以用CGContext函数绘制一个UIImage:

UiGraphicsBeginContext(CGSize);
// draw with CGContext functions
UIImage * myImage = UIGraphicsGetImageFromCurrentContext();
UIGraphicsEndImageContext();
3.创建完UIImage后,可以把UIImage绘制到当前图形内容(graphics context)中:

UIImage * image = ...;
[image drawAtPoint:(CGPoint)p]; // p is upper left corner of the image
[image drawInRect:(CGRect)r];  // scales the image to fit in r
[image drawAsPatternInRect:(CGRect)patRect]; // tiles the image into patRect
4.另外,你可以从UIImage中得到PNG或JPG格式的数据:

NSData * jpgData = UIImageJPEGRepresentation((UIImage *)myImage, (CGFloat)quality);

NSData * pngData = UIImagePNGRepresentation((UIImage *)myImage);

十. 边框变化时重绘:

1.默认情况下当你的视图边框变化时,不会重绘,

只是相应的拉伸或压缩或移动你的视图中的位图。通常这不是你想要的,

有一个UIView的@property控制这个,可以在Xcode中设置:

@property (nonatomic) UIViewContentMode contentMode;

以下content modes移动你的视图中的位图:

UIViewContentMode{Left, Right, Top, BottomLeft, BottomRigth, TopLeft, TopRight}

以下content modes拉伸或压缩你视图中的位图:

UIViewContentModeScale{ToFill, AspectFill, AspectFit}

以下content mode 调用drawRect函数,边框变化是重绘你视图中的所有元素:

UIViewContentModeRedraw //it is quite often that this is what you want

默认是UIViewContentModeScaleToFill
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: