浅谈iOS中的Views

这篇文章咱们来简单谈谈Views。

你们可能甚至都没有意识到已经在用Views了,因为UILable、UIButton都是Views。

View就是屏幕上的一个矩形空间,它非常强大因为这个空间里有很多好用的东西。iOS的设计范例就是把复杂的部分都放在系统里,你所能看到的都非常简单。(That’s good for you!)

View是干什么的?

Draws and handles events in that rectangle

它画出那个矩形空间,并处理其中的事情,这两件事都非常简单。

View的结构是分层的,你可以想象一个view在另一个view里,一个view只能有一个父view,但可以有很多子view。

子view的顺序是有关系的,在数组中的数字位置越高或者数字越大,就显示在后面;位置低的就在前面。

在这里我说一下,UIWindow其实是UIView的子类。因为在iOS里UIWindow非常不重要,你几乎不会继承UIWindow(Only have one UIWindow(generally) in an iOS application)。

如果你在其他的系统编程,UIWindow可能很重要,但在iOS里都是view在处理,最底层的View是管理屏幕的controller的view,这个view的层级结构一点要在Xcode里图形化操作。

你还可以用代码实现分层。这有两个很重要的代码

1
2
- (void)addSubview:(UIView *)viewName;
- (void)removeFromSuperview;

addSubview是往一个view里添加子view。

但是把一个view拿出来就不是这么回事了,你不能要求同一个view移除刚刚添加的view,你要去到那个view,让它移除它自己。

常常有人搞不清这里,这是不同的模式。通过父view来添加子view,但是要移除子view要通过子view自己。

View Coordinates

我说过view是一个坐标系统,那么需要有东西来描绘坐标。

最基础的是CGFloat,一个默认的float或者double类型,具体是什么我们不用管。图形化编程的时候一定要用CGFloat而不是float和double

然后是CGPoint。他是一个C结构体,含有两个CGFloat数x和y。

还有CGSize。也是一个C结构体,含有两个CGFloat数width和height。

CGRect就是含有一个CGPoint和一个CGSize。

以上就是用来描绘view视图的四个主要类型

1
2
CGPoint point = CGPointMake(x, y);
point.x = 20;
1
2
CGSize size = CGSizeMake(width, height);
size.height = 80;
1
2
3
CGRect view = CGRectMake(x, y, width, height);
view.size.height = 80;
view.origin.x = 20;

Coordinates

现在讲一下坐标系统。

iOS的坐标原点是在左上角,而不是数学中常用的左下角。

坐标单位是点而不是像素。

点是种图形上的术语,与像素不同的是,由于iPhone的分辨率很高,如果点等于像素的话会导致做图时发生错误。

所以UIView有一个属性(property)叫contentScaleFactor,他会返回一个点有多少像素,所以我们只需要用点就可以了

views有三个和位置大小相关的property。

1
2
3
@property CGRect bounds;
@property CGPoint center;
@property CHRect frame;

bounds:他是个矩形,是view自己的坐标系统下的绘画区域,所以在你自己的绘画实现中,你创建了个自定义的view,当你绘图的时候只会用到bounds,不要在view的实现里面用另外两个,别管原因这么做就好了。

center:在父view坐标系统中你的view的中心点

frame:在父view坐标系统中容纳你的view的框

center和frame只用来在父view中定位你的view,两者是联动的。当你center会移动frame;设置frame也就设置了center。

永远不要在view的实现里用frame和center,这两个是用来定位的

Creating Views

一是通过storyboard进行拖拽;二是使用代码进行初始化。
1
2
3
UILabel *labei = [[UILabel alloc] initWithFrame:CGRectMake(x, y, width, height)];
label.text = @"HelloWorld";
[self.view addSubview: label];

Custom View

那么什么时候需要创建自己的UIView子类呢?

我指的是自己的自定义类,什么时候会被用到。

view是用来控制绘图和触摸事件,所以就是当你需要绘制特殊图形或者想要控制触摸事件的时候。

此时也许会有人问,“那我继承UIButton可以吗?因为我主要需要的就是UIButton那样的东西,只是点击事件的处理不同。”通常情况下的建议是不要继承这些内置类型,它们只被优化为拖出来使用,而没有优化过继承和重载,他们也可以这么做,只是不建议这么做罢了。

这里会用到一些drawRect的知识,关于这部分内容详见我的另一篇文章。

我有了drawRect,那么接下来如何去重载它来绘制我的自定义view?

答案是用CGFramework,这也是主要会用到的非面向对象的framework。他是C-APIs,但是用起来简单多了。

CGFramework的基本概念是你创建了一个环境,然后创建了一些轨迹比如直线和弧线,再设置他们的字体颜色样式等并描边或填充到轨迹里,这就是绘图的功能。

现在我要讲一个绘制图片和文字,因为这才是最主要的。文字和轨迹是一回事,就是很多精细的弧线组成的,这些当然都是系统帮你做好的。绘制图片就是复制二进制码。

Context

环境决定了你在哪绘图,所以你创建环境的方法就决定了要在那绘图。

使用drawRect是一个很棒的事情,就是你不用担心这个。因为你进入drawRect后会调用一个C函数,他会给你系统准备绘图的环境。

系统可能是要打印 ,当打印的时候你会在drawRect里发现打印的内容,可能是要打印个大东西,但是得到的环境的方法是一样的。

关于这个环境要注意的有一点,就是每次调用drawRect环境都是不一样的,所以不要把他保存起来,而是去获取新的环境。

1
CGContextRef context = UIGraphicsGetCurrentContext();

这是调用drawRect的方法,这也几乎是每个drawRect的第一行。CGContextRef是一个cookie,你不知道到底是什么,他不是个对象,因为没有*。调用上面的那一行代码你就会得到一个context。

当你有了环境,你就可以绘制轨迹了。

下面是一个例子

1
2
3
4
5
6
7
8
9
10
//Begin the path
CGContextBeginPath(context);

//Move around,add lines or arcs to the path
CGContextMoveToPoint(context, 75, 10);
CGContextAddLineToPoint(context, 160, 150);
CGContextAddLineToPoint(context, 10, 150);

//Close the path(connects the last point to the first)
CGContextClosePath(context);

上面看上去我画了一个三角形,但是只有上面几行代码的话其实什么也没画,虽然轨迹被创建了,但是没有真的画到屏幕上去。

为什么会这样?

那是因为你需要描边(stroke)或者填充(fill)来显示轨迹。

在描边或填充之前,还可以设置图形属性,这里我用了UIColor来设置颜色。关于UIColor,我的另一篇文章会简单介绍到。

1
2
[[UIColor greenColor] setfill];
[[UIColor redColor] setStroke];

我在这里设置了描边和填充,还是什么都没画,还需要调用CGContextDrawPath。

1
2
CGContextDrawPath(context, kCGPathFillStroke);
//这个参数kCGPathFillStroke是常亮标志,表示描边和填充还是仅描边或仅填充。

其实还可以建立一个轨迹,保存到一个轨迹变量CGPath,你用完了之后还可以交给其他环境继续用。