iOS Programming View Controllers 视图控制器
1.1
A view controller is an instance of a subclass of UIViewController.
一个view controller 是一个UIViewController的子类。
A view controller manages a view hierarchy.
一个view controller 管理一个视图树。
It is responsible for creating view objects that make up the hierarchy, for handling events associated with the view objects in its hierarchy, and for adding its hierarchy to the window.
它负责创建view 对象,处理event,把视图树加到window上。
In this chapter, you will create an application named HypnoNerd. In HypnoNerd, the user will be able to switch between two view hierarchies – one for being hypnotized and the other for setting a reminder for hypnosis on a future date.
在本章中,你将创建两个视图,并能切换。
step1:Create a new iOS project (Command-Shift-N) from the Empty Application template. Name this project HypnoNerd
创建一个新应用名字为HypnoNerd。
Step2
:
Step3: 创建UIViewController 的子类
Step4:Open BNRHypnosisViewController.h and change the superclass to UIViewController.
@interface BNRHypnosisViewController : UIViewController @end
打开BNRHypnosisViewController.h,将继承NSObject改成继承UIViewController
1.2 The view of a view controller
As a subclass of UIViewController, BNRHypnosisViewController inherits an important property: @property (nonatomic, strong) UIView *view;
作为UIViewController的子类,它继承了一个很重要的属性:UIView.
This property points to a UIView instance that is the root of the view controller's view hierarchy.
这个属性指定了一个UIView实例,而这个实例就是view controller
A view controller's view is not created until it needs to appear on the screen. This optimization is called lazy loading, and it can often conserve memory and improve performance.
view controller 的view 总是到要显示到屏幕上时才创建。这样能解决内存和提升性能。
There are two ways that a view controller can create its view hierarchy:
有两种方式创建view controller 的view hierarchy.
(1)programmatically, by overriding the UIViewController method loadView.
编程 重写UIViewController 的loadView方法。
(2)in Interface Builder, by loading a NIB file. (Recall that a NIB file is the file that gets loaded and the XIB file is what you edit in Interface Builder.)
在Interface Builder 通过加载NIB文件。
1.3 Creating a view programmatically
编程方法创建View
step 1 :Open BNRHypnosisViewController.m and import the header file for BNRHypnosisView. Then override loadView to create a screen-sized instance of BNRHypnosisView and set it as the view of the view controller.
在view controller 里重写loadView,将controller的view 设置为你要想要的view。
#import "BNRHypnosisViewController.h" #import "BNRHypnosisView.h"
@implementation BNRHypnosisViewController
- (void)loadView
{
// Create a view
BNRHypnosisView *backgroundView = [[BNRHypnosisView alloc] init];
// Set it as *the* view of this view controller
self.view = backgroundView; }
When a view controller is created, its view property is nil. If a view controller is asked for its view and its view is nil, then the view controller is sent the loadView message.
当一个view controller 被创建时,它的view property 是nil。当view controller被 要求view的时候,如果view 是空,那么view controller 将会发送loadView 信息。
The next step is to add the view hierarchy of the BNRHypnosisViewController to the application window so that it will appear on screen to users.
接下来要要将view controller 的view hierarchy 加载到application window这样就能显示给用户了。
1.4 Setting the root view controller 设置 root view controller
There is a convenient method for adding a view controller's view hierarchy to the window: UIWindow's setRootViewController:. Setting a view controller as the rootViewController adds that view controller's view as a subview of the window.
用一个方便的方法UIWindow的setRootViewController方法,可以将view controller 的view hierarchy 到窗口。
Setting a view controller as the rootViewController adds that view controller's view as a subview of the window. It also automatically resizes the view to be the same size as the window.
设值view controller 作为rootViewController 自动将view controller 的视图作为window 的subview . 并且将自动调整view 的尺寸和window 一样。
导入你想作为根视图控制器的控制器头文件到Delegate方法中。
#import "BNRAppDelegate.h"
#import "BNRHypnosisViewController.h"
@implementation BNRAppDelegate
- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; // Override point for customization after application launch
BNRHypnosisViewController *hvc = [[BNRHypnosisViewController alloc] init];
self.window.rootViewController = hvc;
self.window.backgroundColor = [UIColor whiteColor]; [self.window makeKeyAndVisible];
return YES;
}
The view of the root view controller appears at the start of the run of the application. Thus, the window asks for it when setting the view controller as its rootViewController.
root view controller 的view出现在应用一开始。因此window请求view 在设置view controller 作为rootViewController .
我们可以猜想setRootViewController 应该是这样的:
- (void)setRootViewController:(UIViewController *)viewController
{
// Get the view of the root view controller UIView *rootView = viewController.view;
// Make a frame that fits the window's bounds CGRect viewFrame = self.bounds; rootView.frame = viewFrame;
// Insert this view as window's subview [self addSubview:rootView];
// Update the instance variable _rootViewController = viewController;
}
1.5 Another UIViewController 另一个UIViewController
The BNRReminderViewController's view will be a full-screen UIView with two subviews – an instance
of UIDatePicker and an instance of UIButton
这个视图控制器的view 将包含两个UIVIiew 一个UIDatePicker and UIButtoon。
In addition, the view controller will have a datePicker property that points to the UIDatePicker object.
另外View controller 将会有一个dataPicker 属性指向UIDatapicker。
Finally, the view controller will be the target of the UIButton and must implement its action method addReminder:.
view controller 将会成为UIButton的target,并且必须实现action 方法addReminder .
1.6Creating a view in Interface Builder 在Interface Builder 上创建一个view.
#import "BNRReminderViewController.h"
@interface BNRReminderViewController ()
@property (nonatomic, weak) IBOutlet UIDatePicker *datePicker; @end
@implementation BNRReminderViewController
- (IBAction)addReminder:(id)sender
{
NSDate *date = self.datePicker.date;
NSLog(@"Setting a reminder for %@", date); }
@end
The IBOutlet and IBAction keywords tell Xcode that you will be making
these connections in Interface Builder
IBOutlet 和IBAction 关键字告诉Xcode 你将在Interface Build 连接。
Create a new XIB file by selecting File → New → File.... From the iOS section, select User Interface, choose the Empty template, and click Next
1.7Creating view objects 创建新的view object
Search for UIView. Drag a View object onto the canvas. By default, it will be screen-sized, which is what you want.
把一个View 对象拖到Canvas 。默认情况下它将是与屏幕匹配的。
find a Date Picker and a Button in the library and drag them onto the view.
In the document outline to the left of the canvas, you can see your view hierarchy: the View is the root and the Picker and Button are its subviews.
在document outline ,你将看到view hierarchy . view is the root .picker and button 是subview.
When a view controller gets its view hierarchy by loading a NIB file, you do not override loadView. The default implementation of loadView knows how to handle loading a NIB file.
当一个view controller 通过NIB 文件获得view hierarchy ,你不需要重写loadView. loadView的默认实现知道如何处理NIB文件。
The BNRReminderViewController does need to know which NIB file to load. You can do this in UIViewController's designated initializer:- (instancetype)initWithNibName:(NSString *)nibName bundle:(NSBundle *)nibBundle;
view controller 需要知道哪个NIB文件需要加载。你可以用UIViiewcontroller的指定初始化方法:- (instancetype)initWithNibName:(NSString *)nibName bundle:(NSBundle *)nibBundle;
In this method, you pass the name of the NIB file to be loaded and the bundle in which to look for that file.
在这个方法中你传递了NIB文件的名字,以及在哪个bundle里查找这个文件。
• Bundle (OS X), a type of directory in NEXTSTEP and OS X
bundle 的含义:在Nextstep 和OS X的一类目录
// This will get a pointer to an object that represents the app bundle NSBundle *appBundle = [NSBundle mainBundle];
// Look in the appBundle for the file BNRReminderViewController.xib BNRReminderViewController *rvc =
[[BNRReminderViewController alloc] initWithNibName:@"BNRReminderViewController" bundle:appBundle];
self.window.rootViewController = hvc;
self.window.rootViewController = rvc;
The bundle that you are getting by sending the mainBundle message is the application bundle.
通过发送 mainBundle 获得的bundle 是应用程序的bundle。
This bundle is a directory on the filesystem that contains the application's executable as well as resources (like NIB files) that the executable will use.
这个bundle是包含了应用执行或者能执行的资源的文件系统的一个目录。
When the corresponding NIB file was loaded, these objects were instantiated. But you have not made connections to link the instantiated objects with the BNRReminderViewController in the running application.
当NIB文件加载时,这些对象初始化,但是你没有将ViewController 与初始化对象连接。
This includes the view controller's view property. Thus, when the view controller tries to get its view added to the screen, an exception is thrown because view is nil.
这包括view controller 的一个view .当view controller 加载时,一个exception抛出了,因为view是nil。
1.8 Connecting to File's Owner
The File's Owner object is a placeholder – it is a hole intentionally left in the XIB file. Loading a NIB, then, is a two-part process: instantiate all of the objects archived in the XIB and then drop the object that is loading the NIB into the File's Owner hole and establish the prepared connections
File's Owner 对象是一个代理。 加载一个NIB,分为两步:1. 实例化所有在Xib中得对象。2.接着把在NIB中加载的对象放到File owner 中并建立连接准备。
So if you want to connect to the object that loads the NIB at runtime, you connect to the File's Owner when working in the XIB. The first step is to tell the XIB file that the File's Owner is going to be an instance of BNRReminderViewController.
所以如果想把在NIB文件中加载的对象,你必须连接File'owner 。第一步便是告诉File'owner 是view controller 的一个实例。
Identity inspector for File's Owner
Now you can make the missing connections. Let's start with the view outlet.
In the dock, Control-click File's Owner to bring up the panel of available connections. Drag from view to
the UIView object in the canvas to set the view outlet to point at the UIView
you declared the datePicker outlet as weak. Declaring outlets as weak is a convention from earlier versions of iOS. In these versions, a view controller's view was automatically destroyed any time that system memory was low and then was recreated later if needed.
你在datePicker outlet 属性设置为weak。声明outlet为weak类型是早起iOS版本的传统。在这些版本中, 如果内存过低,那么view controller 的view 将在任何时间自动销毁,当需要的时候再重新创建。
Ensuring that the view controller only had weak ownership of the subviews meant that destroying the view also destroyed all of its subviews and avoided memory leaks.
确保view controller 只有weak ownership 的subview意味着当destroy the view的时候也destroy 所有的子视图,避免内存泄露。