码迷,mamicode.com
首页 > 移动开发 > 详细

iOS UIKit:viewController之弹出和转换(3)

时间:2016-05-02 21:14:48      阅读:309      评论:0      收藏:0      [点我收藏+]

标签:

弹出和转换view controller技术是一种快速且简单的方式将新view content展示在屏幕中。目前有两种方式弹出新的view controller:program方式和segues方式。

1 Program方式

存在两种方式将新view controller显示在屏幕中:Container 方式和present(弹出)方式。Present方式是由UIViewController类自身支持的,所以任何view controller对象都可用。弹出view controller的过程就是创建原始view controller和新view controller之间的关系,其中将原始view controller称作presenting view controller(主动),而将新的view controller称作presented view controller(被动)。

1.1 样式

1.1.1 Presentation样式

view controller的Presentation(弹出)样式控制着view controller出现在屏幕的行为,UIKit定义了很多种弹出样式,用户也可以自定义。若需改变默认的弹出样式,可修改新view controller UIViewController对象)的modalPresentationStyle属性即可。

  1. Full-Screen样式

Full screen样式会覆盖整个屏幕,从而阻止与底层视图内容进行交互。其中在屏幕处于水平环境下,只有一种Full screen样式会覆盖整个屏幕,其它样式只是覆盖了底层view controller的部分内容,而没有覆盖的部分则处于模糊状态。

如图 31所示,是在horizontally regular环境下使用UIModalPresentationFullScreen,UIModalPresentationPageSheet,和UIModalPresentationFormSheet样式的例子。其中左上绿色的view controller弹出了右上蓝色的view controller,然后有最底下三种样式的不同效果。

技术分享

图 31 The full screen presentation styles

注意:

????当使用了UIModalPresentationFullScreen样式弹出一个view cotroller时,UIKit正常会移除底层的view cotroller,当然可以将其改为UIModalPresentationOverFullScreen样式,从而阻止移除操作。当新弹出的view controller有透明的部分,就应该使用UIModalPresentationOverFullScreen样式。

?

  1. Popover样式

以Popover样式弹出view controller是使用了UIModalPresentationPopover属性值。在horizontally regular环境下,popover view只是覆盖了部分屏幕,如图 32所示。在horizontally compact环境下,popover默认采用UIModalPresentationOverFullScreen样式。若在弹出view controller之后,触摸popover view以外部分,则被弹出view controller将自动消失。

技术分享

图 32 The popover presentation style

????因为在horizontally compact环境下,popover样式是覆盖的整个屏幕,所以需要修改代码来适配。

?

  1. Current Context样式

使用UIModalPresentationCurrentContext样式可以覆盖指定的view controller。当使用这种样式,那么需要设置覆盖 view controller的definesPresentationContext属性为YES。如图 33所示,只覆盖split view controller的其中一个child view cotroller。

技术分享

图 33 The current context presentation style

????当转换到horizontally compact环境时,那么current context样式将采用UIModalPresentationFullScreen样式。

?

  1. Custom样式

当使用自定义的样式,可以设置view controller为UIModalPresentationCustom 样式。当创建自定义样式时,将调用UIPresentationController子类,并且使用某种动画方式展示自定义view到屏幕中,及设置新弹出的view controller的尺寸和位置。

?

1.1.2 Transition样式

Transition样式决定了新view controller的动画方式,可以使用内置的标准Transition样式来设置新view controller的modalTransitionStyle属性。当弹出view controller时,UIKit会创建一个相应动画。如图 34所示,是使用了UIModalTransitionStyleCoverVertical属性值来弹出view controller例子,B view controller从底部向上升起。

技术分享

图 34 A transition animation for a view controller

?

1.1.3 Present与Show

UIViewController类提供两种方式来显示view controller:

  • showViewController:sender和showDetailViewController:sender方法:这两个方法让新的view controller来决定怎样最好的处理present操作;
  • presentViewController:animated:completion方法:该方法总是以模态的形式弹出view controller。

?

1.2 Present

1.2.1 Show

使用showViewController和showDetailViewController方法可以将view controller展示在屏幕中,其操作步骤非常简单:

  1. 创建一个被展示的view controller,并对其进行初始化;
  2. 设置这个被展示view controller的modalPresentationStyle属性
  3. 置这个被展示view controller的modalTransitionStyleproperty属性
  4. 调用当前view controller的showViewControllershowDetailViewController方法

?

如下所示:

