74
视图控制器(View Controllers范圣刚,[email protected] , www.tfan.org

07 View Controllers

  • Upload
    tom-fan

  • View
    390

  • Download
    3

Embed Size (px)

DESCRIPTION

iOS 视图控制器

Citation preview

Page 1: 07 View Controllers

视图控制器(View Controllers)

范圣刚,[email protected], www.tfan.org

Page 2: 07 View Controllers

Quiz 和 QuizViewController 回顾•在第⼀一个 session 的 Quiz 应⽤用程序中,我们把所有的代码都写在 QuizViewController 类中。这个类的⼀一个实例是 Quiz 应⽤用的控制器

•它有指向屏幕上的 labels 的指针,指向按钮的指针,这些按钮被按下时会给它发送消息。还有指向组成应⽤用数据的模型对象的指针

•对于界⾯面只有⼀一屏的 Quiz 应⽤用,只有⼀一个 controller 对象就⾜足够了

•⼤大多数应⽤用具有不⽌止⼀一个屏幕的界⾯面,⽽而且有⾮非常多的对象需要进⾏行管理,设计 iOS 应⽤用时 好针对每⼀一屏都有⼀一个 controller

Page 3: 07 View Controllers

UIViewController

Page 4: 07 View Controllers

•⼀一个 UIViewController 实例专注在控制应⽤用程序内的⼀一个单独的屏幕

•每个 UIViewController 具有⼀一个指向 UIView 或者 UIView ⼦子类的实例的 view 属性,这个 view 就是它的 screen

•通常情况下,这个 view 是⼀一个全屏视图。⽽而且更多的时候,这个 view 也具有 subviews。这样⼀一个 view controller 控制的就是⼀一个 view hierarchy

•因为这个 view 属性是整个 hierarchy 的 root,当 view controller 的 view 被作为⼀一个subview 添加到 window 时,整个 view hierarchy 就被添加了

Page 5: 07 View Controllers

创建 HypoTime

从 Empty Application 模版创建⼀一个新的 iOS 项⺫⽬目,命名为 HypnoTime。按右边的图进⾏行配置:

Page 6: 07 View Controllers

两个 UIViewController 的⼦子类•这个应⽤用我们将创建两个 UIViewController 的⼦子类•HypnosisViewController

• TimeViewController

•每个 view controller 将会控制⼀一个 view,⽤用户将能够在这些 views 之间切换,取决于他们是否想被催眠或者只是想知道现在是⼏几点

• view 之间的切换将会由另外⼀一个类 UITabBarController 来处理

Page 7: 07 View Controllers

⼦子类化 UIViewController

•我们不会直接创建 UIViewController 的实例,⽽而是创建 UIViewController 的⼦子类来实例化

•⽣生成 HypnosisViewController(File -> New File -> iOS -> Cocoa Touch -> Objective-C class)

Page 8: 07 View Controllers

HypnosisViewController

• view controller 负责创建它的 view hierarchy

•HypnosisViewController 的 view hierarchy 将仅由⼀一个 view 组成。就是我们前⾯面创建的 UIView 的⼦子类 HypnosisView

•在 Finder ⾥里定位到 HypnosisView.h 和 HypnosisView.m ⽂文件,把他们拖到 HypnoTime 项⺫⽬目导航器中

•在出现的窗⼝口中,勾选“Copy items into destination group’s folder”

•同时勾选把它们添加到 target - “HypnoTime”

Page 9: 07 View Controllers

添加 HypnosisView 到 HypnoTime

Page 10: 07 View Controllers

loadView

•UIViewController ⼦子类通过重写 loadView ⽅方法来创建它的 view hierarchy

•这个⽅方法创建⼀一个 view 的实例,并且把它设成 view controller 的 view

•在 HypnosisViewController.m 中重写 loadView,确保在⽂文件顶部导⼊入了 HypnosisView 的头⽂文件

// 重写 loadView- (void)loadView{ // 创建⼀一个 view CGRect frame = [[UIScreen mainScreen] bounds]; HypnosisView *view = [[HypnosisView alloc] initWithFrame:frame]; // 把它设成 view controller 的 view [self setView:view];}

Page 11: 07 View Controllers

重写 loadView (代码)

#import "HypnoViewController.h"// 导⼊入 HypnosisView 头⽂文件#import "HypnosisView.h"

@interface HypnoViewController ()

@end

@implementation HypnoViewController

// 重写 loadView- (void)loadView{ // 创建⼀一个 view CGRect frame = [[UIScreen mainScreen] bounds]; HypnosisView *view = [[HypnosisView alloc] initWithFrame:frame]; // 把它设成 view controller 的 view [self setView:view];}

Page 12: 07 View Controllers

rootViewController

•在 HypnoAppDelegate.m 中,创建⼀一个 HypnosisViewController 的实例并且把它设成 UIWindow 的 rootViewController

•确保在⽂文件顶部导⼊入了 HypnosisViewController.h 头⽂文件

Page 13: 07 View Controllers

设置 rootViewController(代码)

#import "HypnoAppDelegate.h"// 导⼊入 HypnosisViewController 的头⽂文件#import "HypnoViewController.h"

@implementation HypnoAppDelegate

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions{ self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; // Override point for customization after application launch. // 实例化⼀一个 HypnosisViewController 的实例,并设置成 window 的 rootViewController HypnoViewController *hvc = [[HypnoViewController alloc] init]; [[self window] setRootViewController:hvc]; self.window.backgroundColor = [UIColor whiteColor]; [self.window makeKeyAndVisible]; return YES;}

Page 14: 07 View Controllers

HypnosisViewController 的 HypnosisView

Page 15: 07 View Controllers

应⽤用的对象⽰示意图•把⼀一个 view controller 设成 window 的 rootViewController 就把这个 view controller 的 view 添加成了 window 的 subview

•同时把 view 的⼤大⼩小⾃自动调整成和 window 同样⼤大⼩小

Page 16: 07 View Controllers

setRootViewController

// UIWindow setRootViewController- (void)setRootViewController:(UIViewController *)vc{ UIView *rootView = [vc view]; CGRect viewFrame = [self bounds]; [rootView setFrame:viewFrame]; [self addSubview:rootView];}

•如果我们⾃自⼰己写 UIWindow 的 setRootViewController, ⼤大概是下⾯面这样:

Page 17: 07 View Controllers

第⼆二个 UIViewController

•HypnoTime 的第⼆二个 UIViewController 的⼦子类• TimeViewController

• TimeViewController 的 screen 将会显⽰示⼀一个 UILabel,和⼀一个把 label 更新到当前时间的 UIButton

•这个 UIView 将会具有两个 subview:按钮和标签#import <UIKit/UIKit.h>

@interface TimeViewController : UIViewController

@end

Page 18: 07 View Controllers

TimeViewController 的 view hierarchy

Page 19: 07 View Controllers

编程或者从 XIB 创建 view

•创建 HypnosisViewController 的 view hierarchy 时,我们通过编程来实现的:在 loadView 中,实例化了⼀一个 HypnosisView, 然后把它设置成 view (view controller 的 view 属性)。这个 view 没有任何的 subview

• TimeViewController 的 view 将会有两个 subview。当⼀一个 view controller 的 view 具有 subview 的时候,好在 XIB ⽂文件中创建和加载它的 view hierarchy,⽽而不是重写 loadView

•程序运⾏行起来以后,通过编程或者从 XIB ⽂文件创建的 view 没有什么区别,只是 XIB ⽂文件更易于放置多个 view objects

Page 20: 07 View Controllers

TimeViewController.xib

• File -> New -> File, iOS, User Interface, Empty 模版

•弹出窗⼝口的 Device Family 选“iPhone”

•命名为 TimeViewController

•命名假设的重要性•在 project navigator 中选中这个⽂文件以使其在

editor area 显⽰示

Page 21: 07 View Controllers

File’s Owner

• XIB ⽂文件是如何⼯工作的的?

• XIB ⽂文件包含的是对象:把对象拖到画布上就把对象保存到 XIB ⽂文件了。当 XIB ⽂文件被加载的时候,这些对象被加载回进内存中

Page 22: 07 View Controllers

TimeViewController 的 XIB ⽂文件•在

TimeViewController.xib 中,拖拽⼀一个 UIView 到画布上,然后拖拽⼀一个 UIButton 和⼀一个 UILabel 到 view 上

•给按钮⼀一个标题:What time is it?

•给标签⽂文本: ???, 然后让它居中

Page 23: 07 View Controllers

TimeViewController.xib 中的 Objects

•我们可以看到这些对象都显⽰示在 Objects 段下⾯面

•显⽰示在 Objects 段下⾯面的对象都是被保存到 XIB ⽂文件中的对象

•这种类型的存储有⼀一个专有名词叫做:archiving,这些对象也就被称作 archived objects

Page 24: 07 View Controllers

placeholder 对象• XIB ⽂文件中还有另外⼀一种类型的对象:placeholder objects

• Placeholder 区段下⾯面有两个对象:

• File’s Owner(重要!)

• First Responder

Page 25: 07 View Controllers

File’s Owner

•为什么需要 File’s Owner?

•当 view controller 加载它的 view 的时候,它会设置 view 属性,这样它就知道它的 view 是什么然后可以把它放到屏幕上

•对于 HypnosisViewController,我们通过编程来做这些,这些操作在 loadView 中实现并且所有的设置都是在编译期间完成的

• TimeViewController 并⾮非如此,当⼀一个 TimeViewController 的实例需要加载 view 时,它将会加载 XIB ⽂文件,这时 XIB ⽂文件中的所有的 archived objects 都会被创建,然⽽而 TimeViewController 并不知道这些对象中的哪⼀一个是它的 view

Page 26: 07 View Controllers

作为 hole 的 File’s Owner

• File’s Owner 是 XIB ⽂文件的⼀一个 hole

•在配置界⾯面的时候,我们在 XIB ⽂文件中的对象和 File’s Owner 之间建⽴立连接

•当 XIB ⽂文件被加载时,TimeViewController 把它⾃自⼰己置⼊入 File’s Owner 的 hole,然后所有的在 File’s Owner 和 archived objects 之间的连接都成了到 TimeViewController 的

•为了能够设置 TimeViewController 需要的连接,我们必须告诉 Xcode TimeViewController 是要把⾃自⼰己放到这个 hole ⾥里的对象的类

Page 27: 07 View Controllers

Identity inspector for File’s Owner

•在 outline view 选中 File’s Owner 对象,然后点击 inspector 区域的“Identity inspector”,把 Custom Class 下⾯面的 class 改成 “TimeViewController”

Page 28: 07 View Controllers

view outlet

• Control-click File’s Owner 弹出可⽤用连接的⾯面板,我们指定的类是⼀一个 UIViewController 的⼦子类,我们就被提供了⼀一个 view outlet

•把 view outlet 连到 XIB ⽂文件的 UIView 对象

•现在当 TimeViewController 加载 XIB ⽂文件时,就具有了适当的连接,并且可以加载它的 view 了

Page 29: 07 View Controllers

File’s Owner

Page 30: 07 View Controllers

outlet

•这样在 XIB ⽂文件中的⼀一个 outlet 连接就等同于给outlet 对应的对象发送⼀一个基于 outlet 名称的 setter 消息

•例如,当 XIB ⽂文件被加载时,把⼀一个对象设置成 view outlet,将会发送⼀一个 setView: 消息给那个对象,这个⽅方法的参数是连接那头的对象

Page 31: 07 View Controllers

TimeViewController 作为 rootViewController

•打开 HypnoAppDelegate.m, 创建⼀一个 TimeViewController 的实例,把它设成 window 的 rootViewController。确保导⼊入 TimeViewController.h 头⽂文件

•构建并运⾏行,我们可以看到在 TimeViewController.xib 中创建的 TimeViewController 的 view

Page 32: 07 View Controllers

TimeViewController 作为 rootViewController

#import "HypnoAppDelegate.h"// 导⼊入 TimeViewController 头⽂文件#import "TimeViewController.h"

@implementation HypnoAppDelegate

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions{ self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; TimeViewController *tvc = [[TimeViewController alloc] init]; [[self window] setRootViewController:tvc]; self.window.backgroundColor = [UIColor whiteColor]; [self.window makeKeyAndVisible]; return YES;}

Page 33: 07 View Controllers

outlet 和 method

• TimeViewController 在控制这个 view hierarchy,TimeViewController 负责在按钮被按下时更新 label,所以TimeViewController 要作为 UIButton 的 target,并且要具有⼀一个指向 UILabel 的指针

•在 TimeViewController.h 中声明⼀一个 outlet 和⼀一个⽅方法

#import <UIKit/UIKit.h>

@interface TimeViewController : UIViewController{ IBOutlet UILabel *timeLabel;}

- (IBAction)showCurrentTime:(id)sender;@end

Page 34: 07 View Controllers

建⽴立连接•打开 TimeViewController.xib,把 File’s Owner 的

timeLabel outlet 连接到 UILabel

•然后连接 UIButton 到 File’s Owner,并且选择 showCurrentTime:

•这些连接如下⼀一⻚页的图所⽰示:

Page 35: 07 View Controllers

TimeViewController

Page 36: 07 View Controllers

在 TimeViewController.m 中实现 action ⽅方法

- (IBAction)showCurrentTime:(id)sender{ NSDate *now = [NSDate date]; NSDateFormatter *formatter = [[NSDateFormatter alloc] init]; [formatter setTimeStyle:NSDateFormatterMediumStyle]; [timeLabel setText:[formatter stringFromDate:now]];}

Page 37: 07 View Controllers

现在应⽤用的对象⽰示意图

Page 38: 07 View Controllers

回顾:view controller 和 加载 view

• view controller 控制⼀一个 screen,这个 screen 就是⼀一个 view

•这个 view controller 的 view,⼜又将具有 subviews,构成了⼀一个 view hierarchy

•⼀一个 view controller 可以通过两种途径得到它的 view:• 其 view 没有任何 subviews 的 view controller 可以通过重写

loadView: 来创建⼀一个 view 实例,然后给⾃自⼰己发送 setView:

• 具有⾮非常复杂 view hierarchy 的 view controller 可以通过把它的 view outlet 连接到 XIB 中 archived 的顶层 view 的⽅方式从⼀一个 XIB ⽂文件中加载它的 view

Page 39: 07 View Controllers

UITabBarController

Page 40: 07 View Controllers

• view controller 可以根据⽤用户的动作呈现另⼀一个 view controller 出来

•UITabBarController 就是这类 view controllers 之⼀一,我们将使⽤用⼀一个 UITabBarController 在 HypnosisViewController 和 TimeViewController 的实例之间切换

•UITabBarController 持有⼀一个 UIViewController 的数组,它也在屏幕底部维护了⼀一个标签栏(tab bar),其数组中的每⼀一个 view controller 都有⼀一个标签

•点击标签的结果就是和这个标签关联的 view controller 的 view 被呈现出来

Page 41: 07 View Controllers

创建⼀一个 UITabBarController

•在 HypnoAppDelegate.m 中,创建⼀一个 UITabBarController 的实例,把两个 view controller 都给它,然后把它设置成 window 的 rootViewController

Page 42: 07 View Controllers

创建 UITabBarController(代码)

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions{ self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; // Override point for customization after application launch. HypnoViewController *hvc = [[HypnoViewController alloc] init]; TimeViewController *tvc = [[TimeViewController alloc] init];

UITabBarController *tabBarController = [[UITabBarController alloc] init]; NSArray *viewControllers = [NSArray arrayWithObjects:hvc, tvc, nil]; [tabBarController setViewControllers:viewControllers]; [[self window] setRootViewController:tabBarController]; self.window.backgroundColor = [UIColor whiteColor]; [self.window makeKeyAndVisible]; return YES;}

Page 43: 07 View Controllers

点击底部的⿊黑⾊色标签可以切换 view controller

Page 44: 07 View Controllers

UITabBarController 的 view

•UITabBarController 是 window 的 rootViewController。这意味着 UITabBarController 本⾝身是 UIViewController的⼀一个⼦子类

•那么它就具有 view 属性,它的 view 是⼀一个具有两个 subviews 的空⽩白 UIView

• tab bar 标签栏

•选中的 view controller 的 view

Page 45: 07 View Controllers

UITabBarController ⽰示意图

Page 46: 07 View Controllers

tabBarItem 属性和 UITabBarItem

Page 47: 07 View Controllers

•标签栏上的每个标签可以显⽰示⼀一个 title 和⼀一个 image

•为了这个⺫⽬目的,每个 view controller 维护⼀一个 tabBarItem 属性

•当⼀一个 view controller 被⼀一个 UITabBarController 包含时,它的标签栏条⺫⽬目出现在标签栏上

Page 48: 07 View Controllers

UITabBarItem ⽰示例

Page 49: 07 View Controllers

为两个 view controller 设置标题•打开 HypnosisViewController.m, 重写

UIViewController 的 designated initializer,initWithNibName:bundle:, 为 HypnosisViewController 获得然后设置⼀一个 tab bar item

•然后打开 TimeViewController.m, 进⾏行同样的操作

Page 50: 07 View Controllers

设置 UITabBarItem 的代码- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil{ self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]; if (self) { // Custom initialization // 获取 tab bar item UITabBarItem *tbi = [self tabBarItem]; // 给它⼀一个标题 [tbi setTitle:@"Hypnosis"]; } return self;}

- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil{ self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]; if (self) { // Custom initialization UITabBarItem *tbi = [self tabBarItem]; [tbi setTitle:@"Time"]; } return self;}

Page 51: 07 View Controllers

设置了标题的 tabBarItem

Page 52: 07 View Controllers

为每⼀一个标签增加图⽚片•把图⽚片⽂文件Hypno.png, Time.png, [email protected],

[email protected] 拖到项⺫⽬目中,勾选拷⻉贝到项⺫⽬目⺫⽬目录选项

•然后再编辑 initWithNibName:bundle: 的代码

Page 53: 07 View Controllers

设置标签栏图⽚片的代码- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil{ self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]; if (self) { UITabBarItem *tbi = [self tabBarItem]; [tbi setTitle:@"Hypnosis"]; UIImage *image = [UIImage imageNamed:@"Hypno.png"]; [tbi setImage:image]; } return self;}

- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil{ self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]; if (self) { // Custom initialization UITabBarItem *tbi = [self tabBarItem]; [tbi setTitle:@"Time"]; UIImage *image = [UIImage imageNamed:@"Time.png"]; [tbi setImage:image]; } return self;}

Page 54: 07 View Controllers

设置了 image 的标签栏

Page 55: 07 View Controllers

view controller 的⽣生命周期

Page 56: 07 View Controllers

•⼀一个 view controller 当被 allocated, 然后发送⼀一个 initializer 消息后就开始了它的⽣生命周期

•⼀一个 view controller 将会看到它的 view 被创建,移动到屏幕上,移离屏幕,销毁,以及再次创建 - 这个过程也许会有很多次以上

•这些事件构成了 view controller 的⽣生命周期

Page 57: 07 View Controllers

初始化 view controller

•UIViewController 的 designated initializer 是 initWithNibName:bundle: 。这个⽅方法使⽤用两个参数指定了在 bundle ⾥里⾯面的 view controller 的 XIB ⽂文件的名称

•两个参数都传⼊入 nil 表⽰示:当加载你的 view 的时候,在 application bundle 中搜索名称和你的类名相同的 XIB ⽂文件

•例如: TimeViewController 将会加载 TimeViewController.xib ⽂文件

•可以把下⾯面的代码添加到 TimeViewController.m 会理解的更清楚⼀一点:

Page 58: 07 View Controllers

nibNameOrNil

- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil{// self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]; // 获得到 application bundle 对象的指针 NSBundle *appBundle = [NSBundle mainBundle]; self = [super initWithNibName:@"TimeViewController" bundle:appBundle]; if (self) { // Custom initialization UITabBarItem *tbi = [self tabBarItem]; [tbi setTitle:@"Time"]; UIImage *image = [UIImage imageNamed:@"Time.png"]; [tbi setImage:image]; } return self;}

Page 59: 07 View Controllers

init

•实际开发过程中,我们⼀一般都使⽤用 UIViewController ⼦子类的名称作为 XIB ⽂文件的名称

•因此,当创建⼀一个 view controller 时,我们只需要发送 init ,等同于给 initWithNibName:bundle: 的两个参数都传⼊入了 nil

•需要的话可以通过重写 initWithNibName:bundle: 来执⾏行⼀一些额外的初始化⼯工作

TimeViewController *tvc = [[TimeViewController alloc] init]; // 等同于 TimeViewController *tvc = [[TimeViewController alloc] initWithNibName:nil bundle:nil];

Page 60: 07 View Controllers

UIViewController 和延迟加载•当⼀一个 view controller 被实例化的时候,它不会⽴立即创建和加载它的 view

•只有当 view 将要在屏幕上显⽰示的时候,⼀一个 view controller 才会特意创建它的 view

•通过在只有需要的时候才加载视图,应⽤用程序不会在它不需要的时候占⽤用内存

Page 61: 07 View Controllers

viewDidLoad

•所有的 UIViewController 都实现了⼀一个 viewDidLoad ⽅方法,当它加载了它的 view 以后就会⽴立即执⾏行

•我们在我们的两个 view controller ⾥里⾯面重写这个⽅方法在控制台记录⼀一些消息

•构建并运⾏行,可以看到控制台报告 HypnosisViewController ⽴立即加载了它的 view;点击 TimeViewController 的标签以后,控制台才报告 view 现在被加载- (void)viewDidLoad{ [super viewDidLoad]; NSLog(@"TimeViewController 加载了它的 view");}

Page 62: 07 View Controllers

Appear 和 Disappear

•除了加载和卸载以外,view controller 也会在特定的时间出现或者消失

• view controller 只会被创建⼀一次,但是它的 view 通常会显⽰示(释放或者隐藏)多次

•我们可以在这些⽣生命周期事件发⽣生时做⼀一些⾃自定义的操作,⽐比如我们可以让 TimeViewController 的 view 每次显⽰示时,可以显⽰示 新时间

- (void)viewWillAppear:(BOOL)animated; // Called when the view is about to made visible. Default does nothing- (void)viewDidAppear:(BOOL)animated; // Called when the view has been fully transitioned onto the screen. Default does nothing- (void)viewWillDisappear:(BOOL)animated; // Called when the view is dismissed, covered or otherwise hidden. Default does nothing- (void)viewDidDisappear:(BOOL)animated; // Called after the view was dismissed, covered or otherwise hidden. Default does nothing

Page 63: 07 View Controllers

重写 viewWillAppear

- (void)viewWillAppear:(BOOL)animated{ NSLog(@"CurrentTimeViewController will appear"); [super viewWillAppear:animated]; [self showCurrentTime:nil];}- (void)viewWillDisappear:(BOOL)animated{ NSLog(@"CurrentTimeViewController will disappear"); [super viewWillDisappear:animated];}

Page 64: 07 View Controllers

view controller 中 view 的⽣生命周期

Page 65: 07 View Controllers

视图控制器的⼦子类和模版•实际开发过程中,我们可以在使⽤用

UIViewController 模版的同时勾选上⽣生成相应的 XIB ⽂文件

•这样 XIB ⽂文件的 File’s Owner 已经被设置成 UIViewController 的类,同时 UIView 的实例已经被挂接到 File’s Owner 的 view outlet

Page 66: 07 View Controllers

练习1:再加⼀一个 Tab

•创建⼀一个新的 UIViewController ⼦子类,它的 view 应该是 MKMapView 的⼀一个实例

•使这个 view controller 成为 UITabBarController 的第三个 view controller

Page 67: 07 View Controllers

练习2:Controller Logic

•增加⼀一个 UISegmentedControl 到 HyposisViewController 的 view,带着三个 segments:Red, Green 和 Blue

•当⽤用户 tap segmented control 时,改变 HyposisView 中 circles 的颜⾊色

Page 68: 07 View Controllers

补充阅读:main 函数和 UIApplication

• C 应⽤用程序都是从执⾏行⼀一个 main 函数开始,Objective-C 应⽤用程序也是⼀一样的

int main(int argc, char *argv[]){ @autoreleasepool { return UIApplicationMain(argc, argv, nil, NSStringFromClass([HypnoAppDelegate class])); }}

•UIApplicationMain 函数⽣生成了⼀一个 UIApplication 类的实例;对于每⼀一个应⽤用来说,都有⼀一个单独的 UIApplication 实例,⽤用于维护 run loop

•UIApplicationMain 还⽣生成⼀一个 作为 UIApplication 的 delegate 类的实例。 后⼀一个参数就是 delegate 类的名称

Page 69: 07 View Controllers

application:didFinishLaunchingWithOptions:

•在 run loop 将要开始接收事件前,application 会发送给它的 delegate ⼀一个消息,告诉它“ 准备好了,我们要开始啦!”,这个消息就是 application:didFinishLaunchingWithOptions:

•我们在 HypnosisAppDelegate.m 中实现这个⽅方法来创建在应⽤用程序中使⽤用的 window 和 controller 对象

•所有的 iOS 应⽤用程序都遵循这种模式

Page 70: 07 View Controllers

补充阅读2:视⺴⽹网膜屏(Retina) 显⽰示

• Retina 和早期设备的 320x480 像素⽐比较⽽而⾔言具有更⾼高的分辨率(640x960)

•如何才能让图形在两种显⽰示上都能得到 佳显⽰示?

•对于⽮矢量图形,像 HypnosisView 的 drawRect: ⽅方法和绘制⽂文本,同样的代码会渲染的和设备允许的⼀一样清晰

•如果使⽤用 Core Graphics 函数绘制,这些图形在不同的设备上会有不同的表现

•在 Core Graphics 中,也被称作 Quarz,我们使⽤用名词 points 描述 lines(直线),curves(曲线),text(⽂文本) 等。在⾮非 Retina 显⽰示上,⼀一个 point 是1x1像素;在 Retina 显⽰示上,⼀一个 point 是 2x2 像素

Page 71: 07 View Controllers

不同分辨率的渲染

Page 72: 07 View Controllers

图像拉伸•位图(像 JPEG 或 PNG ⽂文件),如果图像没有适合设备的屏幕类型,将会变得没有吸引⼒力

•假如你的应⽤用包含了⼀一个 25x25 像素的⼩小图像,如果它在 Retina 显⽰示器上显⽰示时,图像必须被拉伸来覆盖 50x50 的区域。

•从这⼀一点上来讲,系统使⽤用了⼀一种叫做抗锯齿的平均化的⽅方式来使图像看起来没有锯齿。结果是图像没有锯齿 - 但是模糊

Page 73: 07 View Controllers

拉伸图像后引起的模糊

Page 74: 07 View Controllers

@2x

•可以使⽤用⼤大⽂文件替代,但是图像缩⼩小时⼜又会在另⼀一个⽅方向引起问题

•唯⼀一的⽅方案是在应⽤用程序绑定两个⽂文件:⼀一种是在⾮非 Retina 屏幕上采⽤用分辨率等于 points 数量,另外⼀一种是在 Retina 显⽰示上采⽤用 points 两倍的分辨率

•幸运的是我们不需要写额外的代码来处理在哪种设备上加载哪种图⽚片,我们需要做的就是在⾼高分辨率图⽚片后⾯面加上 @2x 后缀。然后当使⽤用 UIImage 的 imageNamed: ⽅方法来加载图⽚片时,这个⽅方法会在 bundle 中查找,然后拿到适合特定设备的⽂文件