前言:之前三篇中,手势过渡动画都是直接使用事件动画的,其实我们可以使用代理方法自定义手势动画。在上一篇列子的基础上,重新定义一个手势交互动画对象。
正文:
最终效果:
由于Push和Pop时的事件动画和上篇都一样,这里着重看手势交互动画。
分析动画实现策略
—> 过渡开始前,在SecondViewController中的大图上添加一个拖拽手势。
—> 过渡开始时,对SecondViewController中的大图截图,覆盖大图位置,隐藏大图,隐藏被选中cell的imageView。
—> 移动过程中,改变SecondViewController的view的alpha值
—> 过渡完成时,对截图做动画缩小到之前被选中的cell的位置上。移除截图,SecondViewController的view的alpha值置为0,显示cell的imageView。
—> 若是取消手势,则截图回到原来的位置上后,截图移除,大图重新显示,SecondViewController的view的alpha值置为1。
手势事件:
- (void)panAction:(UIPanGestureRecognizer *)panGestureRecognizer
{
CGPoint translation = [panGestureRecognizer translationInView:panGestureRecognizer.view];
// 计算移动距离
CGFloat distance = sqrtf(translation.x * translation.x + translation.y * translation.y);
// 200是比例基数,可根据需要设置
CGFloat progress = distance / 200;
// 向上移动才有效
progress = translation.y > 0 ? 0 : MIN(progress, 1.0f);
if (panGestureRecognizer.state == UIGestureRecognizerStateBegan)
{
self.interactiving = YES;
// 记录初始手指位置
self.lastPoint = [panGestureRecognizer locationInView:panGestureRecognizer.view];
[self.vc.navigationController popViewControllerAnimated:YES];
}
else if (panGestureRecognizer.state == UIGestureRecognizerStateChanged)
{
CGPoint currentPoint = [panGestureRecognizer locationInView:panGestureRecognizer.view];
// 手指的位移
self.changePoint = CGPointMake(currentPoint.x - self.lastPoint.x, currentPoint.y - self.lastPoint.y);
self.lastPoint = currentPoint;
[self updateInteractiveTransition:progress];
}
else if (panGestureRecognizer.state == UIGestureRecognizerStateCancelled)
{
self.interactiving = NO;
[self cancelInteractiveTransition];
}
else if (panGestureRecognizer.state == UIGestureRecognizerStateEnded)
{
self.interactiving = NO;
if (progress > 0.5f) {
[self finishInteractiveTransition];
} else {
[self cancelInteractiveTransition];
}
}
}
(1)changePoint是记录每次移动的距离,截图跟随手势移动的偏移量就是它。
过渡动画开始时的代理方法
- (void)startInteractiveTransition:(id<UIViewControllerContextTransitioning>)transitionContext
{
self.contextTransition = transitionContext;
// 1. 获取源和目标控制器
SecondViewController *fromVc = [self fromVc];
FirstCollectionViewController *toVc = [self toVc];
UIView *containerView = [self containerView];
// 2. 截图,并初始化截图状态
UIView *sanpShotView = [fromVc.imageInSecond snapshotViewAfterScreenUpdates:NO];
self.sanpShotView = sanpShotView;
sanpShotView.frame = [containerView convertRect:fromVc.imageInSecond.frame fromView:fromVc.view];
fromVc.imageInSecond.hidden = YES;
// 3. 初始化目标控制器的状态
toVc.view.frame = [transitionContext finalFrameForViewController:toVc];
FirstCellCollectionViewCell *cell = (FirstCellCollectionViewCell *)[toVc.collectionView cellForItemAtIndexPath:toVc.seletedIndexPath];
self.selectedCell = cell;
cell.imageInCell.hidden = YES;
[containerView insertSubview:toVc.view belowSubview:fromVc.view];
[containerView addSubview:sanpShotView];
}
(1)在这里对控制器的view和截图做初始状态的设置,动画不在这里做。
(2)此代理方法在[self.vc.navigationController popViewControllerAnimated:YES]之后调用。
重写 进度更新方法
- (void)updateInteractiveTransition:(CGFloat)percentComplete
{
[super updateInteractiveTransition:percentComplete];
// 1. 淡化当前视图控制器的view
SecondViewController *fromVc = [self fromVc];
fromVc.view.alpha = 1 - percentComplete;
// 2. 随手指移动截图
self.sanpShotView.transform = CGAffineTransformTranslate(self.sanpShotView.transform, self.changePoint.x, self.changePoint.y);
}
(1)控制器view的淡化和截图跟随效果在这里实现。由于是重写系统的方法,注意调用super。
重写 过渡完成方法
- (void)finishInteractiveTransition
{
[super finishInteractiveTransition];
// 截图收缩到被点击的cell的位置
CGRect finialFrame = [self.containerView convertRect:self.selectedCell.imageInCell.frame fromView:self.selectedCell];
[UIView animateWithDuration:self.duration
delay:0
usingSpringWithDamping:0.5
initialSpringVelocity:0.5
options:UIViewAnimationOptionCurveEaseInOut
animations:^{
self.fromVc.view.alpha = 0.0f;
self.sanpShotView.frame = finialFrame;
} completion:^(BOOL finished) {
self.selectedCell.imageInCell.hidden = NO;
[self.sanpShotView removeFromSuperview];
[self.contextTransition completeTransition:YES];
}];
}
(1)过渡完成时需要做的事在这里完成。同时注意调用super。
重写 取消过渡方法
- (void)cancelInteractiveTransition
{
[super cancelInteractiveTransition];
[UIView animateWithDuration:self.duration
delay:0
usingSpringWithDamping:0.5
initialSpringVelocity:0.5
options:UIViewAnimationOptionCurveEaseInOut
animations:^{
self.sanpShotView.transform = CGAffineTransformIdentity;
self.fromVc.view.alpha = 1.0f;
} completion:^(BOOL finished) {
self.fromVc.imageInSecond.hidden = NO;
self.selectedCell.imageInCell.hidden = NO;
[self.sanpShotView removeFromSuperview];
[self.contextTransition completeTransition:NO];
}];
}
(1)手势取消时,一些状态值的恢复在这里进行。注意调用super。