- (IBAction)segueDown:(id)sender {

firstViewController *firstVC = [self.storyboard instantiateViewControllerWithIdentifier:@"firstViewController"];

firstVC.modalPresentationStyle = UIModalPresentationFullScreen;

firstVC.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal;

?

[self showViewController:firstVC sender:sender];

}

?

1.2.2 Present(Modally)

当采用Present模型弹出一个view controller时,应该告诉UIKit想弹出的view controller是什么,并且还需告诉以什么动画样式展示。其使用过程为:

  1. 创建一个被展示的view controller,并对其进行初始化;
  2. 设置这个被展示view controller的modalPresentationStyle属性
  3. 置这个被展示view controller的modalTransitionStyleproperty属性
  4. 调用当前view controller的presentViewController:animated:completion:方法

?

如下所示:????

- (IBAction)PresentDown:(id)sender {

firstViewController *firstVC = [self.storyboard instantiateViewControllerWithIdentifier:@"firstViewController"];

firstVC.modalPresentationStyle = UIModalPresentationFullScreen;

firstVC.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal;

?

[self presentViewController:firstVC animated:YES completion:nil];

}

?

1.2.3 Present(Popover)

若是以Popover样式弹出view controller,则需要进行一些配置,因为默认会被配置为UIModalPresentationOverFullScreen样式。其中配置过程为:

  1. 创建一个被展示的view controller,并对其进行初始化。
  2. 设置这个新view controller的modalPresentationStyle属性UIModalPresentationPopover值
  3. 获取新view controller的popoverPresentationController属性,即UIPopoverPresentationController对象。
  4. 设置UIPopoverPresentationController对象中的preferredContentSize属性值,即为弹出view cotroller窗口的显示大小。
  5. 设置弹出的锚点,其中有两种方式:
  • 若是bar button item,则设置UIPopoverPresentationController对象的barButtonItem属性;
  • 若是普通视图,则设置UIPopoverPresentationController对象的sourceView和sourceRect属性。
  1. 设置UIPopoverPresentationController对象的delegate,并且该delegate需要实现两个方法:adaptivePresentationStyleForPresentationController和popoverPresentationControllerShouldDismissPopover。
  2. 调用当前view controller的presentViewController:animated:completion:方法

?

如下是两种不同锚点的设置方法:

  1. 导航工具栏

若是在工具栏按钮中弹出Popover,则设置UIPopoverPresentationController对象的barButtonItem属性为相应工具栏项,该项只是显示箭头显示的起始位置。

- (UIModalPresentationStyle)adaptivePresentationStyleForPresentationController:(UIPresentationController *)controller traitCollection:(UITraitCollection *)traitCollection //实现delegate的两个方法

{

return UIModalPresentationNone;

}

- (BOOL)popoverPresentationControllerShouldDismissPopover:(UIPopoverPresentationController *)popoverPresentationController

{

return YES;

}?

- (IBAction)PopoverDown:(id)sender //该方法为按钮的响应方法。

{

firstViewController *firstVC = [self.storyboard instantiateViewControllerWithIdentifier:@"firstViewController"];

firstVC.modalPresentationStyle = UIModalPresentationPopover;

firstVC.preferredContentSize = CGSizeMake(100, 150); //设置弹出窗口的长宽值,可选

?

UIPopoverPresentationController *popvc = [firstVC popoverPresentationController];

popvc.barButtonItem = _navigation.rightBarButtonItem;

popvc.permittedArrowDirections = UIPopoverArrowDirectionUp; //设置弹出窗口的箭头方向,可选

popvc.delegate = self; //必须设置委托对象

?

[self presentViewController:firstVC animated:YES completion:nil];

}

?

技术分享

图 35 导航工具栏Popover弹出效果图

  1. 按钮

也可将锚点设置在按钮中,其它操作方法相同,也需要实现delegate,如下的delegate与上述一样,如下所示。

- (IBAction)PopoverDown:(id)sender //该方法为按钮的响应方法。

{

firstViewController *firstVC = [self.storyboard instantiateViewControllerWithIdentifier:@"firstViewController"];

firstVC.modalPresentationStyle = UIModalPresentationPopover;

firstVC.modalTransitionStyle = UIModalTransitionStyleCoverVertical;

// firstVC.preferredContentSize = CGSizeMake(100, 150);

?

UIPopoverPresentationController *popvc = [firstVC popoverPresentationController];

popvc.permittedArrowDirections = UIPopoverArrowDirectionDown; //与上述的箭头方向不同

popvc.sourceView = _popoverButton;

popvc.sourceRect = _popoverButton.bounds;

popvc.delegate = self;

?

[self presentViewController:firstVC animated:YES completion:nil];

}

