iOS7 interactive transitions(视图控制器的过渡动画 )
原文地址:http://dativestudios.com/blog/2013/09/29/interactive-transitions/
iOS7 引入了一种新的视图控制器过渡的方式,通过这篇文章和项目源码看看怎么使用 UINavigationController实现自定义的transition.
在这个demo中我们会在两个view controller之间添加一个custom transition,DSLFirstViewController 作为一个collectionview呈现Things实例,每个cell有一个image和label,DSLSecondViewController展示单个thing对象,同样拥有一个image和label。在过渡过程中我们要实现的就是把cell的image view 过渡到 第二个控制器的 imageView。
The Xcode project for this article is available on GitHub.
Implementing a custom transition
这个transition由一个遵守<UIViewControllerAnimatedTransitioning>协议的NSObject的对象控制,在这个transition object中,主要有两个要实现的委托方法 animateTransition:和transitionDuration:
transitionDuration:确定过渡的时间
- (NSTimeInterval)transitionDuration:(id<UIViewControllerContextTransitioning>)transitionContext { return 0.3; }animateTransition:方法实现两个view之间过渡时的主要逻辑,这个方法传递给了几个我们可以在transition过程使用的参数
viewControllerForKey:
gives us access to the two view controllers we‘re transitioning betweencontainerView
property gives us the containing view of both view controllersinitialFrameForViewController:
andfinalFrameForViewController
give us the start and end position of each view controller‘s view
- (void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext { DSLFirstViewController *fromViewController = (DSLFirstViewController*)[transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey]; DSLSecondViewController *toViewController = (DSLSecondViewController*)[transitionContext viewControllerForKey:UITransitionContextToViewControllerKey]; UIView *containerView = [transitionContext containerView];接下来获取用来过渡的cell,并对cell的imageView进行快照(snapshot),这个snapshot就是在过渡过程中要实现动画的对象
主要思路就是:隐藏cell.imageView这样给人的错觉是cell.imageView在动画而不是这个snapshot
// Get a snapshot of the thing cell we're transitioning from DSLThingCell *cell = (DSLThingCell*)[fromViewController.collectionView cellForItemAtIndexPath:[[fromViewController.collectionView indexPathsForSelectedItems] firstObject]]; UIView *cellImageSnapshot = [cell.imageView snapshotView]; cellImageSnapshot.frame = [containerView convertRect:cell.imageView.frame fromView:cell.imageView.superview]; cell.imageView.hidden = YES;把second view controller‘s view放到相应位置使它透明,之后在过渡过程来把它渐显出来
// Setup the initial view states toViewController.view.frame = [transitionContext finalFrameForViewController:toViewController]; toViewController.view.alpha = 0; toViewController.imageView.hidden = YES; [containerView addSubview:toViewController.view]; [containerView addSubview:cellImageSnapshot];现在开始给views 动画,移动image snapshot,在第二个controller中逐渐显示出来,最后在动画的completion block中移除snapshot,恢复透明度就行了
[UIView animateWithDuration:duration animations:^{ // Fade in the second view controller's view toViewController.view.alpha = 1.0; // Move the cell snapshot so it's over the second view controller's image view CGRect frame = [containerView convertRect:toViewController.imageView.frame fromView:toViewController.view]; cellImageSnapshot.frame = frame; } completion:^(BOOL finished) { // Clean up toViewController.imageView.hidden = NO; cell.hidden = NO; [cellImageSnapshot removeFromSuperview]; // Declare that we've finished [transitionContext completeTransition:!transitionContext.transitionWasCancelled]; }]; }
Using the custom transition
到此我们自定义的 transition object就实现了,接下来就是告诉UINavigationController来使用它,当一个viewcontroller从navigation的stack中push或者pop off的时候,UINavigatonDelegate的委托方法就需要寻求这个 transition object类了(翻译的好吃力啊 )
The first thing to do is to specify that our view controller conforms to the UINavigationControllerDelegate
protocol
@interface DSLFirstViewController ()<UINavigationControllerDelegate>
We also need to make our view controller it‘s navigation controller‘s delegate. A good spot to do this is in viewDidAppear:
.
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
// Set outself as the navigation controller‘s delegate so we‘re asked for a transitioning object
self.navigationController.delegate = self;
}
We should also stop being the navigation control‘s delegate when the view controller is disappearing
- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
// Stop being the navigation controller‘s delegate
if (self.navigationController.delegate == self) {
self.navigationController.delegate = nil;
}
}
Now our view controller is the navigation controller‘s delegate, we can implement the following longwinded UINavigationControllerDelegate
method
- (id<UIViewControllerAnimatedTransitioning>)navigationController:(UINavigationController *)navigationController
animationControllerForOperation:(UINavigationControllerOperation)operation
fromViewController:(UIViewController *)fromVC
toViewController:(UIViewController *)toVC {
// Check if we‘re transitioning from this view controller to a DSLSecondViewController
if (fromVC == self && [toVC isKindOfClass:[DSLSecondViewController class]]) {
return [[DSLTransitionFromFirstToSecond alloc] init];
}
else {
return nil;
}
}
Making transitions interactive
UIScreenEdgePanGestureRecognizer和UIPercentDrivenInteractiveTransition的理解
- (void)viewDidLoad {
[super viewDidLoad];
...
UIScreenEdgePanGestureRecognizer *popRecognizer = [[UIScreenEdgePanGestureRecognizer alloc] initWithTarget:self action:@selector(handlePopRecognizer:)];
popRecognizer.edges = UIRectEdgeLeft;
[self.view addGestureRecognizer:popRecognizer];
}
- (void)handlePopRecognizer:(UIScreenEdgePanGestureRecognizer*)recognizer {
// Calculate how far the user has dragged across the view
CGFloat progress = [recognizer translationInView:self.view].x / (self.view.bounds.size.width * 1.0);
progress = MIN(1.0, MAX(0.0, progress));
if (recognizer.state == UIGestureRecognizerStateBegan) {
// Create a interactive transition and pop the view controller
self.interactivePopTransition = [[UIPercentDrivenInteractiveTransition alloc] init];
[self.navigationController popViewControllerAnimated:YES];
}
else if (recognizer.state == UIGestureRecognizerStateChanged) {
// Update the interactive transition‘s progress
[self.interactivePopTransition updateInteractiveTransition:progress];
}
else if (recognizer.state == UIGestureRecognizerStateEnded || recognizer.state == UIGestureRecognizerStateCancelled) {
// Finish or cancel the interactive transition
if (progress > 0.5) {
[self.interactivePopTransition finishInteractiveTransition];
}
else {
[self.interactivePopTransition cancelInteractiveTransition];
}
self.interactivePopTransition = nil;
}
}
Now we‘re creating and updating a UIPercentDrivenInteractiveTransition
object,
we need to tell the navigation controller to use it. We do this by implementing another new UINavigationControllerDelegate
method:
- (id<UIViewControllerInteractiveTransitioning>)navigationController:(UINavigationController *)navigationController
interactionControllerForAnimationController:(id<UIViewControllerAnimatedTransitioning>)animationController {
// Check if this is for our custom transition
if ([animationController isKindOfClass:[DSLTransitionFromSecondToFirst class]]) {
return self.interactivePopTransition;
}
else {
return nil;
}
}
Summary
Hopefully this post and the example project will help you to understand the steps needed to create your own custom transitions and interactions. A sample Xcode project is available on GitHub.
郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。