iOS 模仿格瓦拉電影的轉場動畫

模仿格瓦拉電影的轉場動畫


demo.gif

自定義轉場動畫

  • 首先就要聲明一個遵守UIViewControllerAnimatedTransitioning協(xié)議的類.
  • 然后實現(xiàn)協(xié)議中的兩個函數
// This is used for percent driven interactive transitions, as well as for container controllers that have companion animations that might need to
// 返回轉場時間
- (NSTimeInterval)transitionDuration:(nullable id <UIViewControllerContextTransitioning>)transitionContext;
// 轉場的動作
- (void)animateTransition:(id <UIViewControllerContextTransitioning>)transitionContext;
  • push和pop都在animateTransition:里面實現(xiàn)烧给,所以需要一個參數表示是push還是pop;還有一個參數表示轉場時間
#import <UIKit/UIKit.h>
#import <Foundation/Foundation.h>


typedef enum : NSUInteger {
    animate_push = 0,
    animate_pop = 1,
    
} Animate_Type;

@interface SFTrainsitionAnimate : NSObject<UIViewControllerAnimatedTransitioning>

- (instancetype)initWithAnimateType:(Animate_Type)type andDuration:(CGFloat)dura;

@property (assign, nonatomic) CGFloat duration;
@property (assign, nonatomic) Animate_Type type;

@end

那么要如何使用新建的對象呢喝噪?可以通過UINavigationControllerDelegate中的

- (id<UIViewControllerAnimatedTransitioning>)navigationController:(UINavigationController *)navigationController animationControllerForOperation:(UINavigationControllerOperation)operation fromViewController:(UIViewController *)fromVC toViewController:(UIViewController *)toVC{
    if (operation == UINavigationControllerOperationPush) {
        self.animate = [[SFTrainsitionAnimate alloc] init];
        return self.animate;
    }else{
        return nil;
    }
    
}

當然础嫡,要調用這個方法還得先把UINavigationControllerDelegate委托給視圖控制器

self.navigationController.delegate = self;

再來看看UIViewControllerAnimatedTransitioning這個協(xié)議,返回時間的方法就不介紹了酝惧。重點在