?

技术分享

图 36 按钮位置的Popover的显示效果图

1.3 Dismiss

可以清除一个已经被弹出的view controller,只需调用原来view controller中的dismissViewControllerAnimated:completion方法。当然也可以调用被弹出view controller的dismissViewControllerAnimated:completion方法,一样可以返回到弹出view controller的状态。

?

如下是在被弹出view controller的按钮响应方法中:

- (IBAction)dismissDown:(id)sender

{

[self dismissViewControllerAnimated:YES completion:nil];

?

}

?

?

1.4 Storyboard间

在同一个Storyboard中,可以使用segues来完成不同view controller的弹出操作;但在不同的Storyboard之间是无法使用segues完成弹出操作的。但以Programing方式,是可以实现不同Storyboard间的view controller的弹出操作。

?

如下所示:

  • UIStoryboard* sb = [UIStoryboard storyboardWithName:@"SecondStoryboard" bundle:nil];
  • MyViewController* myVC = [sb instantiateViewControllerWithIdentifier:@"MyViewController"];
  • [self presentViewController:myVC animated:YES completion:nil]; // Display the view controller

?

?

2 Segues方式

可以使用Segues来定义app交互接口,在storyboard中用一个Segues来定义两个view controller之间的转换。Segues的起始点可以是 buttontable row,或 gesture recognizer,而Segues的终止点是相应显示的view controller。可以使用Segues端出(present)一个新的view controller,也可以使用unwind segue来清除view controller。

技术分享

图 37 A segue between two view controllers

开发工程师不需要通过程序来触发segue ,在运行时,UIKit会加载segue相关的view controller并连接相关的元素。当用户点击相关元素时,UIKit会自动加载合适的view controller,并通知app相应的segue 发生触发。

2.1 创建

在同一个storyboard文件中创建view controller之间的segue,是非常简单的。首先按住Control键,同时拖拉第一个view controller到目标view controller,其中segue起始点必须是一个view或者是拥有响应方法的对象,如图 38所示。

技术分享

图 38 Creating the segue relationship

?

当释放鼠标左键时,Interface Builder会弹出一个窗口,让选择两个 view controller之间的segue类型,如图 39所示。其中segue类型如表 31所示。

技术分享

图 39 Selecting the type of segue to create

?

表 31 Adaptive segue types

Segue type?

Behavior?

Show (Push)?

这种类型是使用目标view controller的 showViewController:sender:方法来展示新的内容。对于多数view controller,这种类型会弹出新的modal内容来覆盖原始的view controller;对于少数view controller会重载该方法来实现不同的行为,比如navigation controller会将新的view controller压入栈顶。

Show Detail (Replace)?

这种类型是使用目标view controller的 showDetailViewController:sender: 方法来展示新的内容。这种类型相当是将view controller潜入UISplitViewController 对象中。使用这种类型segue,split view controller 会用一个新view controller替换第二个子view controller。

Present Modally?

这种类型segue以modal形式展示view controller,新的场景完全盖住了旧的那个。用户无法再与上一个场景交互,除非先关闭这个场景。

Present as Popover?

在水平regular环境下,view controller会以popover形式出现;在水平compact环境下,view controller会以full-screen形式显示。

?

在创建segue后,可以在attributes inspector中设置segue对象的identifier。在segue中,可以使用identifier来识别哪个segue被触发,若是在支持多segue的view controller中特别有用。当segue被执行时,identifier会随 UIStoryboardSegue对象传递给view controller。

?

2.2 修改

UIKit提供一些支持,让工程师在segue运行期可修改segue的行为,如图 310所示是当segue触发时发生的动作,多数工作是在原(presenting)view controller中发生,其负责管理新view controller的转换。

技术分享

图 310 Displaying a view controller using a segue

?

在执行segue时,UIKit会调用current view controller的如下方法,从而有机会来影响segue的输出。

  • shouldPerformSegueWithIdentifier:sender方法

该方法给工程师机会来阻止segue的发生,当该方法返回NO时,将导致segue返回失败;但不能阻止其它响应方法的触发。比如,点击table row时仍然触发table相关的delegete方法;

?

  • prepareForSegue:sender方法

该方法属于原view controller的方法,通过这个方法可以将数据从原view controller传递给目标view controller。该方法的 UIStoryboardSegue包含有目标view controller的引用,并且还有其它一些相关信息。

?

2.3 清除

