iOS 视图与视图层次结构(内容根据iOS编程)

  • 视图基础
  1. 视图是 UIView 对象,或者其子对象。
  2. 视图知道如何绘制自己。
  3. 视图可以处理事件,例如触摸(touch)。
  4. 视图会按照层次结构排列,位于视图层次结构顶端的是应用窗口。
  • 视图层次结构

  任何应用有且只有一个  UIWindow 对象。 UIWindow 对象就像是一个容器,负责包含应用中的所有的视图。应用需要在启动时创建并设置 UIWindow 对象,然后为其添加其他视图。

  加入窗口的视图会成为该窗口的子视图。窗口的子视图还可以有自己的子视图,从而构成一个以 UIWindow 对象为根视图的,类似于树形结构的视图层次结构。

  视图层次结构形成之后,系统会将其绘制到屏幕上,绘制过程可以分为两步:

    1. 层次结构中的每个视图(包括 UIWindow 对象)分别绘制自己。视图会将自己绘制到图层( layer )上,每个 UIView 对象都有一个 layer 属性,指向一个 CALayer 类的对象

    2. 所有视图的图层何曾一幅图像,绘制到屏幕上。

  获取当前应用程序的 UIWindow 方法是  UIWindow * keyWindow = [UIApplication sharedApplication].keyWindow;

  • 创建 UIView 子类

  首先创建一个 UIView 子类。

    视图及其 frame 属性

    在控制器中创建一个  CGRect 结构,然后使用该结构创建一个视图对象,并将这个视图对象加入到控制器视图子视图上。

#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

显示结果

   CGRect 结构包含该另外两个结构: origin 和 size 。其中 origin 的类型是 CGPoint 结构,该结构包含两个 float 类型测成员。 size 的类型是 CGSize 结构,该结构也包含两个 float 类型的成员: width 和 height 。

  所以我们创建的视图对象,在上图中可以看出  JXHypnosisView 对象的左上角位于父视图右侧 100点 、下方 200点 的位置。此外,因为这个  frame 结构中的 size 是(100,200),所以我们自定义  JXHypnosisView 对象的宽度是 100点 、高度是 200点 。

  我们这里所说的这些值的单位是 点(points),不是 像素(pixels)如果是像素,那么在不同的 Retina 显示屏上显示的大小是不同的。在  Retina 显示屏上,一个点是两个像素高度。(所以在跟美工沟通的时候最好让他们根据像素来做图片,并且图片的像素大小是点的两倍,或者三倍)。

  每个视图对象都有一个 superview 属性。将一个视图作为子视图加入另一个视图时,会自动创建相应的反向关联。

  • 在  drawRect: 方法中自定义绘图

  前面我们编写了一个简单的自定义的 JXHypnosisView 对象,并且设置了他的一些基本的属性,如位置,大小,颜色等。在本节中我们将在 drawRect: 方法中编写绘图代码。

  视图根据 drawRect: 方法将自己绘制到图层上。 UIView 的子类可以覆盖 drawRect: 方法完成自定义的绘图任务。例如, UIButton 的 drawRect: 方法默认会在 frame 表示的矩形区域中心画出一行浅蓝色的文字。

  覆盖 drawRect: 后首先应该获取视图从 UIView 继承而来的 bounds 属性,该属性定义了一个矩形范围,表示视图的绘制区域。

  视图在绘制自己时,会参考一个坐标系, bounds 表示的矩形位于自己的坐标系,而 frame 表示的矩形位于父视图的坐标系,但是两个矩形的大小是相同的。

   frame 和 bounds 表示的矩形用法不同。前者用于确定与视图层次结构中其他视图的相对位置,从而将自己的图层与其他视图的图层正确组合成屏幕上的图像。而后者属性用于确定绘制区域,避免将自己绘制到图层边界之外(其视图是相对于自己而言,设置只有宽高有效)。

  • 绘制圆形

  接下来在 JXHypnosisView 的 drawRect 方法中添加绘图代码,画出一个尽可能大的圆形,但是不能好过视图的绘制区域。

  首先,需要根据视图的 bounds 属性找到绘制预期的中心点:

#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

  然后再比较视图的宽和高,将较小的值的一般设置为圆形的半径:

#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
  • UIBezierPath

   UIBezierPath 是用来绘制直线或者曲线的一个类。

  首先要创建一个  UIBezierPath 对象:

#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

  接下来我们定义  UIBezierPath 对象需要绘制的路径。

#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

  路径已经定义好了,但是之定义路径不会进行实际的绘制。我们还需要向 UIBezierPath 对象发送消息,绘制之前定制的路径:

#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

  绘制结果:

  现在改变圆形的线条的粗细和颜色。

#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

  下面来改变绘制图形的轨迹颜色

#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

  运行结果:

  这里我们可以尝试视图的 backgroundColor 属性不会受到 drawRect 中代码的影响,通常应该将重写 drawRect 方法的视图的背景色设置为透明(对应于  clearColor),这样可以让视图只显示 drawRect 方法中绘制的内容。

#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];

}
#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
  • 绘制同心圆 

  在  JXHypnosisView 中绘制多个同心圆有两个方法,第一个方法是创建多个 UIBezierPath 对象,每个对象代表一个圆形;第二个方法是使用一个 UIBezierPath 对象绘制多个圆形,为每个圆形定义一个绘制路径。很明显第二种方法更好。

#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

  运行结果:可以看到我们已经画出了一些列的同心圆,但是屏幕右边多出了一条奇怪的线条

  这是因为单个 UIBezierPath 对象将多个路径(每个路径可以画出一个圆形)连接起来,形成了一个完成的路径。可以将 UIBezierPath 对象想象成一支在纸上画画的铅笔-但是当我们绘制完成一个圆形之后去绘制另外一个圆形时,铅笔并没有抬起,所以才会出现一条很奇怪的线条。

#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

  运行结果:完美

  

  • 绘制图像

  创建一个  UIImage 对象: UIImage * logoImage = [UIImage imageNamed:@"train"]; ,然后在 drawRect 方法中将图像会知道视图上: [logoImage drawInRect:someRect]

#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
  • 深入学习: Core Graphics 

  UIImage、UIBezierPath 和 NSString 都提供了至少一种用于在  drawRect 中绘图的方法,这些绘图的方法会在 drawRect 执行时分别将图像,图形,和文本绘制到视图的图层上。

  无论是绘制 JPEG 、PDF 还是视图的图层,都是由  Core Graphics 框架完成的。 Core Graphics 是一套提供 2D 绘图功能的 C语言API,使用 C结构和 C函数模拟了一套面向对象的编程机制,并没有OC对象和方法。 Core Graphics 中最重要的“对象”是 图形上下文 ,图形上下文是 CGContextRef 的“对象”,负责存储绘画状态(例如画笔颜色和线条粗细)和绘制内容所处的内存空间。

  视图的  drawRect  方法在执行之前,系统首先为视图的图层创建一个图形上下文,然后为绘画状态设置一些默认参数。 drawRect 方法开始执行时,随着图形上下文不断执行绘图操作,图层上的内容也会随之改变。 drawRect 执行完毕后,系统会将图层与其他图层一起组合成完整的图像并显示在屏幕上。

  参与绘图操作的类都定义了改变绘画状态和执行绘图操作的方法,这些方法其实调用了对应的  Core Graphics 函数。例如,向 UIColor 对象发送 setCtroke 消息时,会调用  Core Graphics 中的 CGContextSetRGBSrokeColor 函数改变当前上下文中的画笔颜色。

时间: 2024-11-08 10:54:40

iOS 视图与视图层次结构(内容根据iOS编程)的相关文章

IOS开发之视图和视图控制器

视图(View), 视图控制器(ViewController)是IOS开发UI部分比较重要的东西.在学习视图这一块的东西的时候,感觉和Java Swing中的Panel差不多.在UIKit框架中都有一个UIWindow来容纳我们的View.应用程序中几乎全部的可视控件都是UIView以及UIView的子类的实例,并且UIWindow也是UIView的子类.UIWindow可以不借助于父类视图显示在屏幕上,其余的视图都需要添加到父视图中才能显示.窗口是用来显示视图的,下面我们将会结合着实例来具体的

Swift - iOS中各种视图控制器(View Controller)的介绍

