转场动画:
12.29
自己写过一个转场动画以后,感觉这个转场动画设计的很好,逻辑清晰,自由度也比较大,而且实现起来也比较容易。
一、present 动画
问题:
1、为什么在+ animateTransition
方法中,一定要自己调用
[containerview addSubview:toVc.view]
。
这个有点想不明白,不是应该自己添加的吗??
我在第一次尝试的时候,忘记把toVc.view 添加到上面去,结果虽然最后调用了[transitionContext completeTransition:YES];
,但是还是transationView 还在视图中,造成界面不能够再操作。
现在想想,自己添加其实是有了更多的自由度,可以用来管理什么时候再把新的toVc.view 添加到容器上。
自定义转场动画的实现:
1、在要实现自定义转场动画的viewController 的初始化方法里面里面设置转场动画的代理和style:
self.transitioningDelegate = self;
self.modalPresentationStyle = UIModalPresentationCustom;
2、实现转场动画的代理方法。 这里只实现了最基本的两个方法,即present 和dismiss动画。另外还有手势操作和动画被打断时的的代理方法。
这两个代理方法都返回一个遵守<UIViewControllerAnimatedTransitioning>协议的对象,我这里是 CircleTransitionAnimation
类。
- (nullable id <UIViewControllerAnimatedTransitioning>)animationControllerForPresentedController:(UIViewController *)presented presentingController:(UIViewController *)presenting sourceController:(UIViewController *)source
{
return [CircleTransitionAnimation transitioningWithType:CircleTransitionAnimationTypePresent];
}
- (nullable id <UIViewControllerAnimatedTransitioning>)animationControllerForDismissedController:(UIViewController *)dismissed
{
return [CircleTransitionAnimation transitioningWithType:CircleTransitionAnimationTypeDismiss];
}
3、自定义遵守<UIViewControllerAnimatedTransitioning>协议的类,我这里是 CircleTransitionAnimation类。
在这个类中实现相关协议方法来完成转场动画:
#pragma mark UIViewControllerTransitioning
//这个方法返回转场动画的持续时长
- (NSTimeInterval)transitionDuration:(nullable id <UIViewControllerContextTransitioning>)transitionContext;
{
return 0.5;
}
//这个方法是 转场时要执行的动画,我把代码分到两个私有方法里面了
- (void)animateTransition:(id <UIViewControllerContextTransitioning>)transitionContext{
//animation
switch (_type) {
case CircleTransitionAnimationTypePresent:
[self presentAnimationWithContext:transitionContext];
break;
case CircleTransitionAnimationTypeDismiss:
[self dismissAnimationWithContext:transitionContext];
default:
break;
}
}
// This is a convenience and if implemented will be invoked by the system when the transition context's completeTransition: method is invoked.
- (void)animationEnded:(BOOL) transitionCompleted
{
NSLog(@"animation ended");
}
#pragma mark CAAnimationDelegate
- (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag
{
NSLog(@"animationDidStop");
switch (_type) {
case CircleTransitionAnimationTypePresent:{
NSLog(@"animationDidStop:present");
id<UIViewControllerContextTransitioning> transitionContext = [anim valueForKey:@"transitionContext"];
[transitionContext completeTransition:YES];
NSLog(@"%@",[transitionContext viewControllerForKey:UITransitionContextToViewKey].view);
//这边为什么取不到 view???
[transitionContext viewControllerForKey:UITransitionContextToViewKey].view.layer.mask = nil;
}
break;
case CircleTransitionAnimationTypeDismiss:{
NSLog(@"animationDidStop:dismiss");
id<UIViewControllerContextTransitioning> transitionContext = [anim valueForKey:@"transitionContext"];
[transitionContext completeTransition:![transitionContext transitionWasCancelled]];
if ([transitionContext transitionWasCancelled]) {
[transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey].view.layer.mask = nil;
}
}
break;
}
}
#pragma mark - custom delegates
#pragma mark - event responses
#pragma mark - public methods
+ (instancetype)transitioningWithType:(CircleTransitionAnimationType)type
{
CircleTransitionAnimation *transitioning = [[self alloc]init];
transitioning.type = type;
return transitioning;
}
#pragma mark - private methods
- (void)presentAnimationWithContext:(id <UIViewControllerContextTransitioning>)transitionContext
{
//vc
ViewController *fromVc = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
UIViewController *toVc = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
UIView *containerView = [transitionContext containerView];
[containerView addSubview:toVc.view];
//圆形放大动画
//mask
UIBezierPath *startPath = [UIBezierPath bezierPathWithOvalInRect:fromVc.Btnframe];
float radius = sqrt(kScreenWidth * kScreenWidth + kScreenHeight * kScreenHeight) / 2.0;
//没理解
// CGFloat x = MAX(fromVc.Btnframe.origin.x, containerView.frame.size.width - fromVc.Btnframe.origin.x);
// CGFloat y = MAX(fromVc.Btnframe.origin.y, containerView.frame.size.height - fromVc.Btnframe.origin.y);
// CGFloat radius = sqrtf(pow(x, 2) + pow(y, 2));
UIBezierPath *endPath = [UIBezierPath bezierPathWithArcCenter:containerView.center radius:radius startAngle:0 endAngle:M_PI * 2 clockwise:YES];
CAShapeLayer *maskLayer = [CAShapeLayer new];
maskLayer.path = endPath.CGPath;
maskLayer.fillColor = [UIColor greenColor].CGColor;
toVc.view.layer.mask = maskLayer;
//animation
CAKeyframeAnimation *animation = [CAKeyframeAnimation animationWithKeyPath:@"path"];
animation.values = @[(__bridge id)startPath.CGPath,(__bridge id)endPath.CGPath];
animation.duration = [self transitionDuration:transitionContext];
animation.keyTimes = @[@1];
animation.beginTime = CACurrentMediaTime();
animation.removedOnCompletion = YES;
animation.delegate = self;
[animation setValue:transitionContext forKey:@"transitionContext"];
[maskLayer addAnimation:animation forKey:@"path"];
}
- (void)dismissAnimationWithContext:(id <UIViewControllerContextTransitioning>)transitionContext
{
UIViewController *fromVC = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
UINavigationController *toVC = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
ViewController *temp = toVC;
UIView *containerView = [transitionContext containerView];
//画两个圆路径
CGFloat radius = sqrtf(containerView.frame.size.height * containerView.frame.size.height + containerView.frame.size.width * containerView.frame.size.width) / 2;
UIBezierPath *startCycle = [UIBezierPath bezierPathWithArcCenter:containerView.center radius:radius startAngle:0 endAngle:M_PI * 2 clockwise:YES];
UIBezierPath *endCycle = [UIBezierPath bezierPathWithOvalInRect:temp.Btnframe];
//创建CAShapeLayer进行遮盖
CAShapeLayer *maskLayer = [CAShapeLayer layer];
maskLayer.fillColor = [UIColor greenColor].CGColor;
maskLayer.path = endCycle.CGPath;
fromVC.view.layer.mask = maskLayer;
//创建路径动画
CABasicAnimation *maskLayerAnimation = [CABasicAnimation animationWithKeyPath:@"path"];
maskLayerAnimation.delegate = self;
maskLayerAnimation.fromValue = (__bridge id)(startCycle.CGPath);
maskLayerAnimation.toValue = (__bridge id)((endCycle.CGPath));
maskLayerAnimation.duration = [self transitionDuration:transitionContext];
maskLayerAnimation.delegate = self;
maskLayerAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
[maskLayerAnimation setValue:transitionContext forKey:@"transitionContext"];
[maskLayer addAnimation:maskLayerAnimation forKey:@"path"];
}
这样就完成了一个简单的转场动画,最终的效果图如下:
CircleTransitioning.gif
网友评论