Unwind Segue可以清除已经弹出的view controller。Unwind Segue也是一种segue,只不过它是segue的逆过程。可以通过Interface Builder窗口,创建Unwind Segue,其是在new view controller中的button、或其它合适对象链接到其Exit按钮中。从而当用户触发这个元素时,UIKit会在view controller层次结构中搜索能够处理Unwind Segue的对象;接着自动从new view controller返回到current view controller,并清理new view controller。

创建Unwind Segue的步骤是:可参考网上视频错误! 未找到引用源。

  1. 在原来正向segue的current view controller中声明一个Unwind Segue响应方法:

    如Object-c是:

- (IBAction)myUnwindAction:(UIStoryboardSegue*)unwindSegue

  1. 在原来正向segue的new view controller中,确定一个触发元素;按住control键,同时拖拉到本身new view controller视图的exit按钮;
  2. 在弹出的窗口中,选择current view controller中声明的方法。

技术分享

图 311 create an unwind segue

?

注意:

????Unwind Segue可以应用于segue路径的逆过程,但不能没有segue的路径。如有A、B、C、D四个view controller,存在正向的segue路径有:AàBàC,D是孤立的,那么可以从C返回到B,也可以从C直接返回到A,但是不能从C返回到D。

?

2.4 程序触发

一遍情况下,segue的触发是由用户的操作而启动的。也可以利用UIViewController的performSegueWithIdentifier:sender:方法在程序中手动触发segue,从而切换到其它的View controller。

-(void)performSegueWithIdentifier:(NSString*)identifier sender:(id)sender

  • identifier:是segueInterface Builder设置的标识;
  • sender:其必须是segue存在的起始点,若不存在则无法转换。

?

如下所示是手动从当前的view controller切换到其它view controller:

- (IBAction)performSegue:(id)sender {

[self performSegueWithIdentifier:@"thirdViewController" sender:self];

}?

?

2.5 自定义

Interface Builder提供一些标准的segue来实现从一个view controller转换到另一个view controller,然而有时不一定能满足要求,这时可用自定义segue来实现转换。

  1. segue生命周期

segue其实也是个实体对象,其是UIStoryboardSegue类或其子类的对象。在app中不需要直接创建它,当segue被触发时,UIKit会创建它们,其过程:

  • 被弹出的view controller被创建和初始化;
  • segue对象被创建,并调用其initWithIdentifier:source:destination: 方法
  • 起始点的view controller内的prepareForSegue:sender: 方法会被调用;
  • segue对象的 perform方法被调用;
  • segue对象被释放。

?

  1. 实现自定义segue

为了自定义segue,需要继承UIStoryboardSegue,并实现如下方法:

  • initWithIdentifier:source:destination方法: 该方法用于初始化segue;
  • perform方法:该方法用于实现转换及动画。

?

如下所示:

  • - (void)perform {
  • // Add your own animation code here.
  • [[self sourceViewController] presentViewController:[self destinationViewController] animated:NO completion:nil];

}

?

3 定制转换动画

当弹出一个view controller时,UIKit提供了一些标准转换动画,并且也支持用户自定义的动画效果。

3.1 UIView动画

UIView是自带动画实现功能,其中有两种方式实现:

  • animateWithDuration系列方法
  • transitionFromViewController方法

?

3.1.1 animateWithDuration

只要在该方法的animations block中修改UIView的动画属性,那么即可实现动画变换,所以为container viewController中实现不同view controller之间的动画切换,只要改变了UIView的动画属性即可以实现变换。

?

如在调用viewMove方法之前,fromVC为显示状态,而toVC为未显示状态。所以调用时即可实现动画切换:

-(void) viewMove:(UIViewController*) fromVC:(UIViewController*)toVC

{

[UIView animateWithDuration:1 animations:^{

fromVC.view.alpha = 0;

toVC.view.alpha = 1;

} completion:^(BOOL finished) {

fromVC.view.alpha = 0;

toVC.view.alpha = 1;

}];

}

?

3.1.2 transitionFromViewController

UIView同时提供了视图切换方法,同时可支持动画变换,即transitionFromViewController方法。从而可以在container viewController中调用该方法来实现视图的变换。

?

如在当前viewController有两个子视图控制器:fromVC和toVC,并且fromVC为显示状态。所以可以调用transitionFromViewController方法来实现动画切换。

-(void) viewMove:(UIViewController*) fromVC:(UIViewController*)toVC

{

[self transitionFromViewController:fromVC toViewController:toVC duration:1

options:UIViewAnimationOptionTransitionFlipFromLeft |UIViewAnimationOptionShowHideTransitionViews

animations:nil completion:nil

];

}?