在iOS中,不同的视图控制器负责不同的功能,采用不同的风格向用户呈现信息.下面对各个视图控制器做个总结: 1,标准视图控制器 - View Controller 这个控制器只是用来呈现内容.通常会用来作为子类,以向屏幕中添加逻辑. 2,导航控制器 - Navigation Controller 这个控制器呈现一个视图控制器的栈,应用程序可以在上面推入更多的视图控制器. 当新视图推入栈,或旧视图弹出栈时,导航控制器会以动画的形式(比如卷动)显示隐藏这些视图. 使用样例:系统“设置”应用程序 3,表

iOS开发系列--视图切换

概述 在iOS开发中视图的切换是很频繁的,独立的视图应用在实际开发过程中并不常见,除非你的应用足够简单.在iOS开发中常用的视图切换有三种,今天我们将一一介绍: UITabBarController UINavigationController 模态窗口 UITabBarController iOS三种视图切换的原理各不相同: UITabBarController:以平行的方式管理视图,各个视图之间往往关系并不大,每个加入到UITabBarController的视图都会进行初始化即使当前不显示在

iOS 7 表视图顶部空白解决

太阳火神的美丽人生 (http://blog.csdn.net/opengl_es) 本文遵循"署名-非商业用途-保持一致"创作公用协议 转载请保留此句:太阳火神的美丽人生 -  本博客专注于 敏捷开发及移动和物联设备研究:iOS.Android.Html5.Arduino.pcDuino,否则,出自本博客的文章拒绝转载或再转载,谢谢合作. 官方文档有述,以下的属性用于指示是否允许视图控制调整它内部嵌入的滚动视图的 insets 图,以应对屏幕区域被状态栏.导航栏和工具栏或标签栏所占用

IOS中标签视图和Block

标签视图控制器 - UITabBarController 自定义UITabBarItem 自定义导航栏和标签栏背景图片 三个视图控制器的综合使用 一. 标签视图控制器  UITabBarController UITabBarController有以下重要属性 viewControllers   显示的视图控制器 tabBar                  标签栏 delegate                协议 tabBar 是 UITabBar对象, 包含多个UITabBarItem,

iOS学习之视图控制器

一.自定义视图(label-textField组合视图) 1.自定义视图:系统标准UI之外,自己组合出的新的视图. 2.优点:iOS提供了很多UI组件,借助它们我们可以实现不同的功能.尽管如此,实际开发中,我们还需自定义视图.积累自己的代码库,方便开发.自己封装的视图,能像UI空间一样,用于别的项目中,能大大降低开发成本,提高开发效率. 3.高质量代码的特点:可复用,可移植,精炼等.(高内聚,低耦合). 4.自定义视图步骤 根据需求的不同,自定义视图继承的类也有所不同.一般自定义的视图会继承于U

iOS开发中视图控制器ViewControllers之间的数据传递

iOS开发中视图控制器ViewControllers之间的数据传递 这里我们用一个demo来说明ios是如何在视图控制器之间传递重要的参数的.本文先从手写UI来讨论,在下一篇文章中讨论在storyboard中传递数据. 首先新建一个空工程,并添加一个根视图控制器类,如下图所示: # 在函数didFinishLunchingWithOption中添加几行代码,完成后如下: ? 1 2 3 4 5 6 7 8 9 10 11 12 - (BOOL)application:(UIApplication

Xamarin iOS文本框视图

Xamarin iOS文本框视图 与标签视图不同,文本框视图(一般使用UITextField类实现)可以接收用户的文本输入,并进行显示. [示例2-16]以下将使用文本框来实现QQ登录界面的效果.具体步骤如下: (1)创建一个Single View Application类型的工程,命名为2-5 (2)打开MainStoryboard.storyboard文件,对主视图进行设置.效果如图2.30所示. 图2.30  主视图 需要添加的视图以及设置如表2-8所示. 表2-8  设置主视图 (3)打

ios 获取当前视图第一响应者

Football on Table 题意:一些杆上有人,人有一个宽度,然后现在有一个球射过去,要求出球不会碰到任何人的概率 思路:计算出每根杆的概率,之后累乘,计算杆的概率的时候,可以先把每块人的区间长度再移动过程中会覆盖多少长度累加出来,然后(1?总和/可移动距离)就是不会碰到的概率 代码: #include <stdio.h> #include <string.h> #include <math.h> const double eps = 1e-8; int t,