IOS|iOS7教程系列(自定义导航转场动画以及更多)
【IOS|iOS7教程系列(自定义导航转场动画以及更多)】转自cocoachina:http://www.cocoachina.com/gamedev/misc/2013/1224/7597.html
原文:
iOS 7 Tutorial Series: Custom Navigation Transitions & More
感谢翻译小组成员
dingdaojun热心翻译。本篇文章是我们每周推荐优秀国外的技术类文章的其中一篇。如果您有不错的原创或译文,欢迎提交给我们,更欢迎其他朋友加入我们的翻译小组(联系qq:2408167315)。 在iOS7以前,开发者如果希望定制导航控制器推入推出视图时的转场动画,一般都只能通过子类化UINavigationController或者自己编写动画代码去覆盖相应的方法,现在iOS7为开发者带来了福音,苹果公司引入了大量新API,给予了开发者很高的自由度,在处理由UIViewController管理的UIView动画时,这些API使用方便,可扩展性也很强,定制起来非常轻松: ? 全新的针对UIView的动画block方法 ? 全新的UIViewControllerAnimatedTransitioning协议以及动画控制器的概念 ? Interaction Controllers以及Transition Coordinators ? 全新的针对动画的助手API(简便方法) 这里我编写了一个示例应用程序,其中展示了我将在这篇文章中所提到的一些技巧, 为了快速理解我们应当如何使用iOS7的新API来处理 UIViewController的转场动画,请在此链接中下载该示例。
文章图片 |
/cms/uploads/soft/131224/4673-131224114628.zip |
- [UIView animateKeyframesWithDuration:duration delay:delay
- options:options animations:^{
- [UIView addKeyframeWithRelativeStartTime:0.0
- relativeDuration:0.5 animations:^{
- //第一帧要执行的动画
- }];
- [UIView addKeyframeWithRelativeStartTime:0.5
- relativeDuration:0.5 animations:^{
- //第二帧要执行的动画
- }];
- } completion:^(BOOL finished) {
- //动画结束后执行的代码块
- }];
- [UIView addKeyframeWithRelativeStartTime:0.0
- relativeDuration:0.15 animations:^{
- //顺时针旋转90度
- snapshot.transform = CGAffineTransformMakeRotation(M_PI *
- -1.5);
- }];
- [UIView addKeyframeWithRelativeStartTime:0.15
- relativeDuration:0.10 animations:^{
- //180度
- snapshot.transform = CGAffineTransformMakeRotation(M_PI *
- 1.0);
- }];
- [UIView addKeyframeWithRelativeStartTime:0.25
- relativeDuration:0.20 animations:^{
- //摆过中点,225度
- snapshot.transform = CGAffineTransformMakeRotation(M_PI *
- 1.3);
- }];
- [UIView addKeyframeWithRelativeStartTime:0.45
- relativeDuration:0.20 animations:^{
- //再摆回来,140度
- snapshot.transform = CGAffineTransformMakeRotation(M_PI *
- 0.8);
- }];
- [UIView addKeyframeWithRelativeStartTime:0.65
- relativeDuration:0.35 animations:^{
- //旋转后掉落
- //最后一步,视图淡出并消失
- CGAffineTransform shift =
- CGAffineTransformMakeTranslation(180.0, 0.0);
- CGAffineTransform rotate =
- CGAffineTransformMakeRotation(M_PI * 0.3);
- snapshot.transform = CGAffineTransformConcat(shift,
- rotate);
- _coverView.alpha = 0.0;
- }];
- [UIView animateWithDuration:duration delay:delay
- usingSpringWithDamping:damping initialSpringVelocity:velocity
- options:options animations:^{
- //这里书写动画相关代码
- } completion:^(BOOL finished) {
- //动画结束后执行的代码块
- }];
- -(void)animateTransition:
- (id
)transitionContext { - //获取容器视图引用
- UIView *containerView = [transitionContext
- containerView];
- UIViewController *fromViewController = [transitionContext
- viewControllerForKey:UITransitionContextFromViewControllerKey
- ];
- UIViewController *toViewController = [transitionContext
- viewControllerForKey:UITransitionContextToViewControllerKey];
- if (self.type == AnimationTypePresent) {
- //插入“to”视图,初始缩放值为0.0
- toViewController.view.transform =
- CGAffineTransformMakeScale(0.0, 0.0);
- [containerView insertSubview:toViewController.view
- aboveSubview:fromViewController.view];
- //缩放“to”视图为想要的效果
- [UIView animateWithDuration:[self
- transitionDuration:transitionContext] animations:^{
- toViewController.view.transform =
- CGAffineTransformMakeScale(1.0, 1.0);
- } completion:^(BOOL finished) {
- [transitionContext completeTransition:YES];
- }];
- } else if (self.type == AnimationTypeDismiss) {
- //插入“to”视图
- [containerView insertSubview:toViewController.view
- belowSubview:fromViewController.view];
- //缩小“from”视图,直到其消失
- [UIView animateWithDuration:[self
- transitionDuration:transitionContext] animations:^{
- fromViewController.view.transform =
- CGAffineTransformMakeScale(0.0, 0.0);
- } completion:^(BOOL finished) {
- [transitionContext completeTransition:YES];
- }];
- }
- }
- -(NSTimeInterval)transitionDuration:
- (id
)transitionContext { - return 0.4;
- }
文章图片
显然,苹果公司帮助开发者完成了大部分让人讨厌的细节工作,仅仅需要我们自己完成的工作就是定义动画的初始状态和终止状态,并调整到自己满意的效果。最后我再啰嗦两句有关transitionContext的重要注意事项: 1.获取frame的方法可能会返回CGRectZero——如果系统无法确定该frame的值具体是什么。例如,如果你使用自定义的模态视图控制器 推出动画,在结束时系统无法确定其finalFrame。 2.如果视图控制器已经从屏幕上移除了,那么获取frame的方法也会返回CGRectZero。例如在导航控制器的转场动画结束后,试图获取“from”视图的finalFrame。 你不用手动去移除“from”视图,transitionContext将自动帮你完成。 3.如果你在应用的其他地方需要使用transitionContext,你可以放心地使用动画控制器保留一个transitionContext的引用。 将动画控制器应用到转场动画中。 现在,我们已经开发好了动画控制器,那么最后需要做的就是,将它们应用到转场动画中:我们需要对管理转场动画的UIViewController做一些操作。 一般来说,我们只需要让UIViewController符合UIViewController-TransitioningDelegate 协议, 编写animationController-ForPresentedController和animationControllerForDismissedController方法。在我的示例应用程序中,我设置了一个属性,用来让动画控制器知道目前正在推入还是推出视图:
- -(id
) - animationControllerForPresentedController:(UIViewController
- *)presented presentingController:(UIViewController
- *)presenting sourceController:(UIViewController *)source {
- modalAnimationController.type = AnimationTypePresent;
- return modalAnimationController;
- }
- -(id
) - animationControllerForDismissedController:(UIViewController
- *)dismissed {
- modalAnimationController.type = AnimationTypeDismiss;
- return modalAnimationController;
- }
- OptionsViewController *modal = [[OptionsViewController alloc]
- initWithNibName:@"OptionsViewController" bundle:[NSBundle
- mainBundle]];
- modal.transitioningDelegate = self;
- modal.modalPresentationStyle = UIModalPresentationCustom;
- [self presentViewController:modal animated:YES
- completion:nil];
- @interface UIPercentDrivenInteractiveTransition : NSObject
- @property (readonly) CGFloat duration;
- @property (readonly) CGFloat percentComplete;
- @property (nonatomic,assign) CGFloat completionSpeed;
- @property (nonatomic,assign) UIViewAnimationCurve
- completionCurve;
- - (void)updateInteractiveTransition:(CGFloat)percentComplete;
- - (void)cancelInteractiveTransition;
- - (void)finishInteractiveTransition;
- -(void)handlePinch:(UIPinchGestureRecognizer *)pinch {
- CGFloat scale = pinch.scale;
- switch (pinch.state) {
- case UIGestureRecognizerStateBegan: {
- _startScale = scale;
- self.interactive = YES;
- [self.navigationController
- popViewControllerAnimated:YES];
- break;
- }
- case UIGestureRecognizerStateChanged: {
- CGFloat percent = (1.0 - scale/_startScale);
- [self updateInteractiveTransition:(percent < 0.0) ?
- 0.0 : percent];
- break;
- }
- case UIGestureRecognizerStateEnded: {
- CGFloat percent = (1.0 - scale/_startScale);
- BOOL cancelled = ([pinch velocity] < 5.0 && percent
- <= 0.3);
- if (cancelled) [self cancelInteractiveTransition];
- else [self finishInteractiveTransition];
- break;
- }
- case UIGestureRecognizerStateCancelled: {
- CGFloat percent = (1.0 - scale/_startScale);
- BOOL cancelled = ([pinch velocity] < 5.0 && percent
- <= 0.3);
- if (cancelled) [self cancelInteractiveTransition];
- else [self finishInteractiveTransition];
- break;
- }
- }
- }
- -(void)startInteractiveTransition:
- (id
)transitionContext { - //获取transitionContext对象的引用
- _context = transitionContext;
- //获取容器视图引用
- UIView *containerView = [transitionContext
- containerView];
- UIViewController *fromViewController = [transitionContext
- viewControllerForKey:UITransitionContextFromViewControllerKey
- ];
- UIViewController *toViewController = [transitionContext
- viewControllerForKey:UITransitionContextToViewControllerKey];
- //插入“to”视图
- toViewController.view.frame = [transitionContext
- finalFrameForViewController:toViewController];
- [containerView insertSubview:toViewController.view
- belowSubview:fromViewController.view];
- //保留需要缩?小的视图的引用
- _transitioningView = fromViewController.view;
- }
- -(void)updateWithPercent:(CGFloat)percent {
- CGFloat scale = fabsf(percent-1.0);
- _transitioningView.transform =
- CGAffineTransformMakeScale(scale, scale);
- [_context updateInteractiveTransition:percent];
- }
- -(void)end:(BOOL)cancelled {
- if (cancelled) {
- [UIView animateWithDuration:_completionSpeed
- animations:^{
- _transitioningView.transform =
- CGAffineTransformMakeScale(1.0, 1.0);
- } completion:^(BOOL finished) {
- [_context cancelInteractiveTransition];
- [_context completeTransition:NO];
- }];
- } else {
- [UIView animateWithDuration:_completionSpeed
- animations:^{
- _transitioningView.transform =
- CGAffineTransformMakeScale(0.0, 0.0);
- } completion:^(BOOL finished) {
- [_context finishInteractiveTransition];
- [_context completeTransition:YES];
- }];
- }
- }
- [UIView performWithoutAnimation:^{
- //确保不执行动画
- }];
- CollectionViewController *VC = [[CollectionViewController
- alloc] initWithCollectionViewLayout:flowLayout];
- VC.title = @"Mini Apples";
- VC.useLayoutToLayoutNavigationTransitions = YES;
- [self.navigationController pushViewController:VC
- animated:YES];
- [self.transitionCoordinator
- animateAlongsideTransition:^(id
- rdinatorContext> context) {
- //要执行的动画
- }
- completion:^(id
- context) {
- //动画结束后执行的代码块
- }];
- [self.transitionCoordinator
- notifyWhenInteractionEndsUsingBlock:^(id
- sitionCoordinatorContext> context) {
- //动画结束后执?行的代码块
- }];
- [view snapshotViewAfterScreenUpdates:NO];
- [view snapshotViewAfterScreenUpdates:YES];
- [view setAlpha:0.0];
文章图片 |
/cms/uploads/soft/131224/4673-131224114628.zip |
推荐阅读
- 2.6|2.6 Photoshop操作步骤的撤消和重做 [Ps教程]
- 2020-04-07vue中Axios的封装和API接口的管理
- 漫画初学者如何学习漫画背景的透视画法(这篇教程请收藏好了!)
- iOS中的Block
- 记录iOS生成分享图片的一些问题,根据UIView生成固定尺寸的分享图片
- 2019-08-29|2019-08-29 iOS13适配那点事
- Hacking|Hacking with iOS: SwiftUI Edition - SnowSeeker 项目(一)
- iOS面试题--基础
- 接口|axios接口报错-参数类型错误解决
- iOS|iOS 笔记之_时间戳 + DES 加密