iOS 视图与视图层次结构

jieyan 8年前
   <ul>     <li> <p><strong>视图基础</strong></p> </li>    </ul>    <ol>     <li> <p>视图是  UIView  对象,或者其子对象。</p> </li>     <li> <p>视图知道如何绘制自己。</p> </li>     <li> <p>视图可以处理事件,例如触摸(touch)。</p> </li>     <li> <p>视图会按照层次结构排列,位于视图层次结构顶端的是应用窗口。</p> </li>    </ol>    <ul>     <li> <p><strong>视图层次结构</strong></p> </li>    </ul>    <p>任何应用有且只有一个 UIWindow 对象。  UIWindow 对象就像是一个容器,负责包含应用中的所有的视图。应用需要在启动时创建并设置  UIWindow 对象,然后为其添加其他视图。</p>    <p>加入窗口的视图会成为该窗口的 <strong>子视图</strong> 。窗口的子视图还可以有自己的子视图,从而构成一个以  UIWindow 对象为根视图的,类似于树形结构的视图层次结构。</p>    <p>视图层次结构形成之后,系统会将其绘制到屏幕上,绘制过程可以分为两步:</p>    <p>1.层次结构中的每个视图(包括  UIWindow 对象)分别绘制自己。视图会将自己绘制到图层(  layer )上,每个  UIView 对象都有一个  layer 属性,指向一个  CALayer 类的对象</p>    <p>2. 所有视图的图层何曾一幅图像,绘制到屏幕上。</p>    <p>获取当前应用程序的 UIWindow 方法是   UIWindow * keyWindow = [UIApplication sharedApplication].keyWindow;</p>    <ul>     <li> <p><strong>创建UIView子类 </strong></p> </li>    </ul>    <p>首先创建一个UIView子类。</p>    <p>视图及其frame属性</p>    <p>在控制器中创建一个CGRect结构,然后使用该结构创建一个视图对象,并将这个视图对象加入到控制器视图子视图上。</p>    <pre>  #import "ViewController.h"  #import "JXHypnosisView.h" // 为创建的子类    @interface ViewController ()    @end    @implementation ViewController    - (void)viewDidLoad {      [super viewDidLoad];            // 创建 CGRect 结构      CGRect rect = CGRectMake(100, 100, 100, 200);            // 创建视图      JXHypnosisView * firstView = [[JXHypnosisView alloc] initWithFrame:rect];      firstView.backgroundColor = [UIColor redColor];            // 将视图添加到控制器View上      [self.view addSubview:firstView];    }    @end  </pre>    <p>显示结果</p>    <p><img src="https://simg.open-open.com/show/f2a2d1f773fc0f331c0c823a5af6d43d.png"></p>    <p>CGRect 结构包含该另外两个结构:origin 和size 。其中origin 的类型是CGPoint 结构,该结构包含两个float 类型测成员。size 的类型是CGSize 结构,该结构也包含两个float 类型的成员:width 和height 。</p>    <p>所以我们创建的视图对象,在上图中可以看出 JXHypnosisView 对象的左上角位于父视图右侧<strong>100点</strong>、下方<strong>200点</strong>的位置。此外,因为这个frame 结构中的size 是<strong>(100,200)</strong> ,所以我们自定义JXHypnosisView 对象的宽度是<strong>100点</strong>、高度是<strong>200点 。</strong></p>    <p>我们这里所说的这些值的单位是<strong>点(points)</strong>,不是<strong>像素(pixels)</strong><strong>。</strong> 如果是像素,那么在不同的<strong>Retina</strong>显示屏上显示的大小是不同的。在<strong>Retina</strong> 显示屏上,一个点是两个像素高度。(所以在跟美工沟通的时候最好让他们根据像素来做图片,并且图片的像素大小是点的两倍,或者三倍)。</p>    <p>每个视图对象都有一个 superview 属性。将一个视图作为子视图加入另一个视图时,会自动创建相应的反向关联。</p>    <ul>     <li> <p><strong>在drawRect:方法中自定义绘图 </strong></p> </li>    </ul>    <p>前面我们编写了一个简单的自定义的JXHypnosisView对象,并且设置了他的一些基本的属性,如位置,大小,颜色等。在本节中我们将在drawRect:方法中编写绘图代码。</p>    <p>视图根据drawRect:方法将自己绘制到图层上。UIView 的子类可以覆盖drawRect:方法完成自定义的绘图任务。例如,  UIButton的drawRect:方法默认会在frame 表示的矩形区域中心画出一行浅蓝色的文字。</p>    <p>覆盖drawRect:后首先应该获取视图从UIView继承而来的bounds 属性,该属性定义了一个矩形范围,表示视图的绘制区域。</p>    <p>视图在绘制自己时,会参考一个坐标系,bounds 表示的矩形位于自己的坐标系,而frame 表示的矩形位于父视图的坐标系,但是两个矩形的大小是相同的。</p>    <p>frame 和bounds 表示的矩形用法不同。前者用于确定与视图层次结构中其他视图的相对位置,从而将自己的图层与其他视图的图层正确组合成屏幕上的图像。而后者属性用于确定绘制区域,避免将自己绘制到图层边界之外(其视图是相对于自己而言,设置只有宽高有效)。</p>    <ul>     <li> <p><strong>绘制圆形</strong></p> </li>    </ul>    <p>接下来在JXHypnosisView的drawRect方法中添加绘图代码,画出一个尽可能大的圆形,但是不能好过视图的绘制区域。</p>    <p>首先,需要根据视图的bounds属性找到绘制预期的中心点:</p>    <pre>  #import "JXHypnosisView.h"    @implementation JXHypnosisView    - (void)drawRect:(CGRect)rect {      CGRect bounds = self.bounds;            // 根据bounds计算中心点      CGPoint center;      center.x = bounds.origin.x + bounds.size.width / 2.0;      center.y = bounds.origin.y + bounds.size.height / 2.0;  }    @end  </pre>    <p>然后再比较视图的宽和高,将较小的值的一般设置为圆形的半径:</p>    <pre>  #import "JXHypnosisView.h"    @implementation JXHypnosisView    - (void)drawRect:(CGRect)rect {      CGRect bounds = self.bounds;            // 根据bounds计算中心点      CGPoint center;      center.x = bounds.origin.x + bounds.size.width / 2.0;      center.y = bounds.origin.y + bounds.size.height / 2.0;            // 根据视图的宽高比较中的较小的值计算圆形的半径      float radius = (MIN(bounds.size.width, bounds.size.height) / 2.0);  }    @end  </pre>    <ul>     <li> <p><strong>UIBezierPath</strong></p> </li>    </ul>    <p>UIBezierPath 是用来绘制直线或者曲线的一个类。</p>    <p>首先要创建一个 UIBezierPath 对象:</p>    <pre>  #import "JXHypnosisView.h"    @implementation JXHypnosisView    - (void)drawRect:(CGRect)rect {      CGRect bounds = self.bounds;            // 根据bounds计算中心点      CGPoint center;      center.x = bounds.origin.x + bounds.size.width / 2.0;      center.y = bounds.origin.y + bounds.size.height / 2.0;            // 根据视图的宽高比较中的较小的值计算圆形的半径      float radius = (MIN(bounds.size.width, bounds.size.height) / 2.0);            UIBezierPath * path = [[UIBezierPath alloc] init];  }    @end  </pre>    <p>接下来我们定义 UIBezierPath 对象需要绘制的路径。</p>    <pre>  #import "JXHypnosisView.h"    @implementation JXHypnosisView    - (void)drawRect:(CGRect)rect {      CGRect bounds = self.bounds;            // 根据bounds计算中心点      CGPoint center;      center.x = bounds.origin.x + bounds.size.width / 2.0;      center.y = bounds.origin.y + bounds.size.height / 2.0;            // 根据视图的宽高比较中的较小的值计算圆形的半径      float radius = (MIN(bounds.size.width, bounds.size.height) / 2.0);            UIBezierPath * path = [[UIBezierPath alloc] init];            // 以中心点为圆心,radius的值为半径,定义一个 0 到 M_PI * 2.0 弧度的路径(整圆)      [path addArcWithCenter:center                      radius:radius                  startAngle:0.0                    endAngle:M_PI * 2.0                   clockwise:YES];  }    @end  </pre>    <p>路径已经定义好了,但是之定义路径不会进行实际的绘制。我们还需要向 <strong>UIBezierPath</strong> 对象发送消息,绘制之前定制的路径:</p>    <pre>  #import "JXHypnosisView.h"    @implementation JXHypnosisView    - (void)drawRect:(CGRect)rect {      CGRect bounds = self.bounds;            // 根据bounds计算中心点      CGPoint center;      center.x = bounds.origin.x + bounds.size.width / 2.0;      center.y = bounds.origin.y + bounds.size.height / 2.0;            // 根据视图的宽高比较中的较小的值计算圆形的半径      float radius = (MIN(bounds.size.width, bounds.size.height) / 2.0);            UIBezierPath * path = [[UIBezierPath alloc] init];            // 以中心点为圆心,radius的值为半径,定义一个 0 到 M_PI * 2.0 弧度的路径(整圆)      [path addArcWithCenter:center                      radius:radius                  startAngle:0.0                    endAngle:M_PI * 2.0                   clockwise:YES];            // 绘制路径      [path stroke];  }    @end  </pre>    <p>绘制结果:</p>    <p><img src="https://simg.open-open.com/show/f6a0e0c8c9ff7b7ffaee3f9620423448.png"></p>    <p>现在改变圆形的线条的粗细和颜色。</p>    <pre>  #import "JXHypnosisView.h"    @implementation JXHypnosisView    - (void)drawRect:(CGRect)rect {      CGRect bounds = self.bounds;            // 根据bounds计算中心点      CGPoint center;      center.x = bounds.origin.x + bounds.size.width / 2.0;      center.y = bounds.origin.y + bounds.size.height / 2.0;            // 根据视图的宽高比较中的较小的值计算圆形的半径      float radius = (MIN(bounds.size.width, bounds.size.height) / 2.0);            UIBezierPath * path = [[UIBezierPath alloc] init];            // 以中心点为圆心,radius的值为半径,定义一个 0 到 M_PI * 2.0 弧度的路径(整圆)      [path addArcWithCenter:center                      radius:radius                  startAngle:0.0                    endAngle:M_PI * 2.0                   clockwise:YES];            // 设置线条宽度为 10 点      path.lineWidth = 10;            // 绘制路径      [path stroke];  }    @end  </pre>    <p>下面来改变绘制图形的轨迹颜色</p>    <pre>  #import "JXHypnosisView.h"    @implementation JXHypnosisView    - (void)drawRect:(CGRect)rect {      CGRect bounds = self.bounds;            // 根据bounds计算中心点      CGPoint center;      center.x = bounds.origin.x + bounds.size.width / 2.0;      center.y = bounds.origin.y + bounds.size.height / 2.0;            // 根据视图的宽高比较中的较小的值计算圆形的半径      float radius = (MIN(bounds.size.width, bounds.size.height) / 2.0);            UIBezierPath * path = [[UIBezierPath alloc] init];            // 以中心点为圆心,radius的值为半径,定义一个 0 到 M_PI * 2.0 弧度的路径(整圆)      [path addArcWithCenter:center                      radius:radius                  startAngle:0.0                    endAngle:M_PI * 2.0                   clockwise:YES];            // 设置线条宽度为 10 点      path.lineWidth = 10;            // 设置绘制颜色为灰色      [[UIColor lightGrayColor] setStroke];            // 绘制路径      [path stroke];  }    @end  </pre>    <p>运行结果:</p>    <p><img src="https://simg.open-open.com/show/13e97655e00be78eeec64e2c55bf4bee.png"></p>    <p>这里我们可以尝试视图的 backgroundColor 属性不会受到drawRect中代码的影响,通常应该将重写drawRect方法的视图的背景色设置为透明对应于<strong>clearColor)</strong>,这样可以让视图只显示drawRect 方法中绘制的内容。</p>    <pre>  #import "ViewController.h"  #import "JXHypnosisView.h"    @interface ViewController ()    @end    @implementation ViewController    - (void)viewDidLoad {      [super viewDidLoad];            // 创建 CGRect 结构      CGRect rect = CGRectMake(100, 200, 200, 300);            // 创建视图      JXHypnosisView * firstView = [[JXHypnosisView alloc] initWithFrame:rect];      firstView.backgroundColor = [UIColor redColor];      NSLog(@"%f",firstView.bounds.origin.x);      // 将视图添加到控制器View上      [self.view addSubview:firstView];    }  </pre>    <pre>  #import "JXHypnosisView.h"    @implementation JXHypnosisView    - (instancetype)initWithFrame:(CGRect)frame {      self = [super initWithFrame:frame];      if (self) {          // 设置 JXHypnosisView 对象的背景颜色为透明          self.backgroundColor = [UIColor clearColor];      }      return self;  }    - (void)drawRect:(CGRect)rect {      CGRect bounds = self.bounds;            // 根据bounds计算中心点      CGPoint center;      center.x = bounds.origin.x + bounds.size.width / 2.0;      center.y = bounds.origin.y + bounds.size.height / 2.0;            // 根据视图的宽高比较中的较小的值计算圆形的半径      float radius = (MIN(bounds.size.width, bounds.size.height) / 2.0);            UIBezierPath * path = [[UIBezierPath alloc] init];            // 以中心点为圆心,radius的值为半径,定义一个 0 到 M_PI * 2.0 弧度的路径(整圆)      [path addArcWithCenter:center                      radius:radius                  startAngle:0.0                    endAngle:M_PI * 2.0                   clockwise:YES];            // 设置线条宽度为 10 点      path.lineWidth = 10;            // 设置绘制颜色为灰色      [[UIColor lightGrayColor] setStroke];            // 绘制路径      [path stroke];  }    @end  </pre>    <ul>     <li> <p><strong>绘制同心圆 </strong></p> </li>    </ul>    <p>在 JXHypnosisView中绘制多个同心圆有两个方法,第一个方法是创建多个<strong>UIBezierPath</strong>对象,每个对象代表一个圆形;第二个方法是使用一个<strong>UIBezierPath</strong>对象绘制多个圆形,为每个圆形定义一个绘制路径。很明显第二种方法更好。</p>    <pre>  #import "JXHypnosisView.h"    @implementation JXHypnosisView    - (instancetype)initWithFrame:(CGRect)frame {      self = [super initWithFrame:frame];      if (self) {          // 设置 JXHypnosisView 对象的背景颜色为透明          self.backgroundColor = [UIColor clearColor];      }      return self;  }    - (void)drawRect:(CGRect)rect {      CGRect bounds = self.bounds;            // 根据bounds计算中心点      CGPoint center;      center.x = bounds.origin.x + bounds.size.width / 2.0;      center.y = bounds.origin.y + bounds.size.height / 2.0;            // 根据视图的宽高比较中的较小的值计算圆形的半径      float radius = (MIN(bounds.size.width, bounds.size.height) / 2.0);        // 是最外层圆形成为视图的外接圆      float maxRadius = hypotf(bounds.size.width, bounds.size.height) / 2.0;            UIBezierPath * path = [[UIBezierPath alloc] init];        // 以中心点为圆心,radius的值为半径,定义一个 0 到 M_PI * 2.0 弧度的路径(整圆)      [path addArcWithCenter:center                      radius:radius                  startAngle:0.0                    endAngle:M_PI * 2.0                   clockwise:YES];            for (float currentRadius = maxRadius; currentRadius > 0; currentRadius -= 20) {          [path addArcWithCenter:center                          radius:currentRadius                      startAngle:0.0                        endAngle:M_PI * 2.0                       clockwise:YES];      }            // 设置线条宽度为 10 点      path.lineWidth = 10;            // 设置绘制颜色为灰色      [[UIColor lightGrayColor] setStroke];            // 绘制路径      [path stroke];        }    @end  </pre>    <p>运行结果:可以看到我们已经画出了一些列的同心圆,但是屏幕右边多出了一条奇怪的线条</p>    <p><img src="https://simg.open-open.com/show/018d8d16d58bb0dc73744f533ee739a0.png"></p>    <p>这是因为单个<strong>UIBezierPath</strong>对象将多个路径(每个路径可以画出一个圆形)连接起来,形成了一个完成的路径。可以将<strong>UIBezierPath</strong>对象想象成一支在纸上画画的铅笔-但是当我们绘制完成一个圆形之后去绘制另外一个圆形时,铅笔并没有抬起,所以才会出现一条很奇怪的线条。</p>    <pre>  #import "JXHypnosisView.h"    @implementation JXHypnosisView    - (instancetype)initWithFrame:(CGRect)frame {      self = [super initWithFrame:frame];      if (self) {          // 设置 JXHypnosisView 对象的背景颜色为透明          self.backgroundColor = [UIColor clearColor];      }      return self;  }    - (void)drawRect:(CGRect)rect {      CGRect bounds = self.bounds;            // 根据bounds计算中心点      CGPoint center;      center.x = bounds.origin.x + bounds.size.width / 2.0;      center.y = bounds.origin.y + bounds.size.height / 2.0;            // 是最外层圆形成为视图的外接圆      float maxRadius = hypotf(bounds.size.width, bounds.size.height) / 2.0;            UIBezierPath * path = [[UIBezierPath alloc] init];        for (float currentRadius = maxRadius; currentRadius > 0; currentRadius -= 20) {                    // 用来设置绘制起始位置          [path moveToPoint:CGPointMake(center.x + currentRadius, center.y)];                    [path addArcWithCenter:center                          radius:currentRadius                      startAngle:0.0                        endAngle:M_PI * 2.0                       clockwise:YES];      }            // 设置线条宽度为 10 点      path.lineWidth = 10;            // 设置绘制颜色为灰色      [[UIColor lightGrayColor] setStroke];            // 绘制路径      [path stroke];        }    @end  </pre>    <p>运行结果:完美</p>    <p><img src="https://simg.open-open.com/show/95180f54dda76428eeac30c381924f1f.png"></p>    <ul>     <li> <p><strong>绘制图像</strong></p> </li>    </ul>    <p>创建一个UIImage 对象:UIImage * logoImage = [UIImage imageNamed: @" train " ]; ,然后在drawRect方法中将图像会知道视图上:[logoImage drawInRect:someRect]</p>    <pre>  #import "JXHypnosisView.h"    @implementation JXHypnosisView    - (instancetype)initWithFrame:(CGRect)frame {      self = [super initWithFrame:frame];      if (self) {          // 设置 JXHypnosisView 对象的背景颜色为透明          self.backgroundColor = [UIColor clearColor];      }      return self;  }    - (void)drawRect:(CGRect)rect {            CGRect bounds = self.bounds;              // 根据bounds计算中心点      CGPoint center;      center.x = bounds.origin.x + bounds.size.width / 2.0;      center.y = bounds.origin.y + bounds.size.height / 2.0;            // 是最外层圆形成为视图的外接圆      float maxRadius = hypotf(bounds.size.width, bounds.size.height) / 2.0;            UIBezierPath * path = [[UIBezierPath alloc] init];        for (float currentRadius = maxRadius; currentRadius > 0; currentRadius -= 20) {                    // 用来设置绘制起始位置          [path moveToPoint:CGPointMake(center.x + currentRadius, center.y)];                    [path addArcWithCenter:center                          radius:currentRadius                      startAngle:0.0                        endAngle:M_PI * 2.0                       clockwise:YES];      }            // 设置线条宽度为 10 点      path.lineWidth = 10;            // 设置绘制颜色为灰色      [[UIColor lightGrayColor] setStroke];            // 绘制路径      [path stroke];                  // 创建UIImage对象      UIImage * logoImage = [UIImage imageNamed:@"train"];      // 绘制图像      [logoImage drawInRect:bounds];        }    @end  </pre>    <ul>     <li> <p><strong>深入学习:Core Graphics   </strong></p> </li>    </ul>    <p>UIImage、UIBezierPath和<strong>NSString</strong>都提供了至少一种用于在drawRect中绘图的方法,这些绘图的方法会在drawRect执行时分别将图像,图形,和文本绘制到视图的图层上。</p>    <p>无论是绘制JPEG、PDF还是视图的图层,都是由Core Graphics 框架完成的。Core Graphics是一套提供2D绘图功能的C语言API,使用C结构和C函数模拟了一套面向对象的编程机制,并没有OC对象和方法。Core Graphics 中最重要的“对象”是<strong>图形上下文</strong> ,图形上下文是CGContextRef的“对象”,负责存储绘画状态(例如画笔颜色和线条粗细)和绘制内容所处的内存空间。</p>    <p>视图的drawRect方法在执行之前,系统首先为视图的图层创建一个图形上下文,然后为绘画状态设置一些默认参数。drawRect方法开始执行时,随着图形上下文不断执行绘图操作,图层上的内容也会随之改变。drawRect执行完毕后,系统会将图层与其他图层一起组合成完整的图像并显示在屏幕上。</p>    <p>参与绘图操作的类都定义了改变绘画状态和执行绘图操作的方法,这些方法其实调用了对应的Core Graphics 函数。例如,向<strong>UIColor</strong>对象发送<strong>setCtroke</strong> 消息时,会调用Core Graphics 中的  <strong>CGContextSetRGBSrokeColor</strong> 函数改变当前上下文中的画笔颜色。</p>    <p> </p>    <p>来自:http://www.cnblogs.com/wang-com/p/5862931.html</p>    <p> </p>