?

3.2 转换动画次序

转换动画是交互两个view controller的内容,其中存在两种转换类型:弹出(presentation)和撤回(dismissal)。

  • presentation:这种动画是指在app的view controller层次结构中添加新的view controller;
  • dismissal:这种动画是指从app的view controller层次结构中移除view controller。

?

3.2.1 转换delegate

转换delegate是一个实现了UIViewControllerTransitioningDelegate协议的对象,其工作是提供如下之一的对象:

  • Animator对象:

    该对象是实现了UIViewControllerAnimatedTransitioning协议的实体。其功能是负责以动画的形式显示或隐藏view controller的视图。转换delegate向那些进行presenting 和dismissing操作的实体提供Animator对象。

  • Interactive animator对象:

    该对象是实现了UIViewControllerInteractiveTransitioning协议的实体。其也是实现动画操作,但可在动画过程中与用户进行事件交互。

  • Presentation controller:

    当view controller在屏幕时,presentation controller负责管理弹出(presentation)的样式。系统已提供了一些内置样式,同时也支持用户自定义更多的presentation样式。

    ?

若要实现自定义动画,需要设置新弹出view controller的transitioningDelegate属性为遵守UIViewControllerTransitioningDelegate协议的对象,同时要修改新弹出view controller的modalPresentationStyle属性常量UIModalPresentationCustom

技术分享

图 312 The custom presentation and animator objects

如图 312所示,绿色表示 Presenting view controller(主动弹出),蓝色为Presented view controller(被动弹出),被弹出的VC有个Transitioning Delegate属性,通过这个属性可获得Animator、Interactive Animator和Presentation Controller对象。

?

3.2.2 执行时序

  1. Present view controller

当被弹出view controller的transitioningDelegate属性是一个有效对象时,UIKit将按指定的动画弹出这个view controller。在准备阶段UIKit会从UIViewControllerTransitioningDelegate协议(称为转换delegate)调用错误! 超链接引用无效。:方法,从而来查询指定的动画对象,若该对象有效,则UIKit会按如下步骤执行:

  1. UIKit会调用转换delegate的interactionControllerForPresentation:方法,若该方法返回nil,那么UIKit将不与用户进行交互的执行动画。
  2. UIKit调用animator对象的transitionDuration:方法来查询动画的执行时间。
  3. UIKit调用相应方法开始执行动画:
  • 若为非交互动画,则调用animator对象的animateTransition:方法
  • 若为交互动画,则调用interactive animator的startInteractiveTransition:方法
  1. UIKit等待动画对象调用context transitioning的completeTransition:方法来等完成动画。

    在自定义的动画中,需要手动调用transitionContext对象的completeTransition方法来完成动画操作。

?

  1. Dismiss view controller

当消除一个view controller时,UIKit会调用UIViewControllerTransitioningDelegate协议(称为转换delegate)中的animationControllerForDismissedController:方法,并按如下步骤执行程序:

  1. UIKit会调用转换delegate的interactionControllerForDismissal:方法,若该方法返回nil,那么UIKit将不与用户进行交互的执行动画。
  2. UIKit调用animator对象的transitionDuration:方法来查询动画的执行时间。
  3. UIKit调用相应方法开始执行动画:
  • 若为非交互动画,则调用animator对象的animateTransition:方法
  • 若为交互动画,则调用interactive animator的startInteractiveTransition:方法
  1. UIKit等待动画对象调用context transitioning的completeTransition:方法来等完成动画。

    在自定义的动画中,需要手动调用transitionContext对象的completeTransition方法来完成动画操作。

    ?

注意:

????当在实现动画体结束时必须手动调用completeTransition:方法来结束操作。

?

3.2.3 转换Context对象

在转换动画开始之前,UIKit会创建一个transitioning context对象,并将一些如何执行动画的信息填充到该对象中,这些信息包括Presenting view controller、Presented view controller、container view controller,以及是否与用户进行交互的信息。其中context对象是实现了UIViewControllerContextTransitioning协议

如图 313所示,白色方块表示container view(不是controller)、绿色表示Presenting view controller、蓝色表示Presented view controller,虚线表示引用,实线表示方法。Animator对象通过animateTransition:方法获得context对象,而context通过自身的containerView:方法获得container VC。

技术分享

图 313 The transitioning context object

?

3.2.4 转换Coordinator对象

不管是内置的转换还是自定义的转换,UIKit都会创建一个Coordinator对象帮助动画的执行。除了Present和dismissal view controller外,转换还可能发生在view cotroller的frame发生变化时。在动画过程中,可以通过Coordinator对象获得一些信息。如图 314所示,图形语义与图 313一样。

