iOS 视图控制器 (内容根据iOS编程编写)

  视图控制器是  UIViewController 类或其子类对象。每个视图控制器都负责管理一个视图层次结构,包括创建视图层级结构中的视图并处理相关用户事件,以及将整个视图层次结构添加到应用窗口。

  创建一个程序,并将上节  JXHypnosisView 类导入到工程中。

  • 创建 UIViewController 子类

  打开工程,创建一个  UIViewController  子类文件,并将其命名为  JXHypnosisViewController

  • UIViewController 的 view 属性

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

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

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

  1. 代码方式:覆盖  UIViewController 的 loadView 方法。

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

  通过代码方法创建视图

  在  JXHypnosisViewController.m 顶部导入头文件  JXHypnosisView.h ,然后覆盖  loadView 方法,创建一个大小与屏幕相同的  JXHypnosisView  对象,并将其赋给视图控制器的 view 属性:

#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 对象会将该视图控制器的view作为子视图家兔窗口,此外,还会自动调整view的大小,将其设置为与窗口的大小相同。

  首先在  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 后立刻加载 view。

  • 另一个视图控制器

  创建另外一个类,命名为  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 方法会自动处理 NIB 文件中包含的视图层次结构。

  接下来在  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.‘

  这是因为 NIB 被加载时,会创建文件中的视图对象,但是这些视图对象在运行时并没有与  JXReminderViewController  关联起来,包括  JXReminderViewController 的 view 属性。当该视图控制器需要将view添加到应用窗口时,view 是 nil ,因此应用程序会崩溃。

  关联 File‘s Owner

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

  因此,如果要在运行时关联加载 NIB 文件的对象,可在 XIB 文件中关联   File‘s Owner 。首先需设置  JXReminderViewController.xib 文件中的   File‘s Owner  是  JXReminderViewController

  接下来在 XIB 文件中添加所需要的关联,,首先是 控制器的view属性,在大纲视图中按住 control 并单击 File‘s Owner ,Xcode 会显示关联面板,列出所有可用的关联。将 view 插座变量与画布中的 UIView 对象关联起来。这样当  JXReminderViewController 对象载入 XIB 文件是,其 view 属性就能自动指向画布中的 UIView 对象。

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

  之前的代码中将  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  属性设置该标签项的标题和图片。

  首先,我们打开 Images.xcassets 这是用来管理项目中所有图片资源的文件,将所需的图片导入。接下来打开  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)。如果向这些属性发送消息,虽然编译的时候不会报错,但是运行的时候无法对这些属性做任何操作。

  那么,我们在应该在哪些方法中访问 XIB 文件中的视图呢?主要包括两个方,可以根据实际需要选择。第一个方法是用于确认视图已经加载的  viewDidLoad ,该方法会在视图控制器加载完 NIB 文件之后调用,此时视图控制器中所有视图属性都已经指向了正确的视图对象。第二个方法是  viewWillAppear: 该方法会在视图控制器的 view 添加到应用窗口之前被调用。

  两个方法的区别是,如果只需要在应用启动后设置一次视图对象,就选择  viewDidLoad 如果用户每次看到视图控制器的 view 都需要对其进行设置就应该选择  viewWillAppear

  

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

   application: didFinishLaunchingWithOptions: 在该方法中设置和初始化应用窗口的根视图控制器。该方法只会在应用启动完毕后调用一次,之后如果从其他应用切换回本应用,则该方法不会再次被调用。

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

   loadView 可以覆盖该方法,使用代码方式设置视图控制器的view属性。

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

   viewWillAppear: 可以覆盖该方法,设置使用 NIB 文件创建的视图对象。该方法和  viewDidAppear: 会在每次视图控制器的 view 显示在屏幕上时被调用。

时间: 2024-10-08 04:14:20

iOS 视图控制器 (内容根据iOS编程编写)的相关文章

iOS视图控制器编程指南 --- 实现一个容器视图控制器

容器视图控制器是一种结合多个视图控制器的内容到一个单一的用户界面上的方式.容器视图控制器经常被用来使导航更方便,基于已经存在的内容创建一个新的用户界面类型.例如,在UIKit中的容器视图控制器包括UINavigationcontroller,UITabBarcontroller 和 UISplitViewcontroller,它们都可以使用户界面在不同视图部分之间的切换和导航更加的容易. 设计一个自定义的容器视图控制器 在几乎所有的方面,一个容器视图控制器就像其它任何一个内容视图控制器一样,它管

