最近闲来无事,研究页面见的跳转动画,觉得挺好玩的,之前系统内置的push,present居然可以自定义确实很新奇。好了闲话不多说。
先看看为什么需要自定动画切换,
首先我们之前如果要自定义动画切换一般通过addsubview的方式,这种一般通过手动编写uiview动画实现
其次我们把动画代码全写在同一个vc中,而且把一个控制器加在另一个控制器中的view上这样增加了他们之间的耦合性,不符合完美代码的标准
所以我们许要自定义动画,ios7自定动画完美的解决了这两个问题
接着我们在编码之前先来了解ios7过渡动画相关的知识和api
@protocol UIViewControllerContextTransitioning
这个接口用来提供页面切换的上下文,总所周知,上下文一般用来保存页面相关的信息,此协议不需要开发者自己实现,对于切换动画来说,这个接口中最重要的方法有:
.-(UIView *)containerView; VC切换所放的容器,你可以向里面加入将要显示的view和移除要切换的view
.-(UIViewController *)viewControllerForKey:(NSString *)key;提供了个key,返回对应的VC。现在的SDK中key的选择只有
UITransitionContextFromViewControllerKey和
UITransitionContextToViewControllerKey两种,分别表示将要切出和切入的控制器
.-(CGRect)initialFrameForViewController:(UIViewController *)vc;某个VC的初始化位置,可以用来做的动画的计算
.-(CGRect)finalFrameForViewController:(UIViewController *)vc;和上面的方法对应,得到切换结束时某个VC的frame.
.-(void)completeTransition:(BOOL)didComplete;向这个context报告切换完成
另外一个比较重要的协议
@protocol UIViewControllerAnimatedTransitioning
这个接口负责切换的具体内容,说白了,就是我们需要自定义的动画代码,一般来说我们就是要自己实现这个协议的方法,自定义个一个继承自nsobject的类,实现下面的方法
-(NSTimeInterval)transitionDuration:(id < UIViewControllerContextTransitioning >)transitionContext; 系统给出一个切换上下文,我们根据上下文环境返回这个切换所需要的花费时间(一般就返回动画的时间就好了,SDK会用这个时间来在百分比驱动的切换中进行帧的计算,后面再详细展开)。
-(void)animateTransition:(id < UIViewControllerContextTransitioning >)transitionContext; 在进行切换的时候将调用该方法,我们对于切换时的UIView的设置和动画都在这个方法中完成。
好了切换动画类写好了,接下来就是使用了,使用的时候我们一般只要在我们用的vc中实现下面的协议就好了比如:
//BouncePresentAnimation.h
@interface BouncePresentAnimation :NSObject<UIViewControllerAnimatedTransitioning>
@end
//BouncePresentAnimation.m
- (NSTimeInterval)transitionDuration:(id <UIViewControllerContextTransitioning>)transitionContext
{
return 0.8f;
}
- (void)animateTransition:(id <UIViewControllerContextTransitioning>)transitionContext
{
// 1. Get controllers from transition context
UIViewController *toVC = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
// 2. Set init frame for toVC
CGRect screenBounds = [[UIScreen mainScreen] bounds];
CGRect finalFrame = [transitionContext finalFrameForViewController:toVC];
toVC.view.frame = CGRectOffset(finalFrame,0, screenBounds.size.height);
// 3. Add toVC's view to containerView
UIView *containerView = [transitionContext containerView];
[containerView addSubview:toVC.view];
// 4. Do animate now
NSTimeInterval duration = [self transitionDuration:transitionContext];
[UIView animateWithDuration:duration
delay:0.0
usingSpringWithDamping:0.6
initialSpringVelocity:0.0
options:UIViewAnimationOptionCurveLinear
animations:^{
toVC.view.frame = finalFrame;
} completion:^(BOOL finished) {
// 5. Tell context that we completed.
[transitionContext completeTransition:YES];
}];
}
@protocol UIViewControllerTransitioningDelegete
- -(id< UIViewControllerAnimatedTransitioning >)animationControllerForPresentedController:(UIViewController *)presented presentingController:(UIViewController *)presenting sourceController:(UIViewController *)source;
- -(id< UIViewControllerAnimatedTransitioning >)animationControllerForDismissedController:(UIViewController *)dismissed;
- -(id< UIViewControllerInteractiveTransitioning >)interactionControllerForPresentation:(id < UIViewControllerAnimatedTransitioning >)animator;
- -(id< UIViewControllerInteractiveTransitioning >)interactionControllerForDismissal:(id < UIViewControllerAnimatedTransitioning >)animator;
后两个方法涉及交互式切换,后面再讲
比如:
@interface MainViewController ()<ModalViewControllerDelegate,UIViewControllerTransitioningDelegate>
@property (nonatomic,strong) BouncePresentAnimation *presentAnimation;
@end
@implementation MainViewController
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
_presentAnimation = [BouncePresentAnimation new];
}
return self;
}
-(void) buttonClicked:(id)sender
{
//...
mvc.transitioningDelegate =self;
//...
}
- (id <UIViewControllerAnimatedTransitioning>)animationControllerForPresentedController:(UIViewController *)presented presentingController:(UIViewController *)presenting sourceController:(UIViewController *)source
{
returnself.presentAnimation;
}
手势驱动的百分比切换
iOS7引入了一种手势驱动的VC切换的方式(交互式切换)。如果你使用系统的各种应用,在navViewController里push了一个新的VC的话,返回时并不需要点击左上的Back按钮,而是通过从屏幕左侧划向右侧即可完成返回操作。而在这个操作过程中,我们甚至可以撤销我们的手势,以取消这次VC转移。在新版的Safari中,我们甚至可以用相同的手势来完成网页的后退功能(所以很大程度上来说屏幕底部的工具栏成为了摆设)。如果您还不知道或者没太留意过这个改动,不妨现在就拿手边的iOS7这辈试试看,手机浏览的朋友记得切回来哦 :)
我们这就动手在自己的VC切换中实现这个功能吧,首先我们需要在刚才的知识基础上补充一些东西:
首先是UIViewControllerContextTransitioning,刚才提到这个是系统提供的VC切换上下文,如果您深入看了它的头文件描述的话,应该会发现其中有三个关于InteractiveTransition的方法,正是用来处理交互式切换的。但是在初级的实际使用中我们其实可以不太理会它们,而是使用iOS 7 SDK已经给我们准备好的一个现成转为交互式切换而新加的类:UIPercentDrivenInteractiveTransition。
UIPercentDrivenInteractiveTransition是什么
这是一个实现了UIViewControllerInteractiveTransitioning接口的类,为我们预先实现和提供了一系列便利的方法,可以用一个百分比来控制交互式切换的过程。一般来说我们更多地会使用某些手势来完成交互式的转移(当然用的高级的话用其他的输入..比如声音,iBeacon距离或者甚至面部微笑来做输入驱动也无不可,毕竟想象无极限嘛..),这样使用这个类(一般是其子类)的话就会非常方便。我们在手势识别中只需要告诉这个类的实例当前的状态百分比如何,系统便根据这个百分比和我们之前设定的迁移方式为我们计算当前应该的UI渲染,十分方便。具体的几个重要方法:
- -(void)updateInteractiveTransition:(CGFloat)percentComplete 更新百分比,一般通过手势识别的长度之类的来计算一个值,然后进行更新。之后的例子里会看到详细的用法
- -(void)cancelInteractiveTransition 报告交互取消,返回切换前的状态
- –(void)finishInteractiveTransition 报告交互完成,更新到切换后的状态
@protocol UIViewControllerInteractiveTransitioning
就如上面提到的,UIPercentDrivenInteractiveTransition只是实现了这个接口的一个类。为了实现交互式切换的功能,我们需要实现这个接口
以上内容参照很多人的总结而成,希望对大家能有所帮助!