技术分享

图 314 The transition coordinator objects

?

3.3 使用自定义动画

使用自定义的动画实现Present(弹出)view controller,需要进行如下操作:

  1. 创建一个Presented view controller;
  2. 创建一个已经实现了UIViewControllerTransitioningDelegate协议的对象,并将其赋值给Presented view controller对象的transitioningDelegate属性
  3. 调用presentViewController:animated:completion:方法弹出新的view controller,并且需要将YES传递给该方法的animated参数。

?

3.4 实现转换delegate

如图 312所示,转换delegate的功能是为Present和dismissal操作,提供Animator、Interactive animator

和Presentation controller三种对象。所以实现UIViewControllerTransitioningDelegate协议也就是实现那些能够获取这三个对象的方法。

如下是实现一个能获取Animator对象的方法:

  • - (id<UIViewControllerAnimatedTransitioning>)
  • animationControllerForPresentedController:(UIViewController *)presented
  • ???? presentingController:(UIViewController *)presenting
  • ???????? sourceController:(UIViewController *)source
  • {
  • ???? MyAnimator* animator = [[MyAnimator alloc] init];
  • ???? return animator;

}

?

3.5 实现动画delegate

为了实现UIViewControllerAnimatedTransitioning协议需要实现两个方法:

  • transitionDuration: transitionContext:返回动画持续的时间;
  • animateTransition: transitionContext:执行具体动画的方法;

?

其中animateTransition方法是重点,主要工作都是在实现该方法中,其中可以将实现该方法分为如下三个步骤:

  1. 获取与动画有关的参数;
  2. 通过core Animation或UIView animation实现动画;
  3. 完成动画操作。

3.5.1 获取参数

当实现animateTransition方法时,其会传递一个transitionContext参数,可以通过这个参数能够获得与动画相关的信息:

  • 通过viewControllerForKey:方法获得在转换时的"from"和"to" view controller;
  • 通过containerView方法获得动画的超类,从而将所有的子view添加到这个container view中;
  • 通过viewForKey:方法获得被添加或被删除的view,其中在转换过程中要么是被添加,要么是被删除的view。
  • 通过finalFrameForViewController:方法获得最后被添加或删除后view 的frame矩形。

?

由于动画对象可以向Present和dismissal操作提供动画,所以通过transitionContext获取的"from"和"to" view controller语义有些差异。如图 315所示,当执行Present操作时,是将"to" view controller添加到container层次结构中;而当执行dismissal操作时,是将"from" view controller从container层次结构中移除。

技术分享

图 315 The from and to objects

3.5.2 实现动画

为了实现动画效果,在animateTransition:方法中必须完成两部分的内容:

  • 以动画的形式修改presented view controller中的view位置;
  • 将presented view controller中的view添加到container view的层次结构中。

?

  1. Presentation 动画

对于执行弹出动画,可以按如下步骤完成配置:

  • 通过transitionContext对象的viewControllerForKey: viewForKey:方法获得view controllers 和views对象。
  • 设置"to" view的起始位置,同时也可修改其它属性值。
  • 通过transitionContext对象的finalFrameForViewController:方法获取"to" view的最终位置。
  • 将 "to" view添加到container view的层次结构中。
  • 创建动画
    • 在animation block中,修改"to" view为最终的位置。同时也可设置其它属性的最终值;
    • 在completion block中,调用transitionContext对象的completeTransition::方法完成动画。

?

?

  1. Dismissal动画

消除view controller的动画与弹出的操作相似,同样也可按如下步骤完成配置:

  • 通过transitionContext对象的viewControllerForKey: viewForKey:方法获得view controllers 和views对象。
  • 计算presented view controller的view(即是"from" view)最终位置,该view为被清除的view。
  • 还是将 "to" view添加为container view的子view。
  • 创建动画
    • 在animation block中,修改"from" view为最终的位置。同时也可设置其它属性的最终值;
    • 在completion block中,调用transitionContext对象的completeTransition::方法完成动画。

?

?

3.5.3 测试实例

  1. 场景

如要实现图 316所示的两种动画操作,在A视图中弹出B视图,并且可以在B视图将其自身推出返回到A视图。

技术分享

图 316 A custom presentation and dismissal

?

  1. 实现动画Delegate

