随笔 - 75  文章 - 0  评论 - 190  阅读 - 29万 

在开始前,我想大家应该先读一读 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挺重要,所以接下来的文章里会有对这个方法的详细讲解。


posted on   一路转圈的雪人  阅读(4177)  评论(0编辑  收藏  举报
编辑推荐:
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
阅读排行:
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· .NET周刊【3月第1期 2025-03-02】
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· [AI/GPT/综述] AI Agent的设计模式综述
点击右上角即可分享
微信分享提示