iOS移动开发ios学习寒哥管理的技术专题

1.Popping笔记--Flat Button

2015-05-22  本文已影响383人  ForeverYoung21

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,让其弹出时带有抖动效果。


结束。

上一篇下一篇

猜你喜欢

热点阅读