用户需要创建已经实现了UIViewControllerAnimatedTransitioning协议的对象,如下是MyAnimator类的具体实现,在该类的animateTransition:方法中实现了两种动画效果(Presentation和Dismissal),其中Presenting为YES时,表示实现Presentation操作;当Presenting为NO时,表示实现Dismissal操作,这个Presenting在构造函数中进行初始化。当然也可以将两种操作分别实现在不同的对象中。

?

@interface MyAnimator : NSObject <UIViewControllerAnimatedTransitioning>

@property bool presenting;

-(id) init:(BOOL)presenting;

@end

@implementation MyAnimator

-(id) init:(BOOL)presenting

{

if(self = [super init])

{

self.presenting = presenting;

}

return (self);

}?

-(NSTimeInterval)transitionDuration:(id<UIViewControllerContextTransitioning>)transitionContext

{

return 1;

}

-(void) animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext

{

UIView *containerView = [transitionContext containerView];

UIViewController *fromVC = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];

UIViewController *toVC = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];

?

UIView *toView = [transitionContext viewForKey:UITransitionContextToViewKey];

UIView *fromView = [transitionContext viewForKey:UITransitionContextFromViewKey];

?

// Set up some variables for the animation.

CGRect containerFrame = containerView.frame;

CGRect toViewStartFrame = [transitionContext initialFrameForViewController:toVC];

CGRect toViewFinalFrame = [transitionContext finalFrameForViewController:toVC];

CGRect fromViewFinalFrame = [transitionContext finalFrameForViewController:fromVC];

?

// Set up the animation parameters.

if (self.presenting) {

// Modify the frame of the presented view so that it starts

// offscreen at the lower-right corner of the container.

toViewStartFrame.origin.x = containerFrame.size.width;

toViewStartFrame.origin.y = containerFrame.size.height;

}

else {

// Modify the frame of the dismissed view so it ends in

// the lower-right corner of the container view.

fromViewFinalFrame = CGRectMake(containerFrame.size.width,

containerFrame.size.height,

toView.frame.size.width,

toView.frame.size.height);

}

?

// Always add the "to" view to the container.

// And it doesn‘t hurt to set its start frame.

[containerView addSubview:toView];

toView.frame = toViewStartFrame;

?

// Animate using the animator‘s own duration value.

[UIView animateWithDuration:[self transitionDuration:transitionContext]

animations:^{

if (self.presenting) {

// Move the presented view into position.

[toView setFrame:toViewFinalFrame];

}

else {

// Move the dismissed view offscreen.

[fromView setFrame:fromViewFinalFrame];

}

}

completion:^(BOOL finished){

BOOL success = ![transitionContext transitionWasCancelled];

?

// After a failed presentation or successful dismissal, remove the view.

if ((self.presenting && !success) || (!self.presenting && success)) {

[toView removeFromSuperview];

}

?

// Notify UIKit that the transition has finished

[transitionContext completeTransition:success];

}];

}

?

  1. 实现转换Delegate

在弹出动画前,需要先给Presented view controller对象设置transitioningDelegate属性,而设置的对象为UIViewControllerTransitioningDelegate协议的实现类。如下是一种实现方式,由于被弹出的view controller有两种动画效果(Presentation和Dismissal),所以需要实现animationControllerForPresentedControlleranimationControllerForDismissedController方法分别弹出两种不同的动画对象。

?

@interface NSObject: UIViewController <UIViewControllerTransitioningDelegate>

@end

@implementation myTransitioningDelegate

- (id<UIViewControllerAnimatedTransitioning>)

animationControllerForPresentedController:(UIViewController *)presented presentingController:(UIViewController *)presenting sourceController:(UIViewController *)source

{

MyAnimator *animator = [[MyAnimator alloc] init:YES];

?

return animator;

}

-(id<UIViewControllerAnimatedTransitioning>)animationControllerForDismissedController:(UIViewController *)dismissed

{

MyAnimator *animator = [[MyAnimator alloc] init:NO];

return animator;

}

@end?

?

  1. 实现弹出操作

上述的转换delegate和动画delegate都已经实现完成,接下来就是要实现转换视图的响应方法。即firstViewController为图 316的A视图控制器,而thirdViewController为图 316的B视图控制器。而customAnimation:方法为按钮的响应方法,在该方法中调用presentViewController方法来弹出thirdViewController视图(即B视图)。

其中需要注意的是mtd属性不能将其声明为customAnimation方法内的局部变量,否则将导致Dismissal操作不能实现动画,因为在该方法推出后,mtd变量的生命周期将结束,从而在Dismissal操作时其已经无效,所以这里将mtd声明为成员属性,当然若firstViewController类自身实现了UIViewControllerAnimatedTransitioning协议,那么可以将transitioningDelegate设置为self。

