这篇文章主要介绍怎样实现类似系统的测滑返回效果
最终的效果图为:
Demo地址为:
Demo地址
对应的实现文件为:
EightViewController、CustomPopGestureViewController、PopAnimation
关于转场动画,下面这张图将的很清楚
为了实现类似系统的测滑返回效果,实际上就是pop的自定义返回动画,再加上UIPanGestureRecognizer手势的交互实现,也就是交互性转场动画(因为根据手指滑动的距离,大于一半会pop到上一个浏览器,小于一半会恢复原状)
由上面的图可以看到,想要实现交互性转场动画,需要实现UIViewControllerInteractiveTransitioning,
但是系统有给我们提供一个可交互的百分比动画实现类,即UIPerCententDrivenInteractiveTransition,
在接下来的实现中,我们可以直接使用系统提供的实现类,也可以自定义一个遵守UIViewControllerInteractiveTransitioning协议的类(我们是使用了系统提供的)
首先,我们先把pop动画的自定义给完成
在eightViewController中,搭建基本的界面,可以push到CustomPopGestureViewController中
在eightViewController.h中
@property (nonatomic, strong) UIButton *button;
在eightViewController.m中
- (void)viewDidLoad {
[super viewDidLoad];
self.title = @"测滑返回手势测试";
self.navigationController.navigationBar.translucent = NO;
self.view.backgroundColor = [UIColor whiteColor];
self.button.center = self.view.center;
[self.view addSubview:self.button];
}
- (UIButton *)button {
if (_button) {
return _button;
}
_button = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, 100, 50)];
[_button setTitle:@"跳转下一个播放器" forState:UIControlStateNormal];
[_button setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
[_button addTarget:self action:@selector(buttonClicked) forControlEvents:UIControlEventTouchUpInside];
return _button;
}
- (void)buttonClicked {
NSLog(@"点击了跳转按钮");
CustomPopGestureViewController *vc = [[CustomPopGestureViewController alloc] init];
[self.navigationController pushViewController:vc animated:YES];
}
点击按钮,可以push到CustomGestureViewcontroller中
为了实现自定义的测滑返回功能,我们需要禁用系统的测滑返回功能
在CustomViewController中,我们需要添加以下代码禁用
//禁用系统自带的侧滑返回手势
self.navigationController.interactivePopGestureRecognizer.enabled = NO;
接下来,先实现pop的自定义动画
在CustomViewController中,需要遵守UINavigationDelegate协议
self.navigationController.delegate = self;
实现相应的协议方法
- (id<UIViewControllerAnimatedTransitioning>)navigationController:(UINavigationController *)navigationController animationControllerForOperation:(UINavigationControllerOperation)operation fromViewController:(UIViewController *)fromVC toViewController:(UIViewController *)toVC {
if (operation == UINavigationControllerOperationPop) {
NSLog(@"执行了这个方法PopAnimation");
//popAnimation是我们后面定义的类,用来实现具体的动画效果
return [[PopAnimation alloc] init];
}
return nil;
}
接下来,我们自定义一个类PopAnimation,遵守协议UIViewControllerAnimatedTransitioning
在类中实现相应的协议方法
//设置动画执行的时常
- (NSTimeInterval)transitionDuration:(id<UIViewControllerContextTransitioning>)transitionContext {
return 1.0f;
}
//用来处理具体的动画
- (void)animateTransition:(nonnull id<UIViewControllerContextTransitioning>)transitionContext {
UIViewController *fromVC = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
UIViewController *toVC = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
//需要将转场后的界面给加上去才行
// [[transitionContext containerView] addSubview:toVC.view];
[[transitionContext containerView] insertSubview:toVC.view belowSubview:fromVC.view];
NSTimeInterval duration = [self transitionDuration:transitionContext];
CGFloat width = [UIScreen mainScreen].bounds.size.width;
CGFloat height = [UIScreen mainScreen].bounds.size.height;
[UIView animateWithDuration:[self transitionDuration:transitionContext] animations:^{
fromVC.view.frame = CGRectMake(width, 0, width, height);
} completion:^(BOOL finished) {
[transitionContext completeTransition:![transitionContext transitionWasCancelled]];
}];
}
上面实现动画的思路:
我们可以从最终效果图上可以看成:
pop动画的过程就是将上面的view向右挪动,直到移出界面
所以代码中有这一句
[[transitionContext containerView] insertSubview:toVC.view belowSubview:fromVC.view];
将toView的view放在fromVC.view的下面,使用UIview animation来完成移动动画
注意:这里只能使用UIView animation来完成动画,代码中注释的是我最初使用CABasicAnimation来完成移动动画,pop自定义动画可行,但是对于后面使用系统类UIPerCententDrivenInteractiveTransition完成交互不可行,
如果想要使用CaBasicAnimation,必须自定义可交互的百分比动画类
这样我们就完成了pop的自定义动画返回,但是我们需要加入交互,通过判断手指滑动的距离来判断动画的进行程度
接下来,我们需要在CustomPopGestureViewController中,定义相应的手势属性和相应的交互类
@property (nonatomic, strong) UIPanGestureRecognizer *pan;
@property (nonatomic, strong) UIPercentDrivenInteractiveTransition *interactiveTransition;
接下来完成相应的手势类的添加
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
self.title = @"类似系统侧滑返回手势实现";
self.navigationController.navigationBar.translucent = NO;
self.view.backgroundColor = [UIColor yellowColor];
//禁用系统自带的侧滑返回手势
self.navigationController.interactivePopGestureRecognizer.enabled = NO;
[self.view addGestureRecognizer:self.pan];
self.navigationController.delegate = self;
}
- (UIPanGestureRecognizer *)pan {
if (_pan) {
return _pan;
}
_pan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(pan:)];
return _pan;
}
在手势类的出发方法中,我们需要根据UIPanGestureRecognizer的状态来初始化或者更新或者取消或者结束交互类
UIGestureRecognizerStateBegan:手势类刚开始出发时,初始化相应的interactiveTransition
UIGestureRecognizerStateChanged:手势继续滑动的时候,交互类进行相应的更新
UIGestureRecognizerStateEnded:手势结束时,交互类结束
UIGestureRecognizerStateCancelled:手势取消时,交互类取消
最后一个手势周期完成后都要将交互类interactiveTransition置为nil
代码为:
- (void)pan:(UIPanGestureRecognizer *)pan {
if (self.navigationController.childViewControllers.count == 1) {
return;
}
CGPoint point = [pan translationInView:self.view];
CGFloat percent = point.x / self.view.bounds.size.width;
NSLog(@"percent的进度值为:%f",percent);
percent = MIN(MAX(0, percent), 1);
if (pan.state == UIGestureRecognizerStateBegan) {
_interactiveTransition = [[UIPercentDrivenInteractiveTransition alloc] init];
[self.navigationController popViewControllerAnimated:YES];
} else if (pan.state == UIGestureRecognizerStateChanged) {
[_interactiveTransition updateInteractiveTransition:percent];
} else if (pan.state == UIGestureRecognizerStateEnded || pan.state == UIGestureRecognizerStateCancelled) {
//手势结束后,若进度大于0.5就完成pop动画,否则取消
if (percent > 0.5) {
[_interactiveTransition finishInteractiveTransition];
} else {
[_interactiveTransition cancelInteractiveTransition];
}
_interactiveTransition = nil;
}
}
另外,不要忘记,在UINavigationDelegate协议方法中实现可交互类的协议方法
- (id<UIViewControllerInteractiveTransitioning>)navigationController:(UINavigationController *)navigationController interactionControllerForAnimationController:(id<UIViewControllerAnimatedTransitioning>)animationController {
if ([animationController isKindOfClass:[PopAnimation class]]) {
NSLog(@"执行了这个方法UIPercentDrivenInteractiveTransition");
return _interactiveTransition;
}
return nil;
}
最终的效果图为:
总结
最终效果图为:
Demo地址为:
Demo地址
iOS动画效果的探究二:UIView Animation实现动画
iOS动画效果三:CABAsicAnimation实现平移、旋转和放大