iOS UINavigationController(内容根据iOS编程编写)

我们知道 UITabBarController 对象,可以通过使用该对象,用户可以切换不同的屏幕.当要切换的各个屏幕之间没有相互依存关系的时候,该对象可以很好的完成任务.但是当多个屏幕互有关系的时候,就要使用另一种控制器 UINavigationController . 事实上,基本上在所有的 APP 中,必不可少的两种控制器. 我们以 iOS 自带的 设置 应用为例,设置应用拥有多个互有关联的窗口,我们称之为这种界面为 垂直界面 . 下面我们将使用 UINavigationController

iOS视图控制器编程指南 --- 呈现一个视图控制器

有两种方法实现一个视图控制器到屏幕上:把它嵌入到一个容器视图控制器或者是直接呈现它.容器视图控制器提供一个应用程序主要的导航功能,但是present 视图控制器也是一个重要的导航工具.你可以直接使用presentation 在当前视图控制器的最上层显示一个新的视图控制器.典型地,当你想要实现模态界面的时候直接present 视图控制器,但是你也可以基于其它目的使用它们. 对呈现视图控制器的支持内建于类UIViewController中,而且对所有的视图控制器对象都是有效的.你可以在其它任何视图控

自定义视图控制器切换(iOS)

在iOS开发过程中,通常我们会使用UINavigationController,UITabbarController等苹果提供的视图控制器来切换我们的视图.在iOS5之前,如果要自定义容器视图控制器很麻烦,比如你要考虑到子视图的生命周期,当设备旋转时的情况等,好在iOS5中苹果提供了添加视图控制器(addChildViewController)等管理视图控制器的API,这样我们就能使用此API来自定义自己的视图控制器了,这篇文章只要介绍如何使用此API实现UITabbarController的基

iOS 视图控制器转场详解

前言的前言 唐巧前辈在微信公众号「iOSDevTips」以及其博客上推送了我的文章后,我的 Github 各项指标有了大幅度的增长,多谢唐巧前辈的推荐.有些人问我相关的问题,好吧,目前为止就几个,由于没有评论系统,实在不方便交流,但我也没把博客好好整理,一直都在简书上写博客,大家有问题请移步我的简书本文章的页面.关于交流,我想说这么几点: 1.问问题就好,不要加上大神大牛之类的称呼,与本文有关的问题我尽量回答:不负责解析转场动画,看心情回答. 2.去我的简书下留言是最有效的交流方式,要加我好友就

IOS视图控制器的生命周期

原创Blog,转载请注明出处 http://blog.csdn.net/hello_hwc?viewmode=contents 所谓的生命周期,也就是几个函数的调用顺序,这里以用Storyboard来创建一个ViewController为例 然后我们测试如下代码 // // ViewController.m // // Created by huangwenchen on 14/12/26. // Copyright (c) 2014年 huangwenchen. All rights rese

ios – 视图控制器如何管理视图?

移动设备的屏幕有限,所有的东西都需要放到一个单一窗口组成的单一界面显示,在ios中体现为视图切换(在<ios – 视图>中已经说明了视图),当一个视图替换掉另一个视图的时候,会经常使用动画效果,这个任务就是交给视图管理器来完成的. ios5之后应用程序窗口有一个根视图控制器(rootViewController),当不为rootViewController赋值时,会出现"Application windows are expected to have a root view cont

iOS(视图控制器转场)

转场需要提供转场代理,不使用默认的代理则需要自己实现代理方式,有UINavigationController.UITabBarController.UIViewController三种代理,实现以下三种协议<UINavigationControllerDelegate> //push和pop切换 <UITabBarControllerDelegate> //tab切换<UIViewControllerTransitioningDelegate> //UICollecti

iOS,视图控制器相关(UIViewController)

1.视图控制器各个方法调用时机 2.选项卡(Tab Bar)和导航栏(Navigation Bar) 视图控制器各个方法调用时机 init:方法 在init方法中实例化必要的对象(遵从LazyLoad思想) init方法中初始化ViewController本身 loadView:方法 这是当没有正在使用nib视图页面,子类将会创建自己的自定义视图层.绝不能直接调用 viewDidLoad:方法 在视图加载后被调用 viewWillAppear:方法 视图即将可见时调用 viewDidAppear