• 视图基础
  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(, , , 20); // 创建视图
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 = ;
// 绘制路径
[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 = ; // 设置绘制颜色为灰色
[[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(, , , ); // 创建视图
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 = ; // 设置绘制颜色为灰色
[[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 > ; currentRadius -= ) {
[path addArcWithCenter:center
radius:currentRadius
startAngle:0.0
endAngle:M_PI * 2.0
clockwise:YES];
}
// 设置线条宽度为 10 点
path.lineWidth = ; // 设置绘制颜色为灰色
[[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 > ; currentRadius -= ) { // 用来设置绘制起始位置
[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 = ; // 设置绘制颜色为灰色
[[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 > ; currentRadius -= ) { // 用来设置绘制起始位置
[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 = ; // 设置绘制颜色为灰色
[[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 函数改变当前上下文中的画笔颜色。

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

  1. iOS UIGestureRecognizer与UIMenuController(内容根据iOS编程)

    UIGestureRecognizer 对象会截取本应由视图处理的触摸事件.当某个UIGestureRecognizer对象识别出特定的手势后,就会向指定的对象发送指定的消息.iOS SDK默认提供若 ...

  2. iOS开发系列--视图切换

    概述 在iOS开发中视图的切换是很频繁的,独立的视图应用在实际开发过程中并不常见,除非你的应用足够简单.在iOS开发中常用的视图切换有三种,今天我们将一一介绍: UITabBarController ...

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

    视图(View), 视图控制器(ViewController)是IOS开发UI部分比较重要的东西.在学习视图这一块的东西的时候,感觉和Java Swing中的Panel差不多.在UIKit框架中都有一 ...

  4. iOS学习之视图控制器

    一.自定义视图(label-textField组合视图)      1.自定义视图:系统标准UI之外,自己组合出的新的视图.      2.优点:iOS提供了很多UI组件,借助它们我们可以实现不同的功 ...

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

    在iOS中,不同的视图控制器负责不同的功能,采用不同的风格向用户呈现信息.下面对各个视图控制器做个总结: 1,标准视图控制器 - View Controller 这个控制器只是用来呈现内容.通常会用来 ...

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

    iOS开发中视图控制器ViewControllers之间的数据传递 这里我们用一个demo来说明ios是如何在视图控制器之间传递重要的参数的.本文先从手写UI来讨论,在下一篇文章中讨论在storybo ...

  7. IOS弹出视图 LewPopupViewController

    LewPopupViewController是一款IOS弹出视图软件.iOS 下的弹出视图.支持iPhone/iPad. 软件截图 使用方法 弹出视图 1 2 3 4 5 PopupView *vie ...

  8. iOS开发:视图生命周期

    iOS应用的视图状态分为以下几种 在viewcontroller的父类UIViewController中可以看到如下代码,通过重写不同的方法对操作视图渲染. @available(iOS 2.0, * ...

  9. 浅谈iOS中的视图优化

    引言: 让我们来思考几个问题,你开发过的产品,它还有可以优化的地方吗?能增加它的帧率吗?能减少多余的CPU计算吗?是不是存在多余的GPU渲染?业务这点工作量对于越来越强大的设备面前显得微不足道,但作为 ...

随机推荐

  1. Linux内核分析作业7:Linux内核如何装载和启动一个可执行程序

            1.可执行文件的格式 在 Linux 平台下主要有以下三种可执行文件格式: 1.a.out(assembler and link editor output 汇编器和链接编辑器的输出) ...

  2. Hadoop学习笔记—7.计数器与自定义计数器

    一.Hadoop中的计数器 计数器:计数器是用来记录job的执行进度和状态的.它的作用可以理解为日志.我们通常可以在程序的某个位置插入计数器,用来记录数据或者进度的变化情况,它比日志更便利进行分析. ...

  3. [ASP.NET MVC 小牛之路]11 - Filter

    Filter(筛选器)是基于AOP(面向方面编程)的设计,它的作用是对MVC框架处理客户端请求注入额外的逻辑,以非常简单优美的方式实现横切关注点(Cross-cutting Concerns).横切关 ...

  4. Eclipse代码格式化规范

    附件()是Eclipse代码格式文件,根据以下步骤导入到Eclipse中,帮助规范代码格式. 导入步骤:1. Window -> Performances2. Java -> Code S ...

  5. MongoDB学习笔记~根据子集合里某个属性排序

    回到目录 这个问题是这样的,有一个实体dog,里面有集合属性DogHistory,它里面有一些自己的属性,其中一个是SortNum,主要用来进行排序,而且这个排序可以影响主对象,即影响dog类,这个在 ...

  6. edit

    <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title> ...

  7. 使用angular中ng-repeat , track by的用处

    我们见到最简单的例子是: <div ng-repeat="link in links" ></div> 如果item的值有重复的,比如links=[&quo ...

  8. angularjs UI Libraries

    angularjs UI Libraries ● ng-bootstrap is currently available. ● PrimeNG has largest number of compon ...

  9. Referenced file contains errors (http://www.springframework.org/schema/context). For more information, right click on the message in the Problems

    spring 配置文件的DTD或schema出问题,一般两种情况: 1.当前网络环境不稳定,按住ctrl+"http://www.springframework.org/schema/con ...

  10. es6学习笔记一数组(中)

    接着上一篇,给大家再分享一些数组的其他方法.大家也可以去点击这里学习数组更多的方法 concat方法: 概述:    concat() 方法将传入的数组或非数组值与原数组合并,组成一个新的数组并返回. ...