1.Popping笔记--Flat Button

作者: ForeverYoung21 | 来源:发表于2015-05-22 21:23 被阅读383次

    Popping笔记。Github上搜索"Popping"即可下载源代码。

    Flat Button


    首先我们来分析完成这个效果都有哪些事情发生。

    1.屏幕中央有一button,写着"Log in"。

    2.点击按钮,左上角会出现networkActicityIndicator,并且navigation上的右面也出现activityIndicator。

    3.等待一段时间,且此时button不可点击。

    4.短暂时间过后,activityIndicator都消失。同时,button左右摇晃,从button的中央弹出一个label,注意这个label有两个动画效果:一个是从小到大,一个是在竖直方向上移动,且都有一些抖动效果。

    5.此时button可以点击了。

    6.点击button,label回到button的中央,同样有两个动画效果:从大到小和竖直移动,不过这次不再抖动,都是迅速回去。

    7.重复。


    分析完毕。我们来看代码:

    首先看FlatButton.h(.m)这两个文件。

    .h 文件:

    只有一个类方法,可以知道是用来创建这个FlatButton的。

    .m 文件:

    + (instancetype)button
    {
        return [self buttonWithType:UIButtonTypeCustom];
    }
    
    - (id)initWithFrame:(CGRect)frame
    {
        self = [super initWithFrame:frame];
        if (self) {
            [self setup];
        }
        return self;
    }
    

    用来创建FlatButton,注意这里返回了一个UIButtonTypeCustom类型的button,如果使用系统默认的button type,那么有些字体之类的是无法修改的(?)。

    接下来到setup方法:

    - (void)setup
    {
        self.backgroundColor = self.tintColor;
        self.layer.cornerRadius = 4.f;
        [self setTitleColor:[UIColor whiteColor]
                     forState:UIControlStateNormal];
        self.titleLabel.font = [UIFont fontWithName:@"Avenir-Medium"
                                                 size:22];
    
        [self addTarget:self action:@selector(scaleToSmall)
       forControlEvents:UIControlEventTouchDown | UIControlEventTouchDragEnter];
        [self addTarget:self action:@selector(scaleAnimation)
       forControlEvents:UIControlEventTouchUpInside];
        [self addTarget:self action:@selector(scaleToDefault)
       forControlEvents:UIControlEventTouchDragExit];
    }
    

    设置button的颜色,圆角之类的,注意这里修改了titleLabel的字体和大小,如果在刚才的button方法中返回的不是[self buttonWithType:UIButtonTypeCustom],而是UIBUttonTypeSystem,那么字体和大小修改的效果是不会出现的。

    接着我们为button添加事件:

    如果鼠标按下去或者按着鼠标进入button的bounds内,则让button缩小。

    如果完成一次点击事件(按下去抬起来),则让button进行动画(一次正常-小-正常的抖动,即我们点击button看到的动画效果)。

    如果按着鼠标离开,则让button恢复正常大小。

    下面是三个触发的方法:

    - (void)scaleToSmall
    {
        POPBasicAnimation *scaleAnimation = [POPBasicAnimation animationWithPropertyNamed:kPOPLayerScaleXY];
        scaleAnimation.toValue = [NSValue valueWithCGSize:CGSizeMake(0.95f, 0.95f)];
        [self.layer pop_addAnimation:scaleAnimation forKey:@"layerScaleSmallAnimation"];
    }
    
    - (void)scaleAnimation
    {
        POPSpringAnimation *scaleAnimation = [POPSpringAnimation animationWithPropertyNamed:kPOPLayerScaleXY];
        scaleAnimation.velocity = [NSValue valueWithCGSize:CGSizeMake(3.f, 3.f)];
        scaleAnimation.toValue = [NSValue valueWithCGSize:CGSizeMake(1.f, 1.f)];
        scaleAnimation.springBounciness = 18.0f;
        [self.layer pop_addAnimation:scaleAnimation forKey:@"layerScaleSpringAnimation"];
    }
    
    - (void)scaleToDefault
    {
        POPBasicAnimation *scaleAnimation = [POPBasicAnimation animationWithPropertyNamed:kPOPLayerScaleXY];
        scaleAnimation.toValue = [NSValue valueWithCGSize:CGSizeMake(1.f, 1.f)];
        [self.layer pop_addAnimation:scaleAnimation forKey:@"layerScaleDefaultAnimation"];
    }
    

    你可能注意到了,开始的时候我们这样做:

    #import <POP/POP.h>
    

    我们导入了facebook的开源代码库---POP,它可以让我们做出很多炫酷的动画,非常方便。可以去github上搜索,有兴趣可以多多了解,popping就是基于POP的。

    scaleToSmall方法和scaleToDefault方法类似,顾名思义,前者是让button变小,后者让button恢复正常。

    创建一个POPBasicAnimation,设置将对kPOPLayerScaleXY进行动画,scaleToSmall中将其x,y方向上都变为原大小的0.95倍,scaleToDefult则让button恢复正常大小。

    scaleAnimation中,创建一个POPSpringAnimation,这个就是实现抖动效果的类。设置其velocity,toValue以及springBounciness属性,让其在变为正常大小的时候附有抖动效果。注意,此时velocity属性是这样赋值的:[NSValue valueWithCGSize:CGSizeMake(3.f, 3.f)]。这是因为我们当前设置的是kPOPLayerScaleXY,则需要对x,y方向分别设置值。那么如果我们对kPOPLayerPositionX设置,则可以这样positionAnimation.velocity = @2000;
    (后面可以见到)。


    现在来看ButtonViewController.m文件

    - (void)viewDidLoad
    {
        [super viewDidLoad];
        self.view.backgroundColor = [UIColor whiteColor];
        [self addButton];
        [self addLabel];
        [self addActivityIndicatorView];
    }
    

    开始时候我们增加一个button(就是我们的FlatButton),一个label(红色字的),一个activityIndicator(Navigation右面那个)。

    #pragma mark - Private Instance methods
    
    - (void)addButton
    {
        self.button = [FlatButton button];
        self.button.backgroundColor = [UIColor customBlueColor];
        self.button.translatesAutoresizingMaskIntoConstraints = NO;
        [self.button setTitle:@"Log in" forState:UIControlStateNormal];
        [self.button addTarget:self action:@selector(touchUpInside:) forControlEvents:UIControlEventTouchUpInside];
        [self.view addSubview:self.button];
    
        [self.view addConstraint:[NSLayoutConstraint constraintWithItem:self.button
                                                              attribute:NSLayoutAttributeCenterX
                                                              relatedBy:NSLayoutRelationEqual
                                                                 toItem:self.view
                                                              attribute:NSLayoutAttributeCenterX
                                                             multiplier:1.f
                                                               constant:0.f]];
    
        [self.view addConstraint:[NSLayoutConstraint constraintWithItem:self.button
                                                              attribute:NSLayoutAttributeCenterY
                                                              relatedBy:NSLayoutRelationEqual
                                                                 toItem:self.view
                                                              attribute:NSLayoutAttributeCenterY
                                                             multiplier:1.f
                                                               constant:0.f]];
    }
    
    - (void)addLabel
    {
        self.errorLabel = [UILabel new];
        self.errorLabel.font = [UIFont fontWithName:@"Avenir-Light" size:18];
        self.errorLabel.textColor = [UIColor customRedColor];
        self.errorLabel.translatesAutoresizingMaskIntoConstraints = NO;
        self.errorLabel.text = @"Just a serious login error.";
        self.errorLabel.textAlignment = NSTextAlignmentCenter;
        [self.view insertSubview:self.errorLabel belowSubview:self.button];
    
        [self.view addConstraint:[NSLayoutConstraint
                                  constraintWithItem:self.errorLabel
                                  attribute:NSLayoutAttributeCenterX
                                  relatedBy:NSLayoutRelationEqual
                                  toItem:self.button
                                  attribute:NSLayoutAttributeCenterX
                                  multiplier:1
                                  constant:0.f]];
    
        [self.view addConstraint:[NSLayoutConstraint
                                  constraintWithItem:self.errorLabel
                                  attribute:NSLayoutAttributeCenterY
                                  relatedBy:NSLayoutRelationEqual toItem:self.button
                                  attribute:NSLayoutAttributeCenterY
                                  multiplier:1
                                  constant:0]];
    
        self.errorLabel.layer.transform = CATransform3DMakeScale(0.5f, 0.5f, 1.f);
    }
    
    - (void)addActivityIndicatorView
    {
        self.activityIndicatorView = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
        UIBarButtonItem *item = [[UIBarButtonItem alloc] initWithCustomView:self.activityIndicatorView];
        self.navigationItem.rightBarButtonItem = item;
    }
    

    addButton方法中,创建了一个button,设置各种属性并监听点击事件,注意将其translatesAutoresizingMaskIntoConstraints = NO,这是因为我们接下来需要addConstraints,所以不用AutoresizingMask。

    接下来我们添加了两个constraint:分别是让button中点的x坐标和view中点的x坐标相等,button中点的y坐标和view中点的y坐标相等(让button处于view中点)。

    addLabel方法中除了与上面相似的内容外,我们将errorLabel插到button的后面了,这样用户就看不到label。可是实际上label会有多出的部分,button无法将其挡住,所以我们设置了errorLabel.layer.transform = CATransform3DMakeScale(0.5f, 0.5f, 1.f),即将label横纵向都缩小到原来的一半,这样button就可以完全将其挡住。

    addActivityIndicatorView没什么好说的,创建一个activityIndicatorView,创建一个UIBarButtonItem内容就是这个view,并将其设置成navigationItem.rightBarButtonItem。

    - (void)touchUpInside:(FlatButton *)button
    {
        [self hideLabel];
        [self.activityIndicatorView startAnimating];
        button.userInteractionEnabled = NO;
        [UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.5f * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            [UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
            [self.activityIndicatorView stopAnimating];
            [self shakeButton];
            [self showLabel];
        });
    }
    

    点击按钮触发touchUpInside:方法。首先是[self hideLabel],这个在第一次点击的时候没有意义,但是以后每次点击,都会首先将上次弹出的label隐藏。如果不这样,label就会一直在了。我们来看看这个方法都做了什么:

    - (void)hideLabel
    {
        POPBasicAnimation *layerScaleAnimation = [POPBasicAnimation animationWithPropertyNamed:kPOPLayerScaleXY];
        layerScaleAnimation.toValue = [NSValue valueWithCGSize:CGSizeMake(0.5f, 0.5f)];
        [self.errorLabel.layer pop_addAnimation:layerScaleAnimation forKey:@"layerScaleAnimation"];
    
        POPBasicAnimation *layerPositionAnimation = [POPBasicAnimation animationWithPropertyNamed:kPOPLayerPositionY];
        layerPositionAnimation.toValue = @(self.button.layer.position.y);
        [self.errorLabel.layer pop_addAnimation:layerPositionAnimation forKey:@"layerPositionAnimation"];
    }
    

    很简单,就像我们开始分析的那样,两个动画:缩小并回到button的中心位置(其初始位置),注意这里用的是POPBasicAnimation,即没有任何抖动之类的效果,只是快速的边缩小边回到button的中点。

    回到touchUpInside:中,在隐藏Label之后,让navigation上的activityIndicatorView和左上角的都开始动画,并且button不允许点击。此时另起queue,在dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.5f * NSEC_PER_SEC)这些时间之后,回到main queue中调整UI。首先关闭activityIndicatorView,接着[self shakeButton]来让button抖动代表有错误,之后弹出label。

    - (void)shakeButton
    {
        POPSpringAnimation *positionAnimation = [POPSpringAnimation animationWithPropertyNamed:kPOPLayerPositionX];
        positionAnimation.velocity = @2000;
        positionAnimation.springBounciness = 20;
        [positionAnimation setCompletionBlock:^(POPAnimation *animation, BOOL finished) {
            self.button.userInteractionEnabled = YES;
        }];
        [self.button.layer pop_addAnimation:positionAnimation forKey:@"positionAnimation"];
    }
    
    - (void)showLabel
    {
        self.errorLabel.layer.opacity = 1.0;
        POPSpringAnimation *layerScaleAnimation = [POPSpringAnimation animationWithPropertyNamed:kPOPLayerScaleXY];
        layerScaleAnimation.springBounciness = 18;
        layerScaleAnimation.toValue = [NSValue valueWithCGSize:CGSizeMake(1.f, 1.f)];
        [self.errorLabel.layer pop_addAnimation:layerScaleAnimation forKey:@"labelScaleAnimation"];
    
        POPSpringAnimation *layerPositionAnimation = [POPSpringAnimation animationWithPropertyNamed:kPOPLayerPositionY];
        layerPositionAnimation.toValue = @(self.button.layer.position.y + self.button.intrinsicContentSize.height);
        layerPositionAnimation.springBounciness = 12;
        [self.errorLabel.layer pop_addAnimation:layerPositionAnimation forKey:@"layerPositionAnimation"];
    }
    

    shakeButton方法中,创建一个POPSpringAnimation,注意此时要对kPOPLayerPositionX进行动画,所以在设置velocity的时候直接设置@2000(一个值不再是x,y两个方向两个值),在动画完成之后设置button可以点击。注意这里并没有设置toValue的值,如果不设置velocity的值,button是不会抖动的。

    showLabel方法和hideLabel相似,不过创建的是POPSpringAnimation,让其弹出时带有抖动效果。


    结束。

    相关文章

      网友评论

      本文标题:1.Popping笔记--Flat Button

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