UIViewController

JXHypnosisView

  • 创建 UIViewController 子类

UIViewController  子类文件,并将其命名为  JXHypnosisViewController

  • UIViewController 的 view 属性

JXHypnosisViewController 控制器从父类集成下来一个重要属性: @property (nonatomic,strong) UIView * view; . view 属性指向一个  UIView 对象。那么这个view就是这个视图层次结构的根视图,当程序将 view 作为子视图加入窗口时,也会加入UIViewController对象所管理的整个视图层次结构;

  视图控制器不会再起被创建出来的那一刻马上创建并载入相应的视图。只有当应用需要将某个视图控制器的视图显示到屏幕上时,相应的视图控制器才会创建其视图。这种延迟加载视图的做法能提高内存的使用效率。

  视图控制器可以通过两种方式创建视图层次结构。

UIViewController 的 loadView

  2. NIB 方式:使用  Interface Builder 创建一个 NIB 文件,然后加入所需的视图层次结构,最后视图控制器会在运行时加载由该 NIB 文件 文件编译而成的 XIB 文件。

  通过代码方法创建视图

JXHypnosisViewController.m 顶部导入头文件  JXHypnosisView.h ,然后覆盖  loadView 方法,创建一个大小与屏幕相同的  JXHypnosisView



#import "JXHypnosisViewController.h"
#import "JXHypnosisView.h"

@interface JXHypnosisViewController ()


@end

@implementation JXHypnosisViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    
}

- (void)loadView {
    // 创建一个 JXHypnosisView 对象
    JXHypnosisView * backgroundView = [[JXHypnosisView alloc] init];
    
    // 将 JXHypnosisView 对象赋给视图控制器的view 属性
    self.view = backgroundView;
}


@end



  视图控制器刚被创建时,其 view 属性会被初始化为 nil 。之后,当应用需要将该视图控制器的视图显示到屏幕上时,如果 view 属性是 nil ,就会自动调用  loadView

JXHypnosisViewController

  设置根视图控制器

UIWindow 对象提供了一个方法, setRootViewController: 。当程序将某个视图控制器设置为  UIWindow 对象的  rootViewController 时, UIWindow

AppDelegate.m  文件中导入需要的控制器,最后将其设置为  UIWindow



#import "AppDelegate.h"
#import "JXHypnosisViewController.h"

@interface AppDelegate ()

@end

@implementation AppDelegate


- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    
    self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
    
    // 在这里添加代码初始化
    JXHypnosisViewController * hvc = [[JXHypnosisViewController alloc] init];
    
    self.window.rootViewController = hvc;
    
    self.window.backgroundColor = [UIColor whiteColor];
    [self.window makeKeyAndVisible];
    return YES;
}



rootViewController 的view需要在启动完毕后就显示,所以  UIWindow 对象会在设置完 rootViewController

 

  • 另一个视图控制器

JXReminderViewController

  在  Interface Builder

JXReminderViewController.m 添加一个类扩展,声明一个 datePicker 属性,然后创建一个 addReminder:方法,向控制台输出datePicker的日期。



#import "JXReminderViewController.h"

@interface JXReminderViewController ()

@property (nonatomic,weak) IBOutlet UIDatePicker * datePicker;

@end

@implementation JXReminderViewController

- (IBAction)addReminder:(id)sender {
    NSDate * date = self.datePicker.date;
    NSLog(@"Setting a reminder for %@",date);
}

- (void)viewDidLoad {
    [super viewDidLoad];
    
    
}

@end



  接下来我们需要创建一个 XIB 文件。

  创建视图对象

  从对象面板库拖拽一个 UIView 对象到画布。然后拖拽一个 UIDatePicker 以及一个 UIButton .

  加载 NIB 文件

  当视图控制器从 NIB 文件中创建视图层次结构时,不需要覆盖  loadView 方法,默认的 loadView

UIViewController 的指定初始化方法中为  JXReminderViewController 设置需要加载的 NIB 文件:



- (instancetype)initWithNibName:(nullable NSString *)nibNameOrNil bundle:(nullable NSBundle *)nibBundleOrNil



  该方法包含两个参数,分别用于指定 NIB 文件的文件名及其所在的程序包。



#import "AppDelegate.h"
#import "JXHypnosisViewController.h"
#import "JXReminderViewController.h"
@interface AppDelegate ()

@end

@implementation AppDelegate


- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    
    self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
    
    // 在这里添加代码初始化
    JXHypnosisViewController * hvc = [[JXHypnosisViewController alloc] init];
    
    // 获取指向 NSBundle 对象的指针,该 NSBundle 对象代表应用的主程序包
    NSBundle * appBundle = [NSBundle mainBundle];
    
    // 告诉初始化方法,在appBundle中查找 JXReminderViewController.xib 文件
    JXReminderViewController * rvc = [[JXReminderViewController alloc] initWithNibName:@"JXReminderViewController" bundle:appBundle];
    
    self.window.rootViewController = rvc;
    
    self.window.backgroundColor = [UIColor whiteColor];
    [self.window makeKeyAndVisible];
    return YES;
}



  向 NSBundle 发送 mainBundle 消息可以得到应用的主程序包。主程序包对应于文件系统中项目的根目录,包含代码文件和资源文件,初始化需要的  JXReminderViewController.xib

  到目前为止,视图层次结构中的所有视图对象都已经创建和设置好了,视图控制器的初始化方法可以正确加载 NIB 文件了。但是目前来说运行程序就会崩溃,,错误信息为 



Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: '-[UIViewController _loadViewFromNibNamed:bundle:] loaded the "JXReminderViewController" nib but the view outlet was not set.'



JXReminderViewController  关联起来,包括  JXReminderViewController

  关联 File's Owner

File's Owner  对象是一个展位符对象,他是 XIB 文件特意留下的一个 “空洞”。当某个视图控制器将 XIB 文件加载为 NIB 文件时,首先会创建 XIB 文件中的所有的视图对象,然后会将自己填入响应的 's Owner  空洞,并建立之前在  Interface Builder

's Owner 。首先需设置  JXReminderViewController.xib 文件中的  's Owner  是  JXReminderViewController

iOS视图层级设置 ios视图控制器的功能_视图控制器

JXReminderViewController

iOS视图层级设置 ios视图控制器的功能_#import_02

JXReminderViewController 对象在运行的时候可以加载 view 了。同样的方法关联其余插座变量,首先将插座变量 datePicker 关联至 UIDatePicker 对象,然后将 UIButton 对象关联至 File's Owner 并选择弹出式菜单中的 addReminder: 

iOS视图层级设置 ios视图控制器的功能_#import_03

iOS视图层级设置 ios视图控制器的功能_xcode_04

 

 

 

 

 

 

 

 

 

 

 

JXReminderViewController  的 datePicker 插座变量声明为弱引用。将插座变量申明为弱引用是一种编程约定。当系统的可用内存偏少时,视图控制器会自动释放其视图并在之后需要显示时再创建。

 

  • UITabBarController

UITabBarController 对象,使应用能够在  JXReminderViewController 对象和  JXHypnosisViewController

UITabBarController 对象可以保存一组视图控制器,此外  UITabBarController 对象还会在屏幕底部显示一个标签栏,标签栏会有多个标签选项,分别对应  UITabBarController 对象保存的每一个视图控制器。单击某个标签项, UITabBarController


#import "AppDelegate.h"
#import "JXHypnosisViewController.h"
#import "JXReminderViewController.h"
@interface AppDelegate ()

@end

@implementation AppDelegate


- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    
    self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
    
    // 在这里添加代码初始化
    JXHypnosisViewController * hvc = [[JXHypnosisViewController alloc] init];
    
    // 获取指向 NSBundle 对象的指针,该 NSBundle 对象代表应用的主程序包
    NSBundle * appBundle = [NSBundle mainBundle];
    
    // 告诉初始化方法,在appBundle中查找 JXReminderViewController.xib 文件
    JXReminderViewController * rvc = [[JXReminderViewController alloc] initWithNibName:@"JXReminderViewController" bundle:appBundle];
    
    UITabBarController * tabBarController = [[UITabBarController alloc] init];
    tabBarController.viewControllers = @[hvc,rvc];
    
    self.window.rootViewController = rvc;
    self.window.rootViewController = tabBarController;

    self.window.backgroundColor = [UIColor whiteColor];
    [self.window makeKeyAndVisible];
    return YES;
}


  构建并运行,就能在底部自由切换两个控制器。

  UITabBarController 也是 UIViewController 的子类,也有一个名为 view 的属性。UITabBarController 对象的 view 指向一个包含该两个子视图的 UIView 对象,分别是标签栏和当前选中的视图控制器的视图。

  设置标签项 

  标签栏上的每一个标签项都可以显示标题和图片,具体数据需要由视图控制器的tabBarItem属性提供。当 UITabBarController 对象加入一个视图控制器时,就会为标签栏增加一个标签项,并根据新加入的视图控制器的 tabBarItem 属性设置该标签项的标题和图片。

JXHypnosisViewController 覆盖  UIViewController 的指定初始化方法  initWithNibName:bundle:


#import "JXHypnosisViewController.h"
#import "JXHypnosisView.h"

@interface JXHypnosisViewController ()


@end

@implementation JXHypnosisViewController

- (instancetype)initWithNibName:(NSString *)nibNameOrNil
                         bundle:(NSBundle *)nibBundleOrNil {
    self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
    if (self) {
        // 设置标签项的标题
        self.tabBarItem.title = @"Hypnotize";
        
        // 从图片文件创建一个 UIImage 对象
        UIImage * i = [UIImage imageNamed:@"Hypno"];
        
        // 将 UIImage 对象赋值给标签项的 iamge 属性
        self.tabBarItem.image = i;
    }
    return self;
}
- (void)viewDidLoad {
    [super viewDidLoad];
    
    
}

