这里封装了一个转场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
网友评论