iOS 视图与视图层次结构(内容根据iOS编程)
- 视图基础
- 视图是 UIView 对象,或者其子对象。
- 视图知道如何绘制自己。
- 视图可以处理事件,例如触摸(touch)。
- 视图会按照层次结构排列,位于视图层次结构顶端的是应用窗口。
- 视图层次结构
任何应用有且只有一个 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编程)的更多相关文章
- iOS UIGestureRecognizer与UIMenuController(内容根据iOS编程)
UIGestureRecognizer 对象会截取本应由视图处理的触摸事件.当某个UIGestureRecognizer对象识别出特定的手势后,就会向指定的对象发送指定的消息.iOS SDK默认提供若 ...
- iOS开发系列--视图切换
概述 在iOS开发中视图的切换是很频繁的,独立的视图应用在实际开发过程中并不常见,除非你的应用足够简单.在iOS开发中常用的视图切换有三种,今天我们将一一介绍: UITabBarController ...
- IOS开发之视图和视图控制器
视图(View), 视图控制器(ViewController)是IOS开发UI部分比较重要的东西.在学习视图这一块的东西的时候,感觉和Java Swing中的Panel差不多.在UIKit框架中都有一 ...
- iOS学习之视图控制器
一.自定义视图(label-textField组合视图) 1.自定义视图:系统标准UI之外,自己组合出的新的视图. 2.优点:iOS提供了很多UI组件,借助它们我们可以实现不同的功 ...
- Swift - iOS中各种视图控制器(View Controller)的介绍
在iOS中,不同的视图控制器负责不同的功能,采用不同的风格向用户呈现信息.下面对各个视图控制器做个总结: 1,标准视图控制器 - View Controller 这个控制器只是用来呈现内容.通常会用来 ...
- iOS开发中视图控制器ViewControllers之间的数据传递
iOS开发中视图控制器ViewControllers之间的数据传递 这里我们用一个demo来说明ios是如何在视图控制器之间传递重要的参数的.本文先从手写UI来讨论,在下一篇文章中讨论在storybo ...
- IOS弹出视图 LewPopupViewController
LewPopupViewController是一款IOS弹出视图软件.iOS 下的弹出视图.支持iPhone/iPad. 软件截图 使用方法 弹出视图 1 2 3 4 5 PopupView *vie ...
- iOS开发:视图生命周期
iOS应用的视图状态分为以下几种 在viewcontroller的父类UIViewController中可以看到如下代码,通过重写不同的方法对操作视图渲染. @available(iOS 2.0, * ...
- 浅谈iOS中的视图优化
引言: 让我们来思考几个问题,你开发过的产品,它还有可以优化的地方吗?能增加它的帧率吗?能减少多余的CPU计算吗?是不是存在多余的GPU渲染?业务这点工作量对于越来越强大的设备面前显得微不足道,但作为 ...
随机推荐
- 参数的元数据信息&数据库的元数据信息
package it.cast.jdbc; import java.sql.Connection; import java.sql.DatabaseMetaData; import java.sql. ...
- Quartz 2D在ios中的使用简述一:坐标体系
Quartz 2D是一个二维图形绘制引擎,支持iOS环境和Mac OS X环境,官方文档:Quartz 2D Programming Guide. 一.坐标体系 这样的坐标体系就导致我们使用Quart ...
- HDFS DataNode 设计实现解析
前文分析了 NameNode,本文进一步解析 DataNode 的设计和实现要点. 文件存储 DataNode 正如其名是负责存储文件数据的节点.HDFS 中文件的存储方式是将文件按块(block)切 ...
- ABP理论学习之数据过滤器
返回总目录 本篇目录 介绍 预定义过滤器 关闭过滤器 开启过滤器 设置过滤器参数 定义自定义过滤器 其他ORM 介绍 软删除模式通常用于不会真正从数据库删除一个实体而是仅仅将它标记为"已删除 ...
- ABP理论学习之功能管理
返回总目录 本篇目录 介绍 功能类型 定义功能 检查功能 功能管理者 版本说明 介绍 大多数的Saas(多租户)应用都有不同 功能的 版本(包).因此,他们可以给租户(客户)提供不同的 价格和功能选项 ...
- Key/Value之王Memcached初探:三、Memcached解决Session的分布式存储场景的应用
一.高可用的Session服务器场景简介 1.1 应用服务器的无状态特性 应用层服务器(这里一般指Web服务器)处理网站应用的业务逻辑,应用的一个最显著的特点是:应用的无状态性. PS:提到无状态特性 ...
- 扩展Bootstrap Tooltip插件使其可交互
最近在公司某项目开发中遇见一特殊需求,请笔者帮助,因此有了本文的插件.在前端开发中tooltip是一个极其常用的插件,它能更好向使用者展示更多的文档等帮助信息.它们通常都是一些静态文本信息.但同事他们 ...
- SecureCRT使用技巧
原创文章转载请注明出处:@协思, http://zeeman.cnblogs.com 修改SecureCRT颜色配置 option->Global options –> Termina ...
- Java使用RSA加密解密及签名校验
该工具类中用到了BASE64,需要借助第三方类库:javabase64-1.3.1.jar注意:RSA加密明文最大长度117字节,解密要求密文最大长度为128字节,所以在加密和解密的过程中需要分块进行 ...
- 《Entity Framework 6 Recipes》中文翻译系列 (22) -----第五章 加载实体和导航属性之延迟加载
翻译的初衷以及为什么选择<Entity Framework 6 Recipes>来学习,请看本系列开篇 第五章 加载实体和导航属性 实体框架提供了非常棒的建模环境,它允许开发人员可视化地使 ...