《View Controller Programming Guide for iOS》笔记

View Controller Programming Guide for iOS 零散地看过几次,这次将主要感兴趣内容记录下来,融入了个人理解,并且对原文结构微调了一下。

简写:
vc = viewcontroller
app = application
delegate

vc的角色

vc最重要角色是管理所有view,每个vc有个根view(root view),所有其他view都在根view上。vc强引用root view,rootview强引用其他子view。

vc的角色

从角色上分,vc分为container和content两种类型,后者只显示自己的view,后者管理自己的view之外,还管理它的每个子vc上的root views(但不管理子vc上其他view)的尺寸位置。uikit提供的UINavigationController,UISplitViewController,UIPageViewController三种vc都是典型的前者,例如UINavigationController包含一个子vc和navigation bar、 optional toolbar,UINavigationController负责展示子vc的view和两个bar。

container

vc作为数据和view之间的中转,应该做好自己的角色定位,不做数据的处理,数据应该由自己处理相应的逻辑。

每个window有个root vc,vc的内容充满window。如果是自己建立的window,需要设置一下root vc。

presented vc通常用于展示与之前vc内容逻辑上不成一体的内容。Presenting一个vc将会用新视图替换原来的vc的内容(通常隐藏之前vc内容),例如present一个vc用于输入内容。当present一个vc时,UIKit会自动创建好presented vc和presenting vc的链。当container vc被需要时,UIKit可能会改变之前的链以简化代码,例如下图:导航vc上已经存在一个子vc,当presente一个全屏vc时,不需要子vc上面present,而是通过导航vc直接present,因为导航vc本身就是铺满屏幕的vc。
When you present a view controller, UIKit looks for a view controller that provides a suitable context for the presentation. In many cases, UIKit chooses the nearest container view controller but it might also choose the window’s root view controller. In some cases, you can also tell UIKit which view controller defines the presentation context and should handle the presentation.

系统根据上下文选择可以present的父vc--通常是最近的container vc或者rootvc。

presente

container vc添加子vc:其中addChildViewController会自动调用子vc的代理方法willMoveToParentViewController,然后需要调整子vc的根view的frame保证其正确显示,然后将其加入到container vc的root view上,最后调用didMoveToParentViewController方法通知子vc,因为这个不会自动调用。

- (void)displayContentController:(UIViewController*)content
{
    [selfaddChildViewController:content];
    content.view.frame=[selfframeForContentController];
    [self.viewaddSubview:self.currentClientView];
    [contentdidMoveToParentViewController:self];
}

container vc移除子vc:移除container与子vc的关系,其中removeFromParentViewController将调用代码方法didMoveToParentViewController:通知子vc。

- (void)hideContentController:(UIViewController*)content
{
    [contentwillMoveToParentViewController:nil];
    [content.viewremoveFromSuperview];
    [contentremoveFromParentViewController];
}

当你想要动画化子vc之间的互换,你可以将子vc的添加和移除操作放到过渡动画的过程中来模拟。

- (void)cycleFromViewController:(UIViewController*)oldVC
toViewController:(UIViewController*)newVC
{
    // Prepare the two view controllers for the change. 让子vc知道即将移除
    [oldVCwillMoveToParentViewController:nil];
    [selfaddChildViewController:newVC];
    // Get the start frame of the new view controller and the end frame
    // for the old view controller. Both rectangles are offscreen.
    newVC.view.frame=[selfnewViewStartFrame];
    CGRectendFrame=[selfoldViewEndFrame];
    // Queue up the transition animation.transitionFromViewController自动更新vc中view的层次
    [selftransitionFromViewController:oldVCtoViewController:newVC duration:0.25options:0 animations:^{
    // Animate the views to their final positions.新vc view入位,老vc的view移除
        newVC.view.frame=oldVC.view.frame;
        oldVC.view.frame=endFrame;
    }
    completion:^(BOOLfinished){
    // Remove the old view controller and send the final
    // notification to the new view controller. 移除老vc,通知新vc已添加
        [oldVCremoveFromParentViewController];
        [newVCdidMoveToParentViewController:self];
    }];
}

管理子vc的出现进程:添加子vc到container上后,container自动将相关消息转发给子vc。但如果多个子vc并发改变view的状态,相应的事件并非按照逻辑顺序。处理这种情况,可以实现代码里方法返回no,让系统不要自动转发这些消息:

- (BOOL)shouldAutomaticallyForwardAppearanceMethods
{
    returnNO;
}

系统不自动转发,我们就需要自己来通知子vc:

- (void)viewWillAppear:(BOOL)animated
{
    [self.childbeginAppearanceTransition:YESanimated:animated];
}
- (void)viewDidAppear:(BOOL)animated
{
    [self.childendAppearanceTransition];
}
- (void)viewWillDisappear:(BOOL)animated
{
    [self.childbeginAppearanceTransition:NOanimated:animated];
}
- (void)viewDidDisappear:(BOOL)animated
{
    [self.childendAppearanceTransition];
}

container需要注意只访问子vc的root view,不要干涉其他view; container定义一些协议方法,让子vc通过这些方法来与container沟通,以保证子vc对container干涉最小。

vc的可理解性支持

