先看動畫效果:
間客的APP上之前是有這個動畫的,拜讀了大神的文章:iOS自定義轉(zhuǎn)場詳解03——實現(xiàn)通過圓圈放大縮小的轉(zhuǎn)場動畫,博客里面很詳細蛤织。
因為要自定義轉(zhuǎn)場,所以我們需要一個新的對象集成NSObject,并且遵守轉(zhuǎn)場動畫的協(xié)議,
UIViewControllerAnimatedTransitioning
介紹個知識點:
1儿礼、CGRectInsetCGRect
CGRectInset (
CGRect rect,
CGFloat dx,
CGFloat dy);
該結(jié)構(gòu)體的應(yīng)用是以原rect為中心,再參考dx庆寺,dy蚊夫,進行縮放或者放大。
2懦尝、CGRectOffsetCGRect
CGRectOffset(
CGRect rect,
CGFloat dx,
CGFloat dy);
相對于源矩形原點rect(左上角的點)沿x軸和y軸偏移, 再rect基礎(chǔ)上沿x軸和y軸偏移
// 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.
設(shè)置轉(zhuǎn)場動畫的時間知纷。
- (NSTimeInterval)transitionDuration:(nullable id <UIViewControllerContextTransitioning>)transitionContext;
// This method can only be a nop if the transition is interactive and not a percentDriven interactive transition.
轉(zhuǎn)場的上下文
- (void)animateTransition:(id <UIViewControllerContextTransitioning>)transitionContext;
關(guān)于這個參數(shù)transitionContext壤圃, 該參數(shù)是一個實現(xiàn)了 UIViewControllerContextTransitioning可以讓我們訪問一些實現(xiàn)過渡所必須的對象。
UIViewControllerContextTransitioning 協(xié)議中有一些方法:
- (UIView *)containerView;
//轉(zhuǎn)場動畫發(fā)生的容器 - (UIViewController *)viewControllerForKey:(NSString *)key;
// 我們可以通過它拿到過渡的兩個 ViewController琅轧。
大致思路是這樣從伍绳,我們畫兩個內(nèi)塞爾曲線的圓,第一個小圓的frame和右上角圓形按鈕的大小一樣乍桂,第二個大圓則是覆蓋了整個屏幕冲杀。然后,去設(shè)置view.layer.mask屬性睹酌,讓這個mask從小圓動畫到大圓权谁。
以push 動畫為例:pop動畫不過是起終點的mask路徑相反罷了。
- (void)animateTransition:(id <UIViewControllerContextTransitioning>)transitionContext{
ViewController * fromVC = (ViewController *)[transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
DetailViewController *toVC = (DetailViewController *)[transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
UIView *contView = [transitionContext containerView];
UIButton *button = fromVC.button;
UIBezierPath *maskStartBP = [UIBezierPath bezierPathWithOvalInRect:button.frame];
[contView addSubview:fromVC.view];
[contView addSubview:toVC.view];
//創(chuàng)建兩個圓形的 UIBezierPath 實例憋沿;一個是 button 的 size 旺芽,另外一個則擁有足夠覆蓋屏幕的半徑。最終的動畫則是在這兩個貝塞爾路徑之間進行的
CGPoint finalPoint;
//判斷觸發(fā)點在那個象限
if(button.frame.origin.x > (toVC.view.bounds.size.width / 2)){
if (button.frame.origin.y < (toVC.view.bounds.size.height / 2)) {
//第一象限
finalPoint = CGPointMake(button.center.x - 0, button.center.y - CGRectGetMaxY(toVC.view.bounds)+30);
}else{
//第四象限
finalPoint = CGPointMake(button.center.x - 0, button.center.y - 0);
}
}else{
if (button.frame.origin.y < (toVC.view.bounds.size.height / 2)) {
//第二象限
finalPoint = CGPointMake(button.center.x - CGRectGetMaxX(toVC.view.bounds), button.center.y - CGRectGetMaxY(toVC.view.bounds)+30);
}else{
//第三象限
finalPoint = CGPointMake(button.center.x - CGRectGetMaxX(toVC.view.bounds), button.center.y - 0);
}
}
CGFloat radius = sqrt((finalPoint.x * finalPoint.x) + (finalPoint.y * finalPoint.y));
UIBezierPath *maskFinalBP = [UIBezierPath bezierPathWithOvalInRect:CGRectInset(button.frame, -radius, -radius)];
//創(chuàng)建一個 CAShapeLayer 來負責展示圓形遮蓋
CAShapeLayer *maskLayer = [CAShapeLayer layer];
maskLayer.path = maskFinalBP.CGPath; //將它的 path 指定為最終的 path 來避免在動畫完成后會回彈
toVC.view.layer.mask = maskLayer;
maskLayer.backgroundColor =[UIColor redColor].CGColor;
CABasicAnimation *maskLayerAnimation = [CABasicAnimation animationWithKeyPath:@"path"];
maskLayerAnimation.fromValue = (__bridge id)(maskStartBP.CGPath);
maskLayerAnimation.toValue = (__bridge id)((maskFinalBP.CGPath));
maskLayerAnimation.duration = [self transitionDuration:transitionContext];
maskLayerAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
maskLayerAnimation.delegate = self;
[maskLayer addAnimation:maskLayerAnimation forKey:@"path"];
```
結(jié)束動畫后設(shè)置:
pragma mark - CABasicAnimation的Delegate
-
(void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag{
//告訴 iOS 這個 transition 完成
[self.transitionContext completeTransition:![self. transitionContext transitionWasCancelled]];
//清除 fromVC 的 mask
[self.transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey].view.layer.mask = nil;
[self.transitionContext viewControllerForKey:UITransitionContextToViewControllerKey].view.layer.mask = nil;
}
>自定義完push 動畫后我們需要進行設(shè)置卤妒,因為所有的子控制器由UINavgationController進行管理甥绿,所有我們需要在ViewCotroller中實現(xiàn)代理;
@interface ViewController ()<UINavigationControllerDelegate>
最好在`viewWillAppear`中設(shè)置代理
-(void)viewWillAppear:(BOOL)animated{
self.navigationController.delegate = self;
}
實現(xiàn)代理:
pragma mark - UINavigationControllerDelegate
-
(id <UIViewControllerAnimatedTransitioning>)navigationController:(UINavigationController *)navigationController
animationControllerForOperation:(UINavigationControllerOperation)operation
fromViewController:(UIViewController *)fromVC
toViewController:(UIViewController *)toVC{
if (operation == UINavigationControllerOperationPush) {PingTransition *ping = [PingTransition new]; return ping;
}else{
return nil;
}
}
本文demo地址:[圓圈轉(zhuǎn)場動畫](https://github.com/liuxinixn/CircleZoomView/tree/master)