登錄動畫

 

LoginViewController.h

#import <UIKit/UIKit.h>

@interface LoginViewController : UIViewController

@property (nonatomic,strong) UIImageView * LoginImage;  // logo圖
@property (nonatomic,strong) UILabel     * LoginWord;   // logo下面的文字
@property (nonatomic,strong) UIButton    * GetButton;   // get按鈕

@property (nonatomic,strong) UITextField * userTextField;     // 帳號輸入框
@property (nonatomic,strong) UITextField * passwordTextField; // 密碼輸入框
@property (nonatomic,strong) UIButton    * LoginButton;       // login按鈕

@property (nonatomic,strong) UIView      * HUDView;       // 登陸時加一個看不見的蒙版,讓控件不能再被點擊
@property (nonatomic,strong) UIView      * LoginAnimView; // 執行登陸按鈕動畫的view (動畫效果不是按鈕自己,而是這個view)

@property (nonatomic,strong) CAShapeLayer * shapeLayer; // 登陸轉圈的那條白線所在的layer
@property (nonatomic,strong) UIView       * animView;   // get按鈕動畫view



- (void)reloadView;

@end
LoginViewController.m


#import "LoginViewController.h"
#import "POP.h"
#import "Masonry.h"
#import "UIView+YYExtension.h"
#import "ViewController.h"
#import "LoginTranslation.h"


#define UIColorFromRGB(rgbValue) \
[UIColor colorWithRed:((float)((rgbValue & 0xFF0000) >> 16))/255.0 green:((float)((rgbValue & 0xFF00) >> 8))/255.0 blue:((float)(rgbValue & 0xFF))/255.0 alpha:1.0]
#define BG_COLOR UIColorFromRGB(0xefeff4)

#define ScreenW [UIScreen mainScreen].bounds.size.width
#define ScreenH [UIScreen mainScreen].bounds.size.height

#define ButtonColor [UIColor colorWithRed:156/255.0 green:197/255.0 blue:251/255.0 alpha:1.0]

static CGFloat const springSpeed = 6.0;
static CGFloat const springBounciness = 16.0;

@interface LoginViewController () <CAAnimationDelegate, UIViewControllerTransitioningDelegate>
// 轉場動畫管理對象()
@property (nonatomic,strong) LoginTranslation * loginTranslation;
@end

@implementation LoginViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    [self SetupUIComponent];
}

#pragma mark - 懶加載

- (LoginTranslation *)loginTranslation {
    if (!_loginTranslation)
    {
        _loginTranslation = [[LoginTranslation alloc] init];
    }
    return _loginTranslation;
}

- (UIImageView *)LoginImage {
    if (!_LoginImage)
    {
        _LoginImage = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"logo.png"]];
        [self.view addSubview:_LoginImage];
    }
    return _LoginImage;
}

- (UILabel *)LoginWord {
    if (!_LoginWord)
    {
        _LoginWord = [[UILabel alloc] init];
        [self.view addSubview:_LoginWord];
        _LoginWord.font =  [UIFont fontWithName:@"TimesNewRomanPS-ItalicMT" size:34.0f];
        _LoginWord.textColor = [UIColor blackColor];
        _LoginWord.text = @"YY Anim Demo";
        [_LoginWord sizeToFit];
    }
    return _LoginWord;
}

