首先创建一个UIView子类,选择菜单File→New→File…(键盘快捷键是Command-N),再选择iOS下的Cocoa Touch,然后选择Objective-C class(见图4-5)。
图4-5 创建新类
点击Next按钮,在Class文本框中输入BNRHypnosisView,在Subclass of下拉菜单中选择UIView(见图4-6)。
图4-6 设置新类的父类是UIView
单击Next按钮,确保选中了Targets中Hypnosister前的选择框。单击Create按钮。
在编写绘制同心圆的代码之前,首先需要了解如何通过编写代码创建和显示视图对象。为简单起见,本节不会在BNRHypnosisView中绘制同心圆,而是绘制一个红色背景的矩形,如图4-7所示。
图4-7 BNRHypnosisView的第一个版本
视图及其frame属性
打开BNRHypnosisView.m文件。UIView子类的模板会自动生成两个方法,第一个是initWithFrame:,该方法是UIView的指定初始化方法,带有一个CGRect结构类型的参数,该参数会被赋给UIView的frame属性。
@property(nonatomic)CGRect frame;
视图的frame属性保存的是视图的大小和相对于父视图的位置。
CGRect结构包含另外两个结构:origin和size。origin的类型是CGPoint结构,该结构包含两个float类型的成员:x和y。size的类型是CGSize结构,该结构也包含两个float类型的成员:width和height(见图4-8)。
图4-8 CGRect
图由frame可知,视图对象的形状一定是矩形。
打开BNRAppDelegate.m,在文件顶部导入BNRHypnosisView类的头文件。
#import /"BNRAppDelegate.h/"
#import /"BNRHypnosisView.h/"
@implementation BNRAppDelegate
在BNRAppDelegate.m中找到application:didFinishLaunchingWithOptions:方法,在创建UIWindow对象的代码之后创建一个CGRect结构,然后使用该结构创建一个BNRHypnosisView对象,并设置backgroundColor属性为红色。最后,将BNRHypnosisView对象加入UIWindow对象,使其成为窗口的子视图。
- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[UIWindow alloc] initWithFrame:
[[UIScreen mainScreen] bounds]];
CGRect firstFrame = CGRectMake(160, 240, 100, 150);
BNRHypnosisView *firstView = [[BNRHypnosisView alloc]
initWithFrame:firstFrame];
firstView.backgroundColor = [UIColor redColor];
[self.window addSubview:firstView];
self.window.backgroundColor = [UIColor whiteColor];
[self.window makeKeyAndVisible];
return YES;
}
结构不是Objective-C对象,因此不能向CGRect发送消息。可以使用CGRectMake函数创建一个CGRect。该函数需要传入origin.x、origin.y、size.width和size.height的值作为参数。CGRect占用的内存比大部分对象都小,因此不用传入指针——initWithFrame:方法中需要传入的参数类型是CGRect,而不是CGRect *。
为了设置backgroundColor属性,需要使用UIColor的类方法:redColor。这是一个简便方法,它返回一个表示红色的UIColor对象。UIColor中定义了一系列对应于常见颜色的简便方法,例如blueColor、blackColor和clearColor。
构建并运行应用,红色的矩形就是BNRHypnosisView对象。因为BNRHypnosisView对象的frame结构中的origin是(160, 240),所以相对于UIWindow对象(BNRHypnosisView对象的父视图)的左上角,BNRHypnosisView对象的左上角会位于右侧160点、下方240点的位置。此外,因为这个frame结构中的size是(100, 150),所以BNRHypnosisView对象的宽度为100点,高度为150点(见图4-9)。
图4-9 向Hypnosister应用中添加一个BNRHypnosisView对象
请注意这些值的单位是点(points),不是像素(pixels)。如果单位是像素,那么视图在Retina和非Retina显示屏上的大小无法保持一致。点的大小与设备分辨率相关,取决于屏幕以多少像素显示一个点:在Retina显示屏上,一个点是两个像素高度、两个像素宽度;非Retina显示屏则是一个像素高度、一个像素宽度。如果打印到纸上,一英寸是72点。为了保持应用界面在不同分辨率的屏幕上看起来一致,大小、位置、直线、曲线等都使用点作为单位。
在Xcode控制台中,请注意输出的警告信息:“Application windows are expected to have a root view controller at the end of application launch.(应用启动完毕之后,需要为窗口设置一个根视图控制器。)”视图控制器可以用来管理应用中的多个视图对象,大部分iOS应用都有一个或多个视图控制器。Hypnosister是一个非常简单的应用,不需要视图控制器,所以读者可以忽略这行警告信息。第6章会介绍更多关于视图控制器的知识。
现在请看应用中已经创建好的视图层次结构(见图4-10)。
图4-10 UIWindow对象有一个子视图——BNRHypnosisView
每个UIView对象都有一个superview属性。将一个视图作为子视图加入另一个视图时,会自动创建相应的反向关联。在本例中,BNRHypnosisView对象的superview属性指向应用的UIWindow对象。(为了避免强引用循环,superview属性是弱引用。)
接下来尝试向视图层次结构中再加入一个视图。打开BNRAppDelegate.m,再创建一个BNRHypnosisView对象并设置不同的大小、位置和背景颜色:
...
[self.window addSubview:firstView];
CGRect secondFrame = CGRectMake(20, 30, 50, 50);
BNRHypnosisView *secondView = [[BNRHypnosisView alloc]
initWithFrame:secondFrame];
secondView.backgroundColor = [UIColor blueColor];
[self.window addSubview:secondView];
self.window.backgroundColor = [UIColor whiteColor];
...
再次构建并运行应用,除了之前添加的红色矩形外,窗口的左上角还有一个蓝色矩形。新的视图层次结构如图4-11所示。
图4-11 UIWindow包含两个子视图
视图层次结构还可以包含两层以上的嵌套。在BNRAppDelegate.m中将第二个BNRHypnosisView对象加入第一个BNRHypnosisView对象,而不是UIWindow对象(见图4-12)。
图4-12 secondView是firstView的子视图
代码如下:
...
BNR HypnosisView *secondView = [[BNRHypnosisView alloc]
initWithFrame:secondFrame];
secondView.backgroundColor = [UIColor blueColor];
[self.window addSubview:secondView];
[firstView addSubview:secondView];
...
构建并运行应用,请注意secondView在屏幕上的位置发生了变化。视图的frame所代表的位置是相对于其父视图的,所以secondView的左上角将以firstView的左上角位置为起点,偏移(20, 30)点。新的视图层次结构如图4-13所示。
图4-13 Hypnosister应用新的视图层次结构
(如果读者觉得蓝色的BNRHypnosisView对象看起来比之前大,这是由于该视图位于更小的区域而产生的一种错觉。它的大小其实没有改变。)现在读者已经理解了视图层次结构,在进行下一步工作之前,请先删除secondView对象。
...
[self.window addSubview:firstView];
CGRect secondFrame = CGRectMake(20, 30, 50, 50);
BNRHypnosisView *secondView = [[BNRHypnosisView alloc]
initWithFrame:secondFrame];
secondView.backgroundColor = [UIColor blueColor];
[view addSubview:secondView];
self.window.backgroundColor = [UIColor whiteColor];
...