和我一起来学iOS(三)UIView及其子类(上)

在开始前,我想大家应该先读一读 Mac OS X 背后的故事,是一篇非常不错的文章。了解曾经发生的过去,才能对现在为什么会是这样有更深刻的认识。

xib与nib的渊源

Project Builder 在 Mac OS X 10.3 时被重命名为现在大家所熟知的 Xcode。Xcode 3以前,Interface Builder 使用一种名为 nib 格式的二进制文件格式。不过由于 nib 不能用肉眼读,也不方便使用版本管理工具来管理,所以 Xcode 3 开始新加入一种名为 xib 的文本文件格式,最后再在项目编译阶段输出为 nib 格式。和产生静态界面布局代码的工具很不同,nib 是不被转译成相应 Objective-C 代码的。用户程序执行时,nib 文件被读入,解包,所以 nib 文件是在运行时动态加载的。


UIView类代表的含义

UIView继承于UIResponder类,因此它主要表达了两个意思

1.可视(CALayer)
2.可互交(UIResponder)

每个UIView都有一个隐式层(implicit Layer),View本身就是这个隐式层的Delegate.
为什么要有层这个东西?因为作图等都是在层上完成,然后再合成。因为UIView有了Layer所以才能显示。

Layer又有两部分组成。present layer和Model layer.
present layer表示了中间的过程状态,而Model layer则表示了起始和结束状态

初始化方法

- (id)initWithFrame:(CGRect)frame

其中frame指定了这个View的大小和位置,起始点在左上角。

 

 

UIView的子类

一、UIWindow

UIWindow是作为包含了其他所有View的一个容器。每一个程序里面都会有一个UIWindow。下面这段代码里我们在程序启动完成后实例化了一个UIWindow类,并调用了它的makeKeyAndVisible方法。这个方法使得window在屏幕上可见。

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
  self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; 
  self.window.backgroundColor = [UIColor whiteColor]; 
  [self.window makeKeyAndVisible];   
return YES; }

 
让View显示在屏幕上

一旦window在屏幕上可见了,之后任何加入window的view都会在屏幕上可见。换句话说,要使得一个view在屏幕上可见,需要把它加入到window之中。
当一个view被加入到window的时候,那么这个view就被称为window的subview。每一个view都可以有自己的subview。Window是这个层级结构中的根节点。前面说了一个view如果想被显示到屏幕上,就需要作为window的subview。除了直接加入window的subview之外,作为已经加入window的view的subview也算是间接作为window的subview,因此也可以显示在屏幕上。
 
结构可以参照下图(图来自iOS Programming 3rd),MKMapView作为UIWindow的subview, UITextfield和UIButton又作为MKMapView的subview, 最终他们都显示在了屏幕上。


这种层级关系可以直接调用UIView类的addSubview方法来实现,比如

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
  self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; 
  MyView *view = [[MyView alloc] initWithFrame:viewFrame]; 
  [[self window] addSubview:view];
  [self.window makeKeyAndVisible];
  return YES;
}



也可以通过界面编辑器(Interface Builder)生成xib文件,然后我们通过UIViewController加载这个xib来实现。这就是MVC模式中C和V的关系,Controller通过连线的方式持有View的引用。当然直接通过实例化的方式持有,完全不使用Interface Builder也是可以的。

我们在项目中会有一个叫MyViewController以.xib作为扩展名的文件,我们之所以用这种结合了Interface Builder的方式去初始化一个Controller,是为了让这个Controller能方便的响应它所持有的View的所触发的事件。下节讲UITableView的时候会详细讲解。

- (id)initWithNibName:(NSString *)nibName bundle:(NSBundle *)bundle {
  self = [super initWithNibName:@"MyViewController" bundle:appBundle];

  return self; 
}

然后把这个controller作为window的rootViewController, 就可以显示了。框架会自动把UIViewController所持有的View加入到window的subviews里。





 

drawRect方法,决定了View长啥样

默认情况下,draRect: 方法啥都不做,交给UIView的子类去实现这个方法,让子类有不同的样子。例如,UIButton的drawRect:方法可以绘制一个圆角的四方形,并在正中显示标题字符串。当你覆盖drawRect方法的时候,系统会为View准备一个graphics context, 接着系统会激活这个context,然后调用需要绘图的UIView对象的drawRect方法。我们可以通过UIGraphicsGetCurrentContext方法来获取已经激活的context, 这个context的类型就是CGContextRef,它负责合并然后生成一个image。这个image就是View最终的样子。

- (void)drawRect:(CGRect)dirtyRect {
  CGContextRef ctx = UIGraphicsGetCurrentContext(); 
  CGRect bounds = [self bounds];   center.x = bounds.origin.x + bounds.size.width / 2.0;
  center.y = bounds.origin.y + bounds.size.height / 2.0;   float maxRadius = hypot(bounds.size.width, bounds.size.height) / 4.0;   CGContextStrokePath(ctx); }

 

我们在子类覆盖了这个方法以后,初始化这个子类就能得到下图

 

 

 
因为drawRect挺重要,所以接下来的文章里会有对这个方法的详细讲解。


你可能感兴趣的:(和我一起来学iOS(三)UIView及其子类(上))