可理解性支持:一个好的app需要充分考虑弱视等有部分身体缺陷用户的正常使用,需要考虑在合适位置安排VoiceOver(系统提供的屏幕阅读辅助工具),暂略。

vc的保存和恢复

vc的保存和恢复需要:

  1. 需要保存的vc赋值一个恢复id
  2. 告知系统在app启动时如何恢复创建和定位新vc
    赋值id:标记vc:对vc的restorationIdentifier属性赋值一个字符串,注意所有父vc都需要赋值。在app启动时,系统会从window的root vc开始遍历vc的层次,逐个恢复,如果某个vc没有id,它和它所有子vc将无法恢复。 如果系统无法自动创建恢复vc,将会通知app创建,并提供所有相关的父与子restorationIdentifier,其实对应的就是一个重建路径。所以restorationIdentifier最好是唯一的并且可辨识,可以为类名。如果想排除一组vc,只需要将父vc的restorationIdentifier设置nil。
    注意:排除vc自动保存后,app启动后如果vc在相关的恢复过程中(vc与要恢复的其他vc有关),这个vc也会被app重建,只是以默认的配置重建。
    对于vc上view如果想保存状态,也需要赋值restorationIdentifier,如果是table或collection需要遵循UIDataSourceModelAssociation协议。

app启动时,系统按下面顺序呢恢复vc:

  1. 如果vc有恢复类,使用这个类调用viewControllerWithRestorationIdentifierPath:coder:来恢复vc;
  2. 否则使用app delegate恢复vc:调用application:viewControllerWithRestorationIdentifierPath:coder:
  3. 如果存在重建路径,则通过这个路径重建。
  4. 如果是storyboard,通过保存的storyboard新来重建

present vc

present过程是UIKit内建的机制,只需要少量代码即可实现present一个vc。我们也可以自己创建定制化的present和动画过程。有两个方法可以present一个vc:
(1)showViewController:sender:showDetailViewController:sender:提供最自适应的方式展示vc,待展示的vc可以决定如果处理present,例如一个container vc可能以将要present的vc加到自己上成为子vc的形式展示,而不是直接present。
(2)presentViewController:animated:completion:以modal形式

注意当一个vcA调用上面两个方法去present vcB时,并非vcA并不一定是最终依托去展示的底部vc,例如全屏模式, UIKit会自动找到可以依托的vc去展示,并更新受影响的vc的presented vc 和presenting vc。有几种present样式供选择,通过设置modalPresentationStyle属性实现:

1.全屏

present时会覆盖整个屏幕,并使present的vc下面的界面不接受交互。全屏包含三种样式:
其中只有UIModalPresentationFullScreen完全遮住底部vc,其他都会漏出一部分。使用UIModalPresentationFullScreen去present时,UIKit自动移除了底部vc的所有view,我们可以通过设置UIModalPresentationOverFullScreen阻止这个行为。使用全屏模式时,如果带presetnt的vc无法覆盖全屏,UIKit会自动沿着vc层次找到可以覆盖全屏的底部vc来展示这个vc,一直找到window的root vc为止。

全屏present模式
2.popover:

UIModalPresentationPopover通过popover形式展示vc,常用语展示额外的相对于选择或聚焦的view上面的列表或者条目形式信息。在水平方向宽松的下图形式时,popover的view只占用屏幕一小部分,而在水平紧凑时,popover会默认调整到UIModalPresentationOverFullScreen形式。点击popover外面的view会dismiss掉这个vc,所以水平紧凑时,需要处理如果dismiss vc,例如可以加个按钮。
popover时需要设置preferredContentSizebarButtonItem和指定区域的sourceViewsourceRect属性。

popover
3.当前上下文

UIModalPresentationCurrentContext模式会遮挡app界面上某个特定的view,使用这个模式时,需要对这个覆盖的vc设置definesPresentationContext属性为yes:

UIModalPresentationCurrentContext
4.自定义

UIModalPresentationCustom模式可以自定义样式:需要子类化UIPresentationController,并实现相关几个方法。
过渡类型:
过渡类型即present过程中的动画类型,例如modalTransitionStyle=UIModalTransitionStyleCoverVertical对应的通话如下:
滑动上去显示,滑动下来消失:
自定义动画需要实现动画类和过渡的代理方法:

过渡

Dismiss

在presented或者presenting vc上都可以调用dismissViewControllerAnimated:completion:方法来隐藏present出来的vc,只是在presented vc上调用时,UIKit会自动转发消息个presenting vc去实现隐藏。

Segue

storyboard直接拉线起点必须是相应用户操作的view或者手势等类型,可以设置segue的显示类型,如push、modal、popover等。过程如下:

流程

其中shouldPerformSegueWithIdentifier:sender:给用户判断是否要跳转到对应界面;prepareForSegue:sender:负责传递数据。
当vc需要dismiss时,只需把segue的线连接到 storyboard上面的 exit。
自定义segue:
子类化UIStoryboardSegue,实现initWithIdentifier:source:destination:

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,324评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,303评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,192评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,555评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,569评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,566评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,927评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,583评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,827评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,590评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,669评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,365评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,941评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,928评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,159评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,880评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,399评论 2 342

推荐阅读更多精彩内容