• 视图基础
  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. AngularJS基础入门初探

    一.AngularJS简介 1.1 什么是AngularJS (1)一款非常优秀的前端JS框架,可以方便实现MVC/MVVM模式 (2)由Misko Hevery 等人创建,2009年被Google所 ...

  2. ASP.Net WebForm温故知新学习笔记:一、aspx与服务器控件探秘

    开篇:毫无疑问,ASP.Net WebForm是微软推出的一个跨时代的Web开发模式,它将WinForm开发模式的快捷便利的优点移植到了Web开发上,我们只要学会三步:拖控件→设属性→绑事件,便可以行 ...

  3. SQL Server 2012故障转移的looksalive check和is alive check

    什么是looksalive check和is alive check SQL Server故障转移集群是建立在windows集群服务上的一种热备的高可用方案.在集群运行过程中,windows集群服务定 ...

  4. 生成模型(Generative Model)与判别模型(Discriminative Model)

    摘要: 1.定义 2.常见算法 3.特性 4.优缺点 内容: 1.定义 1.1 生成模型: 在概率统计理论中, 生成模型是指能够随机生成观测数据的模型,尤其是在给定某些隐含参数的条件下.它给观测值和标 ...

  5. css+div常用属性备忘录

    学习软件设计有一年多了,明年五月就要毕业了.回头看看发现自己其实挺差劲的. 最近开通了博客所以就整理了一下笔记,在这里发布一下自己以前学习css时总是记不住去翻书又很常用的属性,都是一些很基础的. 大 ...

  6. WPF入门教程系列十七——WPF中的数据绑定(三)

    四. XML数据绑定 这次我们来学习新的绑定知识,XML数据绑定.XmlDataProvider 用来绑定 XML 数据,该XML数据可以是嵌入.Xmal文件的 XmlDataProvider 标记中 ...

  7. Ucos系统任务间的通信详解

    物联网开发中,ucos系统任务间的通信是指,两个任务之间有数据的交互,具体的一起来看看吧. 1)消息邮箱 我们还是提供两个任务Task1和Task2,假设我们还是解决刚刚的问题,Task1进行按键扫描 ...

  8. Nginx配置文件nginx.conf详解

    #定义Nginx运行的用户和用户组 user www www; #nginx进程数,建议设置为等于CPU总核心数. worker_processes 8; #全局错误日志定义类型,[ debug | ...

  9. Security8:删除Role 和 User

    数据库的Role 和 User都是基于Specified DB的,在删除这些Principal之前,必须使用Use clause,切换到指定的DB中. sys.database_role_member ...

  10. Data Profiling Task

    Data Profiling Task 是用于收集数据的Metadata的Task,在使用ETL处理数据之前,应该首先检查数据质量,对数据进行分析,这将对Table Schema的设计结构和生成ETL ...