- (void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext{
    //起始視圖控制器
    UIViewController *fromVC = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
    //目標視圖控制器
    UIViewController *toVC = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
    //在這個視圖上實現(xiàn)跳轉動畫
    UIView *containView = [transitionContext containerView];
}

如上注釋榴鼎,我們可以通過參數transitionContext獲取到起始和目標控制。并在containView這個視圖實現(xiàn)我們想要實現(xiàn)的動畫晚唇。


接下來就是轉場動畫的實現(xiàn)了,push動畫的流程大概就是:點擊第一個頁面的一個控件巫财,模擬出控件然后移動到第二個界面同一個樣式控件的位置,之后再把第二個界面以控件的位置中心擴散顯示出來哩陕。

  • 那么現(xiàn)在我們就在協(xié)議方法中通過fromVC獲取到第一個視圖的控件翁涤,并制造一個鏡像視圖移動到toVC的目標控件的位置桥言。在這里,我調用了UIViewcontroller分類關聯(lián)一個對象葵礼,用來記錄控件(非擁有關系)号阿。
#import <UIKit/UIKit.h>
#import <Foundation/Foundation.h>

@interface UIViewController (SFTrainsitionExtension)

@property (assign, nonatomic) CGFloat sf_targetHeight;//灰白背景的分割線高度
@property (weak  , nonatomic) UIView *sf_targetView;

@end

產生targetView鏡像:

//產生targetView鏡像
- (UIView *)customSnapshoFromView:(UIView *)inputView {
    
    // Make an image from the input view.
    UIGraphicsBeginImageContextWithOptions(inputView.bounds.size, NO, 0);
    [inputView.layer renderInContext:UIGraphicsGetCurrentContext()];
    UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    
    // Create an image view.
    UIView *snapshot = [[UIImageView alloc] initWithImage:image];
    snapshot.layer.masksToBounds = NO;
    snapshot.layer.cornerRadius = 0.0;
    snapshot.layer.shadowOffset = CGSizeMake(0.0, 0.0);
    snapshot.layer.shadowRadius = 5.0;
    snapshot.layer.shadowOpacity = 0.4;
    
    return snapshot;
}
  • 現(xiàn)在就可以制作移動的動畫:
//起始位置
    CGRect originFrame = [fromVC.sf_targetView convertRect:fromVC.sf_targetView.bounds toView:fromVC.view];
    //動畫移動的視圖鏡像
    UIView *customView = [self customSnapshoFromView:fromVC.sf_targetView];
    customView.frame = originFrame;
    
    //移動的目標位置
    CGRect finishFrame = [toVC.sf_targetView convertRect:toVC.sf_targetView.bounds toView:toVC.view];
    
    UIView *containView = [transitionContext containerView];
    
    //背景視圖 灰色高度
    CGFloat height = CGRectGetMidY(finishFrame);
    toVC.sf_targetHeight = height;
    
    //背景視圖 灰色
    UIView *backgray = [[UIView alloc] initWithFrame:CGRectMake(0, 0, k_SF_SCREEN_WIDTH, k_SF_SCREEN_HIGHT)];
    backgray.backgroundColor = [UIColor lightGrayColor];
    //背景視圖  白色
    UIView *backwhite = [[UIView alloc] initWithFrame:CGRectMake(0, height, k_SF_SCREEN_HIGHT, k_SF_SCREEN_HIGHT-height)];
    backwhite.backgroundColor = [UIColor whiteColor];
    
    toVC.view.frame = [transitionContext finalFrameForViewController:toVC];
    
    //注意添加順序
    [containView addSubview:toVC.view];
    [containView addSubview:backgray];
    [backgray addSubview:backwhite];
    [containView addSubview:customView];
 
    //動畫
    [UIView animateWithDuration:_duration/3 animations:^{
        customView.frame = finishFrame;
        customView.transform = CGAffineTransformMakeScale(1.1, 1.1);
    } completion:^(BOOL finished) {
        if (finished) {
            
            [UIView animateWithDuration:_duration/3 animations:^{
                
                customView.transform = CGAffineTransformIdentity;
                
            } completion:^(BOOL finished) {
                if (finished) {
                    [UIView animateWithDuration:_duration/3 animations:^{
                        customView.alpha = 0.0;
                    } completion:^(BOOL finished) {
                        if (finished) {
                            [backgray removeFromSuperview];
                            [customView removeFromSuperview];
                            [transitionContext completeTransition:YES];
                        }
                    }];
                    
                    [self addPathAnimateWithView:backgray fromPoint:customView.center];
                    
                }
            }];
  • 移動完之后,要以圓形擴散顯示出push之后的界面鸳粉。就可以通過UIBezierPath和CAShapeLayer來實現(xiàn)扔涧。UIBezierPath表示路徑,CAShapeLayer可以根據路徑來顯示區(qū)域届谈,那么我們可以以第一個界面的視圖先看看效果枯夜。
    UIBezierPath *path = [UIBezierPath bezierPathWithRect:self.collectionView.bounds];

    [path appendPath:[UIBezierPath bezierPathWithArcCenter:self.collectionView.center radius:50 startAngle:0 endAngle:2*M_PI clockwise:NO]];
    
    CAShapeLayer *layer = [CAShapeLayer layer];
    layer.path = path.CGPath;
    self.collectionView.layer.mask = layer;

初始化path路徑以collectionView的四邊畫一個路徑,然后加入一個以collectionView的中心為圓點艰山,半徑為50的路徑湖雹,顯示的區(qū)域就為兩個路徑之間的區(qū)域。

123.png

然后再把代碼中的radius大小設為200

456.png

現(xiàn)在曙搬,我們創(chuàng)建一個CABasicAnimation對象去完成擴散的動畫摔吏,起始位置是半徑為10的圓,終點位置是半徑為200的圓.

    UIBezierPath *path = [UIBezierPath bezierPathWithRect:self.collectionView.bounds];
    
    [path appendPath:[UIBezierPath bezierPathWithArcCenter:self.collectionView.center radius:10 startAngle:0 endAngle:2*M_PI clockwise:NO]];
    
    UIBezierPath *path2 = [UIBezierPath bezierPathWithRect:self.collectionView.bounds];
    
    [path2 appendPath:[UIBezierPath bezierPathWithArcCenter:self.collectionView.center radius:200 startAngle:0 endAngle:2*M_PI clockwise:NO]];
    
    CAShapeLayer *layer = [CAShapeLayer layer];
    self.collectionView.layer.mask = layer;
    
    CABasicAnimation *pathAnimation = [CABasicAnimation animationWithKeyPath:@"path"];
    pathAnimation.fromValue =  (__bridge id)path.CGPath;
    pathAnimation.toValue   =  (__bridge id)path2.CGPath;
    pathAnimation.duration  = 1.0;
    pathAnimation.repeatCount = 1;
    pathAnimation.removedOnCompletion = NO;
    pathAnimation.fillMode = kCAFillModeForwards;
    pathAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut];
    [layer addAnimation:pathAnimation forKey:@"pathAnimate"];
789.gif

現(xiàn)在就可以完成push的 轉場效果了纵装。注意圓圈白色部分顯示的是collectionView底部的self.view的視圖征讲。

//加入收合動畫
- (void)addPathAnimateWithView:(UIView *)toView fromPoint:(CGPoint)point{
    //create path
    UIBezierPath *path = [UIBezierPath bezierPathWithRect:CGRectMake(0, 0, k_SF_SCREEN_WIDTH, k_SF_SCREEN_HIGHT)];
    //create path
    [path appendPath:[UIBezierPath bezierPathWithArcCenter:point radius:0.1 startAngle:0 endAngle:2*M_PI clockwise:NO]];
    
    CGFloat radius = point.y > 0?k_SF_SCREEN_HIGHT*3/4: k_SF_SCREEN_HIGHT*3/4-point.y;
    UIBezierPath *path2 = [UIBezierPath bezierPathWithRect:CGRectMake(0, 0, k_SF_SCREEN_WIDTH, k_SF_SCREEN_HIGHT)];
    [path2 appendPath:[UIBezierPath bezierPathWithArcCenter:point radius:radius startAngle:0 endAngle:2*M_PI clockwise:NO]];
    
    CAShapeLayer *shapeLayer = [CAShapeLayer layer];
    //shapeLayer.path = path.CGPath;
    toView.layer.mask = shapeLayer;
    
    CABasicAnimation *pathAnimation = [CABasicAnimation animationWithKeyPath:@"path"];
    pathAnimation.fromValue = _type == animate_push? (__bridge id)path.CGPath:(__bridge id)path2.CGPath;
    pathAnimation.toValue   = _type == animate_push? (__bridge id)path2.CGPath:(__bridge id)path.CGPath;
    pathAnimation.duration  = _duration/3;
    pathAnimation.repeatCount = 1;
    pathAnimation.removedOnCompletion = NO;
    pathAnimation.fillMode = kCAFillModeForwards;
    pathAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut];
    [shapeLayer addAnimation:pathAnimation forKey:@"pathAnimate"];
}

pop動畫其實和push差不多,這里就不說了橡娄。

github地址:點這里

參考博客:點這里

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末诗箍,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子挽唉,更是在濱河造成了極大的恐慌滤祖,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,464評論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件瓶籽,死亡現(xiàn)場離奇詭異氨距,居然都是意外死亡,警方通過查閱死者的電腦和手機棘劣,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,033評論 3 399
  • 文/潘曉璐 我一進店門俏让,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人茬暇,你說我怎么就攤上這事首昔。” “怎么了糙俗?”我有些...
    開封第一講書人閱讀 169,078評論 0 362
  • 文/不壞的土叔 我叫張陵勒奇,是天一觀的道長。 經常有香客問我巧骚,道長赊颠,這世上最難降的妖魔是什么格二? 我笑而不...
    開封第一講書人閱讀 59,979評論 1 299
  • 正文 為了忘掉前任,我火速辦了婚禮竣蹦,結果婚禮上顶猜,老公的妹妹穿的比我還像新娘。我一直安慰自己痘括,他們只是感情好长窄,可當我...
    茶點故事閱讀 69,001評論 6 398
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著纲菌,像睡著了一般挠日。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上翰舌,一...
    開封第一講書人閱讀 52,584評論 1 312
  • 那天嚣潜,我揣著相機與錄音,去河邊找鬼椅贱。 笑死懂算,一個胖子當著我的面吹牛,可吹牛的內容都是我干的夜涕。 我是一名探鬼主播,決...
    沈念sama閱讀 41,085評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼属愤,長吁一口氣:“原來是場噩夢啊……” “哼女器!你這毒婦竟也來了?” 一聲冷哼從身側響起住诸,我...
    開封第一講書人閱讀 40,023評論 0 277
  • 序言:老撾萬榮一對情侶失蹤驾胆,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后贱呐,有當地人在樹林里發(fā)現(xiàn)了一具尸體丧诺,經...
    沈念sama閱讀 46,555評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 38,626評論 3 342
  • 正文 我和宋清朗相戀三年奄薇,在試婚紗的時候發(fā)現(xiàn)自己被綠了驳阎。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,769評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡馁蒂,死狀恐怖呵晚,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情沫屡,我是刑警寧澤饵隙,帶...
    沈念sama閱讀 36,439評論 5 351
  • 正文 年R本政府宣布,位于F島的核電站沮脖,受9級特大地震影響金矛,放射性物質發(fā)生泄漏芯急。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 42,115評論 3 335
  • 文/蒙蒙 一驶俊、第九天 我趴在偏房一處隱蔽的房頂上張望娶耍。 院中可真熱鬧,春花似錦废睦、人聲如沸伺绽。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,601評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽奈应。三九已至,卻和暖如春购披,著一層夾襖步出監(jiān)牢的瞬間杖挣,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,702評論 1 274
  • 我被黑心中介騙來泰國打工刚陡, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留惩妇,地道東北人。 一個月前我還...
    沈念sama閱讀 49,191評論 3 378
  • 正文 我出身青樓筐乳,卻偏偏與公主長得像歌殃,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子蝙云,可洞房花燭夜當晚...
    茶點故事閱讀 45,781評論 2 361

推薦閱讀更多精彩內容

  • 前言的前言 唐巧前輩在微信公眾號「iOSDevTips」以及其博客上推送了我的文章后氓皱,我的 Github 各項指標...
    VincentHK閱讀 5,379評論 3 44
  • WebSocket-Swift Starscream的使用 WebSocket 是 HTML5 一種新的協(xié)議。它實...
    香橙柚子閱讀 23,921評論 8 183
  • 發(fā)現(xiàn) 關注 消息 iOS 第三方庫勃刨、插件波材、知名博客總結 作者大灰狼的小綿羊哥哥關注 2017.06.26 09:4...
    肇東周閱讀 12,124評論 4 61
  • 七月的天氣很燥熱,落榜的我跟著爸媽的腳步走進了再次奮斗的戰(zhàn)場身隐。對于這次高考廷区,我心有不甘。贾铝。隙轻。也因為此次失敗我才相...
    WAHZBB閱讀 294評論 0 0
  • 2007年的一天晚上大脉,電視上一名叫做胡雪峰的控球后衛(wèi)用他的運球,三分水孩,一人瀟灑砍下36分镰矿,10年過去了,20歲的我...
    顧米夕77閱讀 270評論 0 2