@interface firstViewController : UIViewController <UIViewControllerTransitioningDelegate>

{

myTransitioningDelegate *mtd;

}

@end

- (IBAction)customAnimation:(id)sender {

thirdViewController *thirdVC = [self.storyboard instantiateViewControllerWithIdentifier:@"thirdViewController"];

?

mtd = [[myTransitioningDelegate alloc] init];

thirdVC.transitioningDelegate = mtd;

thirdVC.modalPresentationStyle = UIModalPresentationCustom;

?

[self presentViewController:thirdVC animated:YES completion:nil];

}

?

  1. 实现退出操作

在被弹出视图(B视图)中,只需实现退出按钮的响应方法(dismissal),从而在该方法中调用dismissViewControllerAnimated方法推出操作。

@implementation thirdViewController

- (IBAction)dismissal:(id)sender

{

[self dismissViewControllerAnimated:YES completion:nil];

}

@end

?

3.6 添加交互对象

UIKit支持添加一个交互对象,通过这个对象用户能在进行弹出等操作时,控制动画的动作。为转换delegate添加一个交互对象也非常简单,只需在其interactionControllerForPresentation方法中返回一个遵守UIViewControllerInteractiveTransitioning协议的对象即可。可以直接实现该协议,也可以继承UIPercentDrivenInteractiveTransition类,但都需重载startInteractiveTransition方法。

????最后需要注意的是,由于转换动画还是执行UIViewControllerTransitioningDelegate协议中animationControllerForPresentedController方法返回的对象,但动画开始执行的时间仍是在调用finishInteractiveTransition方法后。

?

如下所示的实例:

  1. 实例场景

用户在点击视图的按钮后,需要再点击视图中的空白区域,才能实现转换及动画。从而本例只是在3.3.5.3小节所示的基础上添加一个交互对象,所以对其它内容都无需修改,只是添加的交互类myInteractiveAnimator,及在myTransitioningDelegate类中添加了一个interactionControllerForPresentation方法,在该方法中返回一个交互对象。

?

  1. 实现交互类

本例采用继承UIPercentDrivenInteractiveTransition类的方式实现交互,从而实现了startInteractiveTransition方法。在该方法中添加了一个点击手势识别器,并在响应方法中启动动画转换。

@interface myInteractiveAnimator : UIPercentDrivenInteractiveTransition

@property UITapGestureRecognizer* panGesture;

@end

@implementation myInteractiveAnimator

- (void)startInteractiveTransition:(id<UIViewControllerContextTransitioning>)transitionContext

{

// Always call super first.

[super startInteractiveTransition:transitionContext];

// Add the gesture recognizer to the container view.

self.panGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleSwipeUpdate:)];

UIView* container = [transitionContext containerView];

[container addGestureRecognizer:self.panGesture];

}?

-(void)handleSwipeUpdate:(UIGestureRecognizer *)gestureRecognizer

{

NSLog(@"hello world");

[self finishInteractiveTransition];

}

@end

?

  1. 返回交互对象

如下是在3.3.5.3小节所示的myTransitioningDelegate类基础上添加了interactionControllerForPresentation方法,其它方法都是原来的内容。

@implementation myTransitioningDelegate

- (id<UIViewControllerAnimatedTransitioning>)

animationControllerForPresentedController:(UIViewController *)presented presentingController:(UIViewController *)presenting sourceController:(UIViewController *)source

{

MyAnimator *animator = [[MyAnimator alloc] init:YES];

return animator;

}

-(id<UIViewControllerAnimatedTransitioning>)animationControllerForDismissedController:(UIViewController *)dismissed

{

MyAnimator *animator = [[MyAnimator alloc] init:NO];

return animator;

}?

- (nullable id <UIViewControllerInteractiveTransitioning>)interactionControllerForPresentation:(id <UIViewControllerAnimatedTransitioning>)animator

{

myInteractiveAnimator *myAnimator = [[myInteractiveAnimator alloc] init];

return myAnimator;

}

@end?

?

?

参考文献

  1. View Controller Programming Guide for IOS
  2. UIViewControllerTransitioningDelegate Protocol Reference.
  3. UIViewControllerContextTransitioning Protocol Reference.
  4. UIViewControllerInteractiveTransitioning Protocol Referrence

iOS UIKit:viewController之弹出和转换(3)

标签:

原文地址:http://www.cnblogs.com/hlwfirst/p/5453062.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!