- (UIButton *)GetButton {
    if (!_GetButton)
    {
        _GetButton = [UIButton buttonWithType:UIButtonTypeCustom];
        [self.view addSubview:_GetButton];
        [_GetButton.layer setMasksToBounds:YES];
        [_GetButton.layer setCornerRadius:22.0];
        [_GetButton setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
        [_GetButton setTitle:@"GET" forState:UIControlStateNormal];
        _GetButton.backgroundColor = ButtonColor;
        [_GetButton addTarget:self action:@selector(GetButtonClick) forControlEvents:UIControlEventTouchUpInside];
    }
    return _GetButton;
}



- (UITextField *)userTextField {
    if(_userTextField == nil) {
        _userTextField = [[UITextField alloc] init];
        _userTextField.font = [UIFont systemFontOfSize:15];
        _userTextField.placeholder = @"Username";
        _userTextField.alpha = 0.0;
        [_userTextField setValue:UIColorFromRGB(0xcccccc) forKeyPath:@"_placeholderLabel.textColor"];
        [_userTextField setValue:[UIFont systemFontOfSize:15.0] forKeyPath:@"_placeholderLabel.font"];
        _userTextField.textAlignment = NSTextAlignmentCenter;
        _userTextField.keyboardType = UIKeyboardTypePhonePad;
        _userTextField.clearButtonMode = UITextFieldViewModeWhileEditing;
        _userTextField.tintColor = ButtonColor;
        
        UIView *seperatorLine = [[UIView alloc] init];
        [_userTextField addSubview:seperatorLine];
        seperatorLine.backgroundColor = UIColorFromRGB(0xe1e1e1);
        [seperatorLine mas_makeConstraints:^(MASConstraintMaker *make) {
            make.left.right.bottom.mas_equalTo(_userTextField);
            make.height.mas_equalTo(1.5);
        }];
        
        [self.view addSubview:_userTextField];
        _userTextField.frame = CGRectMake(ScreenW * 0.2, ScreenH * 0.7-(ScreenH * 0.3 - 44) * 0.5 - 75 + 25, ScreenW * 0.6, 50);
    }
    return _userTextField;
}


- (UITextField *)passwordTextField {
    if(_passwordTextField == nil) {
        _passwordTextField = [[UITextField alloc] init];
        _passwordTextField.font = [UIFont systemFontOfSize:15];
        _passwordTextField.borderStyle = UITextBorderStyleNone;
        _passwordTextField.placeholder = @"Password";
        _passwordTextField.alpha = 0.0;
        [_passwordTextField setValue:UIColorFromRGB(0xcccccc) forKeyPath:@"_placeholderLabel.textColor"];
        [_passwordTextField setValue:[UIFont systemFontOfSize:15.0] forKeyPath:@"_placeholderLabel.font"];
        _passwordTextField.textAlignment = NSTextAlignmentCenter;
        _passwordTextField.secureTextEntry = YES;
        _passwordTextField.tintColor = ButtonColor;
        
        UIView *seperatorLine = [[UIView alloc] init];
        [_passwordTextField addSubview:seperatorLine];
        seperatorLine.backgroundColor = UIColorFromRGB(0xe1e1e1);
        [seperatorLine mas_makeConstraints:^(MASConstraintMaker *make) {
            make.left.right.bottom.mas_equalTo(_passwordTextField);
            make.height.mas_equalTo(1.5);
        }];
        
        [self.view addSubview:_passwordTextField];
        _passwordTextField.frame = CGRectMake(ScreenW * 0.2, ScreenH * 0.7 - (ScreenH * 0.3 - 44) * 0.5 - 75 + 10 + 50 + 25, ScreenW * 0.6, 50);
    }
    return _passwordTextField;
}

- (UIButton *)LoginButton {
    if (!_LoginButton){
        _LoginButton = [UIButton buttonWithType:UIButtonTypeCustom];
        [self.view addSubview:_LoginButton];
        _LoginButton.frame = CGRectMake(0, 0, 0, 0);
        _LoginButton.hidden = YES;
        [_LoginButton.layer setMasksToBounds:YES];
        [_LoginButton.layer setCornerRadius:5.0];
        [_LoginButton setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
        [_LoginButton setTitle:@"LOGIN" forState:UIControlStateNormal];
        _LoginButton.backgroundColor = ButtonColor;
        [_LoginButton addTarget:self action:@selector(LoginButtonClick) forControlEvents:UIControlEventTouchUpInside];
    }
    return _LoginButton;
}

/** 初始化UI */

- (void)SetupUIComponent {
    self.view.backgroundColor = BG_COLOR;
    
    // 文字佈局
    self.LoginWord.yy_centerX = self.view.yy_centerX;
    self.LoginWord.yy_y = self.view.yy_centerY-self.LoginWord.yy_height;
    
    // logo佈局
    CGFloat LoginImageWH = ScreenW * 0.25;
    self.LoginImage.frame = CGRectMake((ScreenW - LoginImageWH) * 0.5, CGRectGetMinY(self.LoginWord.frame) - 40 -LoginImageWH, LoginImageWH, LoginImageWH);
    
    // 按鈕佈局
    CGFloat GetButtonW = ScreenW * 0.4;
    CGFloat GetButtonH = 44;
    self.GetButton.frame = CGRectMake((ScreenW - GetButtonW) * 0.5, ScreenH * 0.7, GetButtonW, GetButtonH);
}

#pragma mark - get按鈕點擊事件——執行動畫

- (void)GetButtonClick {
    
    /**
     *  動畫的思路:
     *  一、造一個view來執行動畫,看上去就像get按鈕自己在形變移動,實際上是這個view
     *  二、改變更畫view的背景顏色,變色的過程是整個動畫效果執行的過程
     *  三、讓按鈕變寬
     *  四、變寬完成後,變高
     *  五、變高完成後,同步執行如下四步
     *      5.0、讓帳號密碼按鈕出現
     *      5.一、讓Login按鈕出現
     *      5.二、移動這個view,帶彈簧效果
     *      5.三、移動logo圖片,帶彈簧效果
     *      5.四、移動logo文字,帶彈簧效果
     */
    
    // 一、get按鈕動畫的view
    UIView * animView = [[UIView alloc] init];
    self.animView = animView;
    animView = [[UIView alloc] initWithFrame:self.GetButton.frame];
    animView.layer.cornerRadius = 10;
    animView.frame = self.GetButton.frame;
    animView.backgroundColor = self.GetButton.backgroundColor;
    [self.view addSubview:animView];
    self.GetButton.hidden = YES;
    self.LoginButton.hidden = NO;
    
    // 二、get背景顏色
    CABasicAnimation * changeColor1 = [CABasicAnimation animationWithKeyPath:@"backgroundColor"];
    changeColor1.fromValue = (__bridge id)ButtonColor.CGColor;
    changeColor1.toValue = (__bridge id)[UIColor whiteColor].CGColor;
    changeColor1.duration = 0.8f;
    changeColor1.beginTime = CACurrentMediaTime();
    changeColor1.fillMode = kCAFillModeForwards;
    changeColor1.removedOnCompletion = false;
    [animView.layer addAnimation:changeColor1 forKey:changeColor1.keyPath];
    
    // 三、get按鈕變寬
    CABasicAnimation * anim1 = [CABasicAnimation animationWithKeyPath:@"bounds.size.width"];
    anim1.fromValue = @(CGRectGetWidth(animView.bounds));
    anim1.toValue = @(ScreenW * 0.8);
    anim1.duration = 0.1;
    anim1.beginTime = CACurrentMediaTime();
    anim1.fillMode = kCAFillModeForwards;
    anim1.removedOnCompletion = false;
    [animView.layer addAnimation:anim1 forKey:anim1.keyPath];
    
    // 四、get按鈕變高
    CABasicAnimation *anim2 = [CABasicAnimation animationWithKeyPath:@"bounds.size.height"];
    anim2.fromValue = @(CGRectGetHeight(animView.bounds));
    anim2.toValue = @(ScreenH * 0.3);
    anim2.duration = 0.1;
    anim2.beginTime = CACurrentMediaTime() + 0.1;
    anim2.fillMode = kCAFillModeForwards;
    anim2.removedOnCompletion = false;
    anim2.delegate = self; // 變高完成,給它加個陰影
    [animView.layer addAnimation:anim2 forKey:anim2.keyPath];
    
    // 5.0、帳號密碼按鈕出現
    self.userTextField.alpha = 0.0;
    self.passwordTextField.alpha = 0.0;
    [UIView animateWithDuration:0.4 delay:0.2 options:UIViewAnimationOptionCurveEaseInOut animations:^{
        self.userTextField.alpha = 1.0;
        self.passwordTextField.alpha = 1.0;
    } completion:^(BOOL finished) {
        
    }];
    
    // 5.一、login按鈕出現動畫
    self.LoginButton.yy_centerX = ScreenW * 0.5;
    self.LoginButton.yy_centerY = ScreenH * 0.7 + 44 + (ScreenH * 0.3 - 44) * 0.5 - 75;
    
    CABasicAnimation * animLoginBtn = [CABasicAnimation animationWithKeyPath:@"bounds.size"];
    animLoginBtn.fromValue = [NSValue valueWithCGSize:CGSizeMake(0, 0)];
    animLoginBtn.toValue = [NSValue valueWithCGSize:CGSizeMake(ScreenW * 0.5, 44)];
    animLoginBtn.duration = 0.4;
    animLoginBtn.beginTime = CACurrentMediaTime() + 0.2;
    animLoginBtn.fillMode = kCAFillModeForwards;
    animLoginBtn.removedOnCompletion = false;
    animLoginBtn.delegate = self;  // 在代理方法(動畫完成回調)裏,讓按鈕真正的寬高改變,而不只僅是它的layer,不然看獲得點不到
    [self.LoginButton.layer addAnimation:animLoginBtn forKey:animLoginBtn.keyPath];
    
    // 5.二、按鈕移動動畫
    POPSpringAnimation * anim3 = [POPSpringAnimation animationWithPropertyNamed:kPOPViewCenter];
    anim3.fromValue = [NSValue valueWithCGRect:CGRectMake(animView.yy_centerX, animView.yy_centerY, animView.yy_width, animView.yy_height)];
    anim3.toValue = [NSValue valueWithCGRect:CGRectMake(animView.yy_centerX, animView.yy_centerY-75, animView.yy_width, animView.yy_height)];
    anim3.beginTime = CACurrentMediaTime() + 0.2;
    anim3.springBounciness = springBounciness;
    anim3.springSpeed = springSpeed;
    [animView pop_addAnimation:anim3 forKey:nil];
    
    // 5.三、圖片移動動畫
    POPSpringAnimation * anim4 = [POPSpringAnimation animationWithPropertyNamed:kPOPViewFrame];
    anim4.fromValue = [NSValue valueWithCGRect:CGRectMake(self.LoginImage.yy_x, self.LoginImage.yy_y, self.LoginImage.yy_width, self.LoginImage.yy_height)];
    anim4.toValue = [NSValue valueWithCGRect:CGRectMake(self.LoginImage.yy_x, self.LoginImage.yy_y-75, self.LoginImage.yy_width, self.LoginImage.yy_height)];
    anim4.beginTime = CACurrentMediaTime()+0.2;
    anim4.springBounciness = springBounciness;
    anim4.springSpeed = springSpeed;
    [self.LoginImage pop_addAnimation:anim4 forKey:nil];
    
    // 5.四、文字移動動畫
    POPSpringAnimation *anim5 = [POPSpringAnimation animationWithPropertyNamed:kPOPViewFrame];
    anim5.fromValue = [NSValue valueWithCGRect:CGRectMake(self.LoginWord.yy_x, self.LoginWord.yy_y, self.LoginWord.yy_width, self.LoginWord.yy_height)];
    anim5.toValue = [NSValue valueWithCGRect:CGRectMake(self.LoginWord.yy_x, self.LoginWord.yy_y-75, self.LoginWord.yy_width, self.LoginWord.yy_height)];
    anim5.beginTime = CACurrentMediaTime()+0.2;
    anim5.springBounciness = springBounciness;
    anim5.springSpeed = springSpeed;
    [self.LoginWord pop_addAnimation:anim5 forKey:nil];
}


#pragma mark - 動畫代理
/** 動畫執行結束回調 */
- (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag {
    if([((CABasicAnimation *)anim).keyPath isEqualToString:@"bounds.size.height"]) {
        // 陰影顏色
        self.animView.layer.shadowColor = [UIColor redColor].CGColor;
        // 陰影的透明度
        self.animView.layer.shadowOpacity = 0.8f;
        // 陰影的圓角
        self.animView.layer.shadowRadius = 5.0f;
        // 陰影偏移量
        self.animView.layer.shadowOffset = CGSizeMake(1,1);
        self.userTextField.alpha = 1.0;
        self.passwordTextField.alpha = 1.0;
    } else if ([((CABasicAnimation *)anim).keyPath isEqualToString:@"bounds.size"]) {
        self.LoginButton.bounds = CGRectMake(ScreenW * 0.5, ScreenH * 0.7 + 44 + (ScreenH * 0.3 - 44) * 0.5 - 75, ScreenW * 0.5, 44);
    }
}

#pragma mark - login按鈕點擊事件——執行動畫

- (void)LoginButtonClick {
    
    // HUDView,蓋住view,以屏蔽掉點擊事件
    self.HUDView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, ScreenW, ScreenH)];
    [[UIApplication sharedApplication].keyWindow addSubview:self.HUDView];
    self.HUDView.backgroundColor = [UIColor colorWithRed:0 green:0 blue:0 alpha:0.0];
    
    // 執行登陸按鈕轉圈動畫的view
    self.LoginAnimView = [[UIView alloc] initWithFrame:self.LoginButton.frame];
    self.LoginAnimView.layer.cornerRadius = 10;
    self.LoginAnimView.layer.masksToBounds = YES;
    self.LoginAnimView.frame = self.LoginButton.frame;
    self.LoginAnimView.backgroundColor = self.LoginButton.backgroundColor;
    [self.view addSubview:self.LoginAnimView];
    self.LoginButton.hidden = YES;
    
    // 把view從寬的樣子變圓
    CGPoint centerPoint = self.LoginAnimView.center;
    CGFloat radius = MIN(self.LoginButton.frame.size.width, self.LoginButton.frame.size.height);
    [UIView animateWithDuration:0.3 delay:0 options:UIViewAnimationOptionCurveEaseOut animations:^{
        self.LoginAnimView.frame = CGRectMake(0, 0, radius, radius);
        self.LoginAnimView.center = centerPoint;
        self.LoginAnimView.layer.cornerRadius = radius/2;
        self.LoginAnimView.layer.masksToBounds = YES;
    }completion:^(BOOL finished) {
        // 給圓加一條不封閉的白色曲線
        self.shapeLayer = [[CAShapeLayer alloc] init];
        self.shapeLayer.lineWidth = 1.5;
        self.shapeLayer.strokeColor = [UIColor whiteColor].CGColor;
        self.shapeLayer.fillColor = self.LoginButton.backgroundColor.CGColor;
        self.shapeLayer.frame = CGRectMake(0, 0, radius, radius);
        
        UIBezierPath * path = [[UIBezierPath alloc] init];
        [path addArcWithCenter:CGPointMake(radius/2, radius/2) radius:(radius/2 - 5) startAngle:0 endAngle:M_PI_2 * 2 clockwise:YES];
        self.shapeLayer.path = path.CGPath;
        [self.LoginAnimView.layer addSublayer:self.shapeLayer];
        
        // 讓圓轉圈,實現"加載中"的效果
        CABasicAnimation * baseAnimation = [CABasicAnimation animationWithKeyPath:@"transform.rotation"];
        baseAnimation.duration = 0.4;
        baseAnimation.fromValue = @(0);
        baseAnimation.toValue = @(2 * M_PI);
        baseAnimation.repeatCount = MAXFLOAT;
        [self.LoginAnimView.layer addAnimation:baseAnimation forKey:nil];
        
        // 開始登陸
        [self doLogin];
    }];
}

/** 模擬登陸 */

- (void)doLogin {
    // 延時,模擬網絡請求的延時
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        if ([self.userTextField.text isEqualToString:@""] || [self.passwordTextField.text isEqualToString:@""]) {
            // 登陸失敗
            [self loginFail];
        } else {
            // 登陸成功
            [self loginSuccess];
        }
    });
}