- (void)loadView {
    // 创建一个 JXHypnosisView 对象
    JXHypnosisView * backgroundView = [[JXHypnosisView alloc] init];
    
    // 将 JXHypnosisView 对象赋给视图控制器的view 属性
    self.view = backgroundView;
}


@end


JXReminderViewController


#import "JXReminderViewController.h"

@interface JXReminderViewController ()

@property (nonatomic,weak) IBOutlet UIDatePicker * datePicker;

@end

@implementation JXReminderViewController
- (instancetype)initWithNibName:(NSString *)nibNameOrNil
                         bundle:(NSBundle *)nibBundleOrNil {
    self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
    if (self) {
        // 设置标签项的标题
        self.tabBarItem.title = @"Reminder";
        
        // 从图片文件创建一个 UIImage 对象
        UIImage * i = [UIImage imageNamed:@"Time"];
        
        // 将 UIImage 对象赋值给标签项的 iamge 属性
        self.tabBarItem.image = i;
    }
    return self;
}
- (IBAction)addReminder:(id)sender {
    NSDate * date = self.datePicker.date;
    NSLog(@"Setting a reminder for %@",date);
}

- (void)viewDidLoad {
    [super viewDidLoad];
    
    
}

@end


  • 视图控制器的初始化方法

  之前在创建 JXHypnosisViewController的标签项时覆盖了  initWithNibName:bundle: 但是,在 AppDelegate 中是使用 init方法来初始化  JXHypnosisViewController  对象的。这是由于  initWithNibName:bundle: 是  UIViewController 的指定初始化方法,向视图控制器发送  init 会调用 initWithNibName:bundle: 方法,并为两个参数都传入 nil ,因此使用  init

  

  • 添加本地通知

  接下来使用本地通知。本地通知用于向用户提醒一条消息,除了闹钟,其他的并没有什么卵用。

UILocalNotification 对象并设置其显示内容和提醒时间,然后调用  UIApplication 单粒对象的  scheduleLocalNotification:


#import "JXReminderViewController.h"

@interface JXReminderViewController ()

@property (nonatomic,weak) IBOutlet UIDatePicker * datePicker;

@end

@implementation JXReminderViewController
- (instancetype)initWithNibName:(NSString *)nibNameOrNil
                         bundle:(NSBundle *)nibBundleOrNil {
    self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
    if (self) {
        // 设置标签项的标题
        self.tabBarItem.title = @"Reminder";
        
        // 从图片文件创建一个 UIImage 对象
        UIImage * i = [UIImage imageNamed:@"Time"];
        
        // 将 UIImage 对象赋值给标签项的 iamge 属性
        self.tabBarItem.image = i;
    }
    return self;
}
- (IBAction)addReminder:(id)sender {
    NSDate * date = self.datePicker.date;
    NSLog(@"Setting a reminder for %@",date);
    UILocalNotification * note = [[UILocalNotification alloc] init];
    note.alertBody = @"Hypnotize me!";
    note.fireDate = date;
    
    [[UIApplication sharedApplication] scheduleLocalNotification:note];
}

- (void)viewDidLoad {
    [super viewDidLoad];
    
    
}

@end


  构建并运行,选中提醒时间即可。

 

  • 加载和显示视图

JXHypnoNerd 应用中有两个视图控制器。当应用启动后,标签栏会默认显示第一个视图控制器的视图,这时第二个视图控制器的视图不需要显示,只有当用户点击了第二个才会显示相应的视图。为了实现视图延迟加载,在 initWithNibName:bundle: 中不应该访问 view 或 view 的任何子视图。凡是和 view 或 view 的子视图相关的初始化代码,都应该在  viewDidLoad

  访问视图

  通常情况下,在用户看到 XIB 文件中创建的视图之前,需要对他们做一些额外的初始化工作,但是,关于视图的初始化代码不能写在视图控制器的初始化方法中(此时视图控制器并没有加载 NIB 文件,所有指向视图的属性都是nil)。如果向这些属性发送消息,虽然编译的时候不会报错,但是运行的时候无法对这些属性做任何操作。

viewDidLoad ,该方法会在视图控制器加载完 NIB 文件之后调用,此时视图控制器中所有视图属性都已经指向了正确的视图对象。第二个方法是  viewWillAppear:

viewDidLoad 如果用户每次看到视图控制器的 view 都需要对其进行设置就应该选择  viewWillAppear

  

  • 与视图控制器及其视图进行交互

application: didFinishLaunchingWithOptions:

initWithNibName:bundle: 该方法是 UIViewController 的指定初始化方法,创建视图控制器时,就会调用该方法。

loadView

viewDidLoad 可以覆盖该方法,设置使用 NIB 文件创建的视图对象。该方法会在视图控制器加载完视图后被调用。

viewWillAppear: 可以覆盖该方法,设置使用 NIB 文件创建的视图对象。该方法和  viewDidAppear: