美文网首页
iOS push转场圆形扩散效果

iOS push转场圆形扩散效果

作者: YannChee | 来源:发表于2019-08-03 14:22 被阅读0次

这里封装了一个转场manager,可直接使用
使用方法如下:

QYPushCircleExpandTransionManager *manager = [QYPushCircleExpandTransionManager managerWithOriginRect:self.testButton.frame];
self.navigationController.delegate = manager ;
self.manager = manager; // 不能销毁,否则pop会失去动画效果
[self.navigationController pushViewController:vc animated:YES];

QYPushCircleExpandTransionManager.h文件

@interface QYPushCircleExpandTransionManager : NSObject <UINavigationControllerDelegate>
@property (nonatomic, assign) CGRect originRect; /**< 圆形扩散区域的起始frame */

+ (instancetype)managerWithOriginRect:(CGRect)originRect;
@end


@interface QYPushCircleExpandTransionAnimator : NSObject<UIViewControllerAnimatedTransitioning, CAAnimationDelegate>
@property (nonatomic, assign) CGFloat animationDuration; /**< 动画时长 */
@property (nonatomic, assign, readonly) UINavigationControllerOperation  operationType; /**< 转场类型 */
@property (nonatomic, strong, readonly) QYPushCircleExpandTransionManager  *manager;
@property (nonatomic, weak, readonly) id<UIViewControllerContextTransitioning> context;


- (instancetype)initWithOperationType:(UINavigationControllerOperation)type manager:(QYPushCircleExpandTransionManager *)manager;
@end

QYPushCircleExpandTransionManager.m文件


#import "QYPushCircleExpandTransionManager.h"

@implementation QYPushCircleExpandTransionManager

+ (instancetype)managerWithOriginRect:(CGRect)originRect {
    return  [[self alloc] initWithWithOriginRect:originRect];
}

#pragma mark - UINavigationControllerDelegate
- (nullable id <UIViewControllerAnimatedTransitioning>)navigationController:(UINavigationController *)navigationController
                                            animationControllerForOperation:(UINavigationControllerOperation)operation
                                                         fromViewController:(UIViewController *)fromVC
                                                           toViewController:(UIViewController *)toVC {
    
    if (operation == UINavigationControllerOperationNone) {
        return nil;
    }
    
    return [[QYPushCircleExpandTransionAnimator alloc] initWithOperationType:operation manager:self];
}

- (instancetype)initWithWithOriginRect:(CGRect)originRect {
    if (self = [super init]) {
        self.originRect = originRect;
    }
    return self;
}
@end



@interface QYPushCircleExpandTransionAnimator ()
@property (nonatomic, strong) CAShapeLayer  *shapeLayer;
@end
@implementation QYPushCircleExpandTransionAnimator
- (instancetype)initWithOperationType:(UINavigationControllerOperation)type manager:(QYPushCircleExpandTransionManager *)manager {
    if (self = [super init]) {
        self.animationDuration = 0.3f;
        _manager = manager;
        _operationType = type;
    }
    return self;
}
//
#pragma mark - UIViewControllerAnimatedTransitioning, CAAnimationDelegate
// This is used for percent driven interactive transitions, as well as for
// container controllers that have companion animations that might need to
// synchronize with the main animation.
- (NSTimeInterval)transitionDuration:(nullable id <UIViewControllerContextTransitioning>)transitionContext {
    return self.animationDuration;
}
// This method can only  be a nop if the transition is interactive and not a percentDriven interactive transition.
- (void)animateTransition:(id <UIViewControllerContextTransitioning>)transitionContext {
    // 1.转场VC
    UIViewController *fromVC = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
    UIViewController *toVC = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
    // 2.转场view
    UIView *fromView = [transitionContext viewForKey:UITransitionContextFromViewKey];
    UIView *toView = [transitionContext viewForKey:UITransitionContextToViewKey];
    UIView *containerView = [transitionContext containerView];
    // 3.动画
    if (self.operationType == UINavigationControllerOperationNone) {
        return;
    }
    [self animationWithFromVC:fromVC fromView:fromView toVC:toVC toView:toView containerView:containerView context:transitionContext];
}


#pragma mark -  CAAnimationDelegate
- (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag {
    if(!flag) {
        return;
    }
    [[self.context viewControllerForKey:UITransitionContextToViewControllerKey].view.layer setMask:nil];
    [[self.context viewControllerForKey:UITransitionContextFromViewControllerKey].view.layer setMask:nil];
    [self.context completeTransition:![self.context transitionWasCancelled]];
    [self.shapeLayer removeAllAnimations];
    if([self.context transitionWasCancelled]) {
        [[self.context viewForKey:UITransitionContextFromViewKey] removeFromSuperview];
        [[self.context viewForKey:UITransitionContextToViewKey] removeFromSuperview];
    }
}

#pragma mark privateMethod
- (void)animationWithFromVC:(UIViewController *)fromVC fromView:(UIView *)fromView toVC:(UIViewController *)toVC toView:(UIView *)toView containerView:(UIView *)containerView context:(id<UIViewControllerContextTransitioning>)context {
    
    if (self.operationType == UINavigationControllerOperationNone) {
        return;
    }
    _context = context;
    [containerView addSubview:toView];
    
    UIView *animationView = self.operationType == UINavigationControllerOperationPush ? toView : fromView;
    [containerView bringSubviewToFront:animationView];
    CGPoint center = CGPointMake(self.manager.originRect.origin.x + CGRectGetWidth(self.manager.originRect) * 0.5,
                                 self.manager.originRect.origin.y + CGRectGetHeight(self.manager.originRect) * 0.5);
    UIBezierPath *bPath = [UIBezierPath bezierPathWithArcCenter:center
                                                         radius:CGRectGetWidth(self.manager.originRect) * 0.5
                                                     startAngle:0
                                                       endAngle:M_PI * 2
                                                      clockwise:YES];
    UIBezierPath *bPath2 = [UIBezierPath bezierPathWithArcCenter:center
                                                          radius:[self maxRadius:center frame:animationView.frame]
                                                      startAngle:0
                                                        endAngle:M_PI * 2
                                                       clockwise:YES];
    [self addAnimation:bPath targetPath:bPath2 animationView:animationView];
}

- (CGFloat)maxRadius:(CGPoint)center frame:(CGRect)frame {
    CGFloat HSpace = CGRectGetWidth(frame) - center.x;
    CGFloat VSpace = CGRectGetHeight(frame) - center.y;
    return sqrt(pow(MAX(HSpace, center.x), 2) + pow(MAX(center.y, VSpace), 2));
}

- (void)addAnimation:(UIBezierPath *)originPath targetPath:(UIBezierPath *)targetPath animationView:(UIView *)animationView {
    if (self.operationType == UINavigationControllerOperationNone) {
        return;
    }
    CAShapeLayer *shapeLayer = [CAShapeLayer layer];
    self.shapeLayer = shapeLayer;
    CABasicAnimation *baseAnimation = [CABasicAnimation animationWithKeyPath:@"path"];
    baseAnimation.duration = self.animationDuration;
    baseAnimation.delegate = self;
    baseAnimation.removedOnCompletion = NO;
    baseAnimation.fillMode = kCAFillModeForwards;
    if(self.operationType == UINavigationControllerOperationPush) {
        baseAnimation.fromValue = (__bridge id)(originPath.CGPath);
        baseAnimation.toValue = (__bridge id)(targetPath.CGPath);
    } else {
        baseAnimation.toValue = (__bridge id)(originPath.CGPath);
        baseAnimation.fromValue = (__bridge id)(targetPath.CGPath);
    }
    [animationView.layer setMask:shapeLayer];
    [shapeLayer addAnimation:baseAnimation forKey:@"pushAnimation"];
}

@end


相关文章

网友评论

      本文标题:iOS push转场圆形扩散效果

      本文链接:https://www.haomeiwen.com/subject/sgejdctx.html