/** 登陸成功 */
- (void)loginSuccess {
    // 移除蒙版
    [self.HUDView removeFromSuperview];
    // 跳轉到另外一個控制器
    ViewController * vc = [[ViewController alloc] init];
    vc.transitioningDelegate = self;
    [self presentViewController:vc animated:YES completion:nil];
}

/** 登陸失敗 */
- (void)loginFail {
    // 把蒙版、動畫view等隱藏,把真正的login按鈕顯示出來
    self.LoginButton.hidden = NO;
    [self.HUDView removeFromSuperview];
    [self.LoginAnimView removeFromSuperview];
    [self.LoginAnimView.layer removeAllAnimations];
    
    // 給按鈕添加左右擺動的效果(路徑動畫)
    // CABasicAnimation是從一個值到另外一個值,關鍵幀動畫CAKeyframeAnimation是值變化的數組
    CAKeyframeAnimation * keyFrame = [CAKeyframeAnimation animationWithKeyPath:@"position"];
    CGPoint point = self.LoginAnimView.layer.position;
    keyFrame.values = @[[NSValue valueWithCGPoint:CGPointMake(point.x, point.y)],
                        [NSValue valueWithCGPoint:CGPointMake(point.x - 10, point.y)],
                        [NSValue valueWithCGPoint:CGPointMake(point.x + 10, point.y)],
                        [NSValue valueWithCGPoint:CGPointMake(point.x - 10, point.y)],
                        [NSValue valueWithCGPoint:CGPointMake(point.x + 10, point.y)],
                        [NSValue valueWithCGPoint:CGPointMake(point.x - 10, point.y)],
                        [NSValue valueWithCGPoint:CGPointMake(point.x + 10, point.y)],
                        [NSValue valueWithCGPoint:point]];
    
    // timingFunction意思是動畫執行的效果,kCAMediaTimingFunctionEaseInEaseOut表示淡入淡出
    keyFrame.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
    keyFrame.duration = 0.5f;
    [self.LoginButton.layer addAnimation:keyFrame forKey:keyFrame.keyPath];
}

/** 點擊退回鍵盤 */
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    [self.view endEditing:YES];
}


#pragma mark UIViewControllerTransitioningDelegate(轉場動畫代理)

- (id<UIViewControllerAnimatedTransitioning>)animationControllerForDismissedController:(UIViewController *)dismissed {
    self.loginTranslation.doLogin = NO;
    return self.loginTranslation;
}

- (id<UIViewControllerAnimatedTransitioning>)animationControllerForPresentedController:(UIViewController *)presented presentingController:(UIViewController *)presenting sourceController:(UIViewController *)source {
    self.loginTranslation.doLogin = YES;
    
    // 須要返回一個遵照了這個代理的對象,須要新建一個類遵照這個代理,實現兩個代理方法。
    return self.loginTranslation;
}

/** 移除並置空全部控件,從新生成控件,對於防止內存泄漏有好處 */
- (void)reloadView {
    int i = [[NSString stringWithFormat:@"%lu",(self.view.subviews.count-1)] intValue];
    for (; i >= 0; i--)
    {
        UIView *subView = self.view.subviews[i];
        [subView removeFromSuperview];
        subView = nil;
    }
    self.LoginImage = nil;
    self.LoginWord = nil;
    self.GetButton = nil;
    self.LoginButton = nil;
    self.HUDView = nil;
    self.LoginAnimView = nil;
    self.shapeLayer = nil;
    self.animView = nil;
    self.userTextField = nil;
    self.passwordTextField = nil;
    
    [self SetupUIComponent];
}



- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}



@end
LoginTranslation.h

#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>

@interface LoginTranslation : NSObject <UIViewControllerAnimatedTransitioning>
/** 登陸或註銷 YES:登陸 */
@property (nonatomic,assign) BOOL doLogin;
@end
LoginTranslation.m

#import "LoginTranslation.h"
#import "LoginViewController.h"
#import "ViewController.h"
#import "POP.h"
#import "UIView+YYExtension.h"

#define ScreenW [UIScreen mainScreen].bounds.size.width
#define ScreenH [UIScreen mainScreen].bounds.size.height

@interface LoginTranslation () <CAAnimationDelegate>
// 作弧線運動的那個圓
@property (strong, nonatomic) UIView * circularAnimView;
@end

@implementation LoginTranslation

// 轉場時間
- (NSTimeInterval)transitionDuration:(id<UIViewControllerContextTransitioning>)transitionContext {
    return 1.0;
}

// 轉場動畫
- (void)animateTransition:(id)transitionContext {
    if (self.doLogin)// 登陸轉場動畫
    {
        // transitionContext:轉場上下文
        // 轉場過程當中顯示的view,全部動畫控件都應該加在這上面
        __block UIView * containerView = [transitionContext containerView];
        
        // 轉場去的控制器
        ViewController * toVC = (ViewController *)[transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
        // 轉場來的控制器
        LoginViewController * fromVC = (LoginViewController *)[transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
        
        // 一、fromVC背景變白 (fromVC.view默認已經加到了containerView中,因此不用再添加)
        [UIView animateWithDuration:0.15 animations:^{
            fromVC.view.backgroundColor = [UIColor whiteColor];
        }completion:^(BOOL finished) {
            [fromVC.view removeFromSuperview];
        }];
        
        // 二、帳號密碼輸入框消失
        [containerView addSubview:fromVC.userTextField];
        [containerView addSubview:fromVC.passwordTextField];
        
        [UIView animateWithDuration:0.1 animations:^{
            fromVC.userTextField.alpha = 0.0;
            fromVC.passwordTextField.alpha = 0.0;
        }];
        
        // 三、logo圖片移動消失
        [containerView addSubview:fromVC.LoginImage];
        
        [UIView animateWithDuration:0.15 delay:0.15 options:UIViewAnimationOptionCurveEaseInOut animations:^{
            fromVC.LoginImage.alpha = 0.0;
        } completion:^(BOOL finished) {
            
        }];
        
        // 四、logo文字縮小、移動
        [containerView addSubview:fromVC.LoginWord];
        
        CGFloat proportion = toVC.navWord.yy_width / fromVC.LoginWord.yy_width;
        CABasicAnimation * LoginWordScale = [CABasicAnimation animationWithKeyPath:@"transform.scale"];
        LoginWordScale.fromValue = [NSNumber numberWithFloat:1.0];
        LoginWordScale.toValue = [NSNumber numberWithFloat:proportion];
        LoginWordScale.duration = 0.4;
        LoginWordScale.beginTime = CACurrentMediaTime()+0.15;
        LoginWordScale.removedOnCompletion = NO;
        LoginWordScale.fillMode = kCAFillModeForwards;
        [fromVC.LoginWord.layer addAnimation:LoginWordScale forKey:LoginWordScale.keyPath];
        
        CGPoint newPosition = [toVC.view convertPoint:toVC.navWord.center fromView:toVC.navView];
        
        [UIView animateWithDuration:0.4 delay:0.15 options:UIViewAnimationOptionCurveEaseInOut animations:^{
            fromVC.LoginWord.yy_centerX = newPosition.x;
            fromVC.LoginWord.yy_centerY = newPosition.y;
        } completion:^(BOOL finished) {
            
        }];
        
        // 五、圓(登陸加載的那個圓)的移動,由於登陸頁面的那個圓有正在動的sublayer,因此這裏新建了個圓來作動畫
        UIView * circularAnimView = [[UIView alloc] initWithFrame:fromVC.LoginAnimView.frame];
        self.circularAnimView = circularAnimView;
        circularAnimView.layer.cornerRadius = circularAnimView.yy_width * 0.5;
        circularAnimView.layer.masksToBounds = YES;
        circularAnimView.frame = fromVC.LoginAnimView.frame;
        circularAnimView.backgroundColor = fromVC.LoginAnimView.backgroundColor;
        self.circularAnimView = circularAnimView;
        [containerView addSubview:circularAnimView];
        [fromVC.LoginAnimView removeFromSuperview];
        
        CGFloat bntSize = 44;
        fromVC.LoginAnimView.layer.cornerRadius = bntSize * 0.5;
        CGFloat originalX = toVC.view.yy_width - bntSize - 15;
        CGFloat originalY = toVC.view.yy_height - bntSize - 15 - 49;
        
        // CGContextRef,CGPath和UIBezierPath。本質上都是同樣的,都是使用Quartz來繪畫。只不過把繪圖操做暴露在不一樣的API層面上,在具體實現上,固然也會有一些細小的差異。
        CGMutablePathRef path = CGPathCreateMutable();
        CGPathMoveToPoint(path, NULL, (circularAnimView.yy_x + circularAnimView.yy_width * 0.5), (circularAnimView.yy_y + circularAnimView.yy_height * 0.5));
        CGPathAddQuadCurveToPoint(path, NULL, ScreenW * 0.9, circularAnimView.yy_y + circularAnimView.yy_height, (originalX + circularAnimView.yy_width * 0.5), (originalY + circularAnimView.yy_height * 0.5));
        
        CAKeyframeAnimation * animate = [CAKeyframeAnimation animationWithKeyPath:@"position"];
        animate.delegate = self;
        animate.duration = 0.4;
        animate.beginTime = CACurrentMediaTime()+0.15;
        animate.fillMode = kCAFillModeForwards;
        animate.repeatCount = 0;
        animate.path = path;
        animate.removedOnCompletion = NO;
        CGPathRelease(path);
        [circularAnimView.layer addAnimation:animate forKey:@"circleMoveAnimation"];
        
        // 導航欄出現
        UIView * navView = [[UIView alloc] init];
        navView.frame = toVC.navView.frame;
        navView.backgroundColor = toVC.navView.backgroundColor;
        [containerView insertSubview:navView atIndex:1];
        navView.alpha = 0.0;
        [UIView animateWithDuration:0.6 delay:0.15 options:UIViewAnimationOptionCurveEaseInOut animations:^{
            navView.alpha = 1.0;
        } completion:^(BOOL finished) {
            
        }];
        
        // 背景出現、移動
        UIImageView * backImage = [[UIImageView alloc] init];
        backImage.image = toVC.backImage.image;
        backImage.frame = toVC.backImage.frame;
        [containerView insertSubview:backImage atIndex:1];
        backImage.alpha = 0.0;
        backImage.yy_y += 100;
        
        POPSpringAnimation * backImageMove = [POPSpringAnimation animationWithPropertyNamed:kPOPViewCenter];
        backImageMove.fromValue = [NSValue valueWithCGRect:CGRectMake(backImage.yy_centerX, backImage.yy_centerY, backImage.yy_width, toVC.backImage.yy_height)];
        backImageMove.toValue = [NSValue valueWithCGRect:CGRectMake(backImage.yy_centerX, backImage.yy_centerY-100, backImage.yy_width, backImage.yy_height)];
        backImageMove.beginTime = CACurrentMediaTime()+0.15+0.2;
        backImageMove.springBounciness = 5.0;
        backImageMove.springSpeed = 10.0;
        [backImage pop_addAnimation:backImageMove forKey:nil];
        
        [UIView animateWithDuration:0.6 delay:0.15 + 0.2 options:UIViewAnimationOptionCurveEaseInOut animations:^{
            backImage.alpha = 1.0;
        } completion:^(BOOL finished) {
            [self cleanContainerView:containerView];   // 移除全部子控件
            [containerView addSubview:toVC.view];      // 將目標控制器的vc添加上去
            [transitionContext completeTransition:YES];// 標誌轉場結束
            containerView = nil;
            [fromVC reloadView]; // 登陸界面重載UI
        }];
    }
    else // 退出登陸轉場動畫
    {
        // transitionContext:轉場上下文
        // 轉場過程當中顯示的view,全部動畫控件都應該加在這上面
        UIView * containerView = [transitionContext containerView];
        // 轉場的來源控制器
        LoginViewController * toVC = (LoginViewController *)[transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
        // 轉場去往的控制器
        ViewController * fromVC = (ViewController *)[transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
        
        // 作一個淡入淡出的效果
        toVC.view.alpha = 0;
        [containerView addSubview:toVC.view];
        [UIView animateWithDuration:1.0 animations:^{
            fromVC.view.alpha = 0;
        } completion:^(BOOL finished) {
            
        }];
        [UIView animateWithDuration:0.6 delay:0.4 options:UIViewAnimationOptionCurveEaseInOut animations:^{
            toVC.view.alpha = 1;
        } completion:^(BOOL finished) {
            [transitionContext completeTransition:YES];
        }];
    }
}

/** 移除containerView的子控件 */
- (void)cleanContainerView:(UIView *)containerView {
    int i = [[NSString stringWithFormat:@"%lu",(containerView.subviews.count-1)] intValue];
    for (; i >= 0; i--) {
        UIView * subView = containerView.subviews[i];
        [subView removeFromSuperview];
    }
}


/** 核心動畫動畫代理 */
- (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag {
    if ([self.circularAnimView.layer animationForKey:@"circleMoveAnimation"] == anim) {
        /** 這裏是在作加號按鈕內部的白色加號的伸展開的效果 */
        //畫線
        CGRect rect = self.circularAnimView.frame;
        CGPoint centerPoint = CGPointMake(rect.size.width*0.5, rect.size.height*0.5);
        
        //貝瑟爾線
        UIBezierPath *path1 = [UIBezierPath bezierPath];
        [path1 moveToPoint:centerPoint];
        [path1 addLineToPoint:CGPointMake(rect.size.width*0.5, rect.size.height*0.25)];
        UIBezierPath *path2 = [UIBezierPath bezierPath];
        [path2 moveToPoint:centerPoint];
        [path2 addLineToPoint:CGPointMake(rect.size.width*0.25, rect.size.height*0.5)];
        UIBezierPath *path3 = [UIBezierPath bezierPath];
        [path3 moveToPoint:centerPoint];
        [path3 addLineToPoint:CGPointMake(rect.size.width*0.5, rect.size.height*0.75)];
        UIBezierPath *path4 = [UIBezierPath bezierPath];
        [path4 moveToPoint:centerPoint];
        [path4 addLineToPoint:CGPointMake(rect.size.width*0.75, rect.size.height*0.5)];
        
        //ShapeLayer
        CAShapeLayer *shape1 = [self makeShapeLayerWithPath:path1 lineWidth:rect.size.width*0.07];
        [self.circularAnimView.layer addSublayer:shape1];
        CAShapeLayer *shape2 = [self makeShapeLayerWithPath:path2 lineWidth:rect.size.width*0.07];
        [self.circularAnimView.layer addSublayer:shape2];
        CAShapeLayer *shape3 = [self makeShapeLayerWithPath:path3 lineWidth:rect.size.width*0.07];
        [self.circularAnimView.layer addSublayer:shape3];
        CAShapeLayer *shape4 = [self makeShapeLayerWithPath:path4 lineWidth:rect.size.width*0.07];
        [self.circularAnimView.layer addSublayer:shape4];
        
        //動畫
        CABasicAnimation *checkAnimation = [CABasicAnimation animationWithKeyPath:@"strokeEnd"];
        checkAnimation.duration = 0.25f;
        checkAnimation.fromValue = @(0.0f);
        checkAnimation.toValue = @(1.0f);
        checkAnimation.delegate = self;
        
        [shape1 addAnimation:checkAnimation forKey:@"checkAnimation"];
        [shape2 addAnimation:checkAnimation forKey:@"checkAnimation"];
        [shape3 addAnimation:checkAnimation forKey:@"checkAnimation"];
        [shape4 addAnimation:checkAnimation forKey:@"checkAnimation"];
    }
}

- (CAShapeLayer *)makeShapeLayerWithPath:(UIBezierPath *)path lineWidth:(CGFloat)lineWidth {
    CAShapeLayer * shape=[CAShapeLayer layer];
    shape.lineWidth = lineWidth;
    shape.fillColor = [UIColor clearColor].CGColor;
    shape.strokeColor = [UIColor whiteColor].CGColor;
    shape.lineCap = kCALineCapRound;
    shape.lineJoin = kCALineJoinRound;
    shape.path = path.CGPath;
    
    return shape;
}

@end
ViewController.h

#import <UIKit/UIKit.h>
#import "AddView.h"

@interface ViewController : UIViewController

@property (nonatomic,strong) UIView  * navView; //導航欄
@property (nonatomic,strong) UILabel * navWord; //導航欄上面的文字
@property (nonatomic,strong) AddView * addView; //加號按鈕
@property (nonatomic,strong) UIImageView * backImage; //背景

@end
ViewController.m

/*
  如何生成一個動畫讓控件執行
  現流行的方式主要有三種:
  一、基本動畫
  二、核心動畫
  三、三方框架——POP框架(由Facebook開發)
 
 一、控件的位置、大小等是否是真的發生了改變:
 基本動畫、pop動畫:是給控件添加動畫(通常也不會有用基本動畫給layer添加動畫的作法),全部動畫完成時,控件的屬性已經改變;
 核心動畫:是給控件的圖層(view.layer)添加動畫,看似發生了位置大小的變化,實際上控件自己的屬性並未改變。
 
 基本動畫
 優點:代碼簡單,代碼量少
 劣勢:功能相對單一
 
 核心動畫
 優點:功能強大、流暢性好、連續幾個動畫之間的銜接度好。流暢主要是由於操做layer是輕量級的,不容易產生動畫卡頓的感受。
 劣勢:代碼量大;容易寫錯(某些參數沒有定義宏,寫錯了都不知道);若有須要,還要手動在動畫完成時將控件的屬性同步修改了。
 
 pop動畫
 優點:比核心動畫代碼要簡單,最大的優點在於,容易作彈簧效果,因此不少有「Q彈」感受的都用pop動畫作
 劣勢:要在一個動畫完成時開始另外一個動畫,pop動畫不擅長,主要由於它的動畫執行時間由"速度"和"彈性係數"兩個參數控制,很差直觀判斷動畫執行了多久,而若是在pop動畫完成回調的block裏提交下一個動畫,會不連貫(親測,緣由不詳)。
 
 
 一、點擊了GET按鈕,logo圖和logo文字上移
 移動屬於比較簡單的操做,但這個移動效果具備彈簧效果,因此能夠採用核心動畫中的關鍵幀動畫CAKeyframeAnimation,或者pop動畫來實現,這裏我用了pop,後面登陸失敗按鈕左右擺動的動畫,我用了CAKeyframeAnimation。
 
 二、get按鈕的變化
 get按鈕分別進行了變寬、變寬的同時圓角變小,而後變高,而後向上移動,整個過程顏色由初始顏色變白。因爲這是N個動畫,有同時執行的,有接着上一步執行的,因此我選擇核心動畫CABasicAnimation,更容易控制每一個動畫的執行時間、開始時間,容易銜接得流暢。
 
 三、點擊LOGIN,按鈕轉圈
 點擊了LOGIN,按鈕先從寬變圓,而後給按鈕添加一條半圓的白色圓弧線,而後讓這個按鈕開始旋轉。
 
 四、登陸失敗按鈕抖動
 這個效果跟pop動畫移動後抖動的效果很相似,這裏我選擇用關鍵幀動畫CAKeyframeAnimation作,它與CABasicAnimation略有不一樣,CABasicAnimation是從一個值到另外一個值,CAKeyframeAnimation是值變化的數組。
 
 一、LOGO圖逐漸消失;
 
 二、LOGO文字逐漸變小、上移至B中頭部文字的位置;
 
 三、A控制器的登陸框消失、A控制器背景顏色變白;
 
 四、轉圈控件通過弧線運動到右下角,白色加號逐漸造成
 
 五、B控制器背景圖上移的動畫。
 */

#import "ViewController.h"
#import "UIView+YYExtension.h"
#import "AddView.h"


#define ScreenW [UIScreen mainScreen].bounds.size.width
#define ScreenH [UIScreen mainScreen].bounds.size.height

@interface ViewController ()

@end

@implementation ViewController

#pragma mark - 懶加載

- (UIImageView *)backImage {
    if (!_backImage) {
        _backImage = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"backImg.jpg"]];
        [self.view addSubview:_backImage];
        _backImage.frame = CGRectMake(0, 0, ScreenW, ScreenH);
    }
    return _backImage;
}

- (UIView *)navView {
    if (!_navView) {
        _navView = [[UIView alloc] init];
        [self.view addSubview:_navView];
        _navView.backgroundColor = [UIColor whiteColor];
        _navView.frame = CGRectMake(0, 0, ScreenW, 64);
    }
    return _navView;
}

- (UILabel *)navWord {
    if (!_navWord) {
        _navWord = [[UILabel alloc] init];
        _navWord.font =  [UIFont fontWithName:@"TimesNewRomanPS-ItalicMT" size:24.0f];
        _navWord.textColor = [UIColor blackColor];
        _navWord.text = @"YY Anim Demo";
        _navWord.hidden = NO;
        [_navWord sizeToFit];
    }
    return _navWord;
}

- (AddView *)addView
{
    if (!_addView) {
        CGFloat bntSize = 44;
        _addView = [[AddView alloc] initWithFrame:CGRectMake(0, 0, bntSize, bntSize)];
        [self.view addSubview:_addView];
        _addView.userInteractionEnabled = YES;
        [_addView addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(addViewClick)]];
        _addView.frame = CGRectMake(ScreenW - 15 - bntSize, ScreenH - 15 - 49 - bntSize, bntSize, bntSize);
    }
    return _addView;
}

- (void)viewDidLoad {
    [super viewDidLoad];
    [self setupUIComponent];
}

/** 初始化UI */
- (void)setupUIComponent {
    self.backImage.hidden = NO;
    self.navView.hidden = NO;
    self.addView.hidden = NO;
    
    [self.navView addSubview:self.navWord];
    self.navWord.yy_centerX = self.navView.yy_centerX;
    self.navWord.yy_centerY = self.navView.yy_centerY + 10;
}

/** 點擊加號按鈕 */
- (void)addViewClick {
    //退回登陸頁面
    [self dismissViewControllerAnimated:YES completion:nil];
}



- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}


@end
AddView.m

#import "AddView.h"

#define MAIN_COLOR [UIColor colorWithRed:156/255.0 green:197/255.0 blue:251/255.0 alpha:1.0]

@implementation AddView

- (instancetype)initWithFrame:(CGRect)frame
{
    if (self = [super initWithFrame:frame])
    {
        self.backgroundColor = [UIColor clearColor];
    }
    return self;
}

- (void)drawRect:(CGRect)rect {
    CGPoint center = CGPointMake(rect.size.width * 0.5,rect.size.height * 0.5);
    UIBezierPath * path = [UIBezierPath bezierPathWithArcCenter:center radius:(rect.size.width * 0.5 - rect.size.width * 0.03) startAngle:0 endAngle:M_PI*2 clockwise:YES];
    
    [MAIN_COLOR set];
    
    // 填充:必須是一個完整的封閉路徑,默認就會自動關閉路徑
    [path fill];
    
    UIBezierPath * path1 = [UIBezierPath bezierPath];
    path1.lineWidth = rect.size.width * 0.07;
    [[UIColor whiteColor] set];
    // 設置起點
    [path1 moveToPoint:CGPointMake(rect.size.width * 0.25, rect.size.height * 0.5)];
    
    // 添加一根線到某個點
    [path1 addLineToPoint:CGPointMake(rect.size.width * 0.75, rect.size.height * 0.5)];
    
    // 繪製路徑
    UIBezierPath * path2 = [UIBezierPath bezierPath];
    path2.lineWidth = rect.size.width * 0.07;
    [[UIColor whiteColor] set];
    // 設置起點
    [path2 moveToPoint:CGPointMake(rect.size.width * 0.5, rect.size.height * 0.25)];
    
    // 添加一根線到某個點
    [path2 addLineToPoint:CGPointMake(rect.size.width * 0.5, rect.size.height * 0.75)];
    
    // 繪製路徑
    //    [path2 stroke];
    [self.layer addSublayer:[self makeShapeLayerWithPath:path1 lineWidth:path1.lineWidth]];
    [self.layer addSublayer:[self makeShapeLayerWithPath:path2 lineWidth:path2.lineWidth]];
    
    self.layer.shadowColor = MAIN_COLOR.CGColor;
    //陰影的透明度
    self.layer.shadowOpacity = 0.5f;
    //陰影的圓角
    self.layer.shadowRadius = 4.0f;
    //陰影偏移量
    self.layer.shadowOffset = CGSizeMake(0,0);
}

- (CAShapeLayer *)makeShapeLayerWithPath:(UIBezierPath *)path lineWidth:(CGFloat)lineWidth {
    CAShapeLayer * shape = [CAShapeLayer layer];
    shape.lineWidth = lineWidth;
    shape.fillColor = [UIColor clearColor].CGColor;
    shape.strokeColor = [UIColor whiteColor].CGColor;
    shape.lineCap = kCALineCapRound;
    shape.lineJoin = kCALineJoinRound;
    shape.path = path.CGPath;
    return shape;
}

@end
相關文章
相關標籤/搜索