1.Popping笔记--Flat Button

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


结束。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,529评论 5 475
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,015评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,409评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,385评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,387评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,466评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,880评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,528评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,727评论 1 295
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,528评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,602评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,302评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,873评论 3 306
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,890评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,132评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,777评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,310评论 2 342

推荐阅读更多精彩内容

  • 发现 关注 消息 iOS 第三方库、插件、知名博客总结 作者大灰狼的小绵羊哥哥关注 2017.06.26 09:4...
    肇东周阅读 12,010评论 4 62
  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,361评论 25 707
  • 白纸脱胎换骨的基本表现就在于能跳出空白格,成为名著经典万人成诵的书,成为大富大贵价值连城的画。作为一张不值一文的白...
    红颜闺蜜阅读 441评论 0 1