Motion Design for iOS——翻译5(20151203)

Jakub设计的音乐播放器动画效果

Jakub是一名来自Slovakia的出色的设计师并且在Dribbble上传了一些具有创造力的界面设计。其中有一个效果给我留下了较为深刻的印象,并且相信会很容易吸引到用户。

在介绍代码实现过程之前,我们先看一下这个炫酷的动画效果:

music.gif

那么这里使用什么技术呢?操纵了动画开始执行的时间。将每个元素一个接着另外一个相继展示在屏幕之上并且整个过程花费相同的时间,所以最终呈现出来的就是一个比较整洁统一的波浪效果,就好像后面出现的元素和前一个元素之间存在着某种粘性一样。

首先,我们需要重新切一些元素来保证我们能够整洁顺利的完成这个动画效果。在这里我选择Photoshop.我们这里不会详细介绍具体的制作过程。
下面是我制作的音乐列表图:
![Upload music1.png failed. Please try again.]

如果你仔细观察原始动画,你就会发现分别有8个不同元素进行了动画:

  1. 返回箭头和“Dance Club”文本
  2. “Ministry of Fun”文本
  3. “Add a Song”按钮
  4. 5行音乐列表

这8个元素通过控制开始动画时间的不同来实现我们最终所看到的较为吸引人的效果。
首先,需要分别将他们添加到界面才能分别对他们执行动画。如果是一个真实的应用界面的话,这个界面将会通过UITableView或者是UICollectionView来有条理的展示列表。使用这两种方式需要你调用一些协议方法来实现界面数据源,比如通过方法来设置每行数据的显示高度以及每行数据。因为我们没有任何数据,而且我们的主要目的是阐述如何实现这种动画效果,所以主要是通过手动将PNG图片添加到屏幕上,首先是顶部的返回箭头和“Dance Club”文本。你可以创建一个**Music Player **项目并实现下列代码:

// Define a variable to get the full width of the window as we’ll
// be using this value a lot.
CGFloat windowWidth = self.window.bounds.size.width;

// Add the background to the window
UIImageView *backgroundView = [[UIImageView alloc] initWithFrame:self.window.bounds];
backgroundView.image = [UIImage imageNamed:@"background"];
[self.window addSubview:backgroundView];

// Add the arrow and top label
UIImageView *arrowView =
    [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, windowWidth, 45)];
arrowView.image = [UIImage imageNamed:@"arrow"];
[self.window addSubview:arrowView];

这里只是简单的将这些View添加到屏幕上,并没有产生一些绚丽的动画效果。背景图片命名为@"background",而@"arrow"图片则是包括了顶部的返回按钮以及“Dance Club”文本,因为他们需要一起执行动画,将他们做在一个图片中应该会更简单。
现在界面效果如下图所示:

music2.png

让我们来继续添加更多内容吧!

// Ministry of Fun image
UIImageView *ministryView =
    [[UIImageView alloc] initWithFrame:CGRectMake(0, 57, windowWidth, 28)];
ministryView.image = [UIImage imageNamed:@"ministry"];
[self.window addSubview:ministryView];

// Add a Song button
UIButton *addButton = [UIButton buttonWithType:UIButtonTypeCustom];
[addButton setImage:[UIImage imageNamed:@"add-button"] forState:UIControlStateNormal];
[addButton setImage:[UIImage imageNamed:@"add-button-pressed"]
    forState:UIControlStateHighlighted];
[addButton setFrame:CGRectMake(0, 102, windowWidth, 45)];
[self.window addSubview:addButton];

添加了一个展示“Ministry of Fun”的imageView,并且创建了一个“Add a Song”按钮。在这里偷了个懒,只是设置了ButtonUIImageView属性,因只是需要设置按钮的正常和电机两种状态,所以通过调用-setImage:forState:来分别设置两种状态下的不同图片。
现在的界面效果以及按钮的点击效果如下图所示:

musicbutton.gif

高亮状态下的图片背景色是白色而不是white stroke.
现在需要往屏幕上添加音乐列表了,他们也是UIImageView.

// Katy Perry row
UIImageView *firstRow =
    [[UIImageView alloc] initWithFrame:CGRectMake(0, 170, windowWidth, 80)];
firstRow.image = [UIImage imageNamed:@"1st-row"];
[self.window addSubview:firstRow];

// Shakira row
UIImageView *secondRow =
    [[UIImageView alloc] initWithFrame:CGRectMake(0, 170+80, windowWidth, 80)];
secondRow.image = [UIImage imageNamed:@"2nd-row"];
[self.window addSubview:secondRow];

// Pitbull row
UIImageView *thirdRow =
    [[UIImageView alloc] initWithFrame:CGRectMake(0, 170+160, windowWidth, 80)];
thirdRow.image = [UIImage imageNamed:@"3rd-row"];
[self.window addSubview:thirdRow];

// Lana del Rey row
UIImageView *fourthRow =
    [[UIImageView alloc] initWithFrame:CGRectMake(0, 170+240, windowWidth, 80)];
fourthRow.image = [UIImage imageNamed:@"4th-row"];
[self.window addSubview:fourthRow];

// HEX row
UIImageView *fifthRow =
    [[UIImageView alloc] initWithFrame:CGRectMake(0, 170+320, windowWidth, 80)];
fifthRow.image = [UIImage imageNamed:@"5th-row"];
[self.window addSubview:fifthRow];

你应该已经发现了,列表每行的frame的Y坐标之间之间都有一个简单的数学等式。每行都是80px高,所以下一个的Y坐标都在前一个的Y坐标的基础上加上了80。同样也可以通过Auto Layout来添加列表,但是对于一个例子没有必要非要这么做。

下图是设置动画效果之前的截图效果:

music3.png

但是,我们并不是想要整个界面展示这种效果,整个练习的重点是图片从屏幕之外并且从右边开始,然后移动向屏幕左边,并最终展示在终点位置,而不是一开始就出现在最终位置。
让我们重新审视并且修改我们的代码,使得元素的frame属性的X坐标值不再是0,而是windowWidth。这会使得每个元素的左侧对齐屏幕的右边缘。

// Add the arrow and top label
UIImageView *arrowView =
   [[UIImageView alloc] initWithFrame:CGRectMake(windowWidth, 0, windowWidth, 45)];
arrowView.image = [UIImage imageNamed:@"arrow"];
[self.window addSubview:arrowView];

// Ministry of Fun label
UIImageView *ministryView =
    [[UIImageView alloc] initWithFrame:CGRectMake(windowWidth, 57, windowWidth, 56/2)];
ministryView.image = [UIImage imageNamed:@"ministry"];
[self.window addSubview:ministryView];

// Add a Song button
UIButton *addButton = [UIButton buttonWithType:UIButtonTypeCustom];
[addButton setImage:[UIImage imageNamed:@"add-button"]
    forState:UIControlStateNormal];
[addButton setImage:[UIImage imageNamed:@"add-button-pressed"]
    forState:UIControlStateHighlighted];
[addButton setFrame:CGRectMake(windowWidth, 102, windowWidth, 45)];
[self.window addSubview:addButton];

// Katy Perry row
UIImageView *firstRow =
    [[UIImageView alloc] initWithFrame:CGRectMake(windowWidth, 170, windowWidth, 80)];
firstRow.image = [UIImage imageNamed:@"1st-row"];
[self.window addSubview:firstRow];

// Shakira row
UIImageView *secondRow =
    [[UIImageView alloc] initWithFrame:CGRectMake(windowWidth, 170+80, windowWidth, 80)];
secondRow.image = [UIImage imageNamed:@"2nd-row"];
[self.window addSubview:secondRow];

// Pitbull row
UIImageView *thirdRow =
    [[UIImageView alloc] initWithFrame:CGRectMake(windowWidth, 170+160, windowWidth, 80)];
thirdRow.image = [UIImage imageNamed:@"3rd-row"];
[self.window addSubview:thirdRow];

// Lana del Rey row
UIImageView *fourthRow =
    [[UIImageView alloc] initWithFrame:CGRectMake(windowWidth, 170+240, windowWidth, 80)];
fourthRow.image = [UIImage imageNamed:@"4th-row"];
[self.window addSubview:fourthRow];

// HEX row
UIImageView *fifthRow =
    [[UIImageView alloc] initWithFrame:CGRectMake(windowWidth, 170+320, windowWidth, 80)];
fifthRow.image = [UIImage imageNamed:@"5th-row"];
[self.window addSubview:fifthRow];

现在我们开始通过block方式将返回按钮和“Dance Club”文本移动到屏幕左侧。

[UIView animateWithDuration:1.1 delay:0 usingSpringWithDamping:0.3
    initialSpringVelocity:0 options:0 animations:^{
    [arrowView setFrame:CGRectMake(0, 0, windowWidth, 45)];
} completion:NULL];

这段代码表示,这个弹簧效果动画会持续1.1秒,并且阻尼(damping)为0.3.效果如下图所示:

musicbutton2.gif

但是上面的弹簧动画效果来的太快和生硬,这样会给用户一种紧张的感觉,这样的动画效果并不会提升用户体验。每一种动画效果都会影响用户心情,而这也包括一些不好的情绪。
将动画持续时间改为2.1秒后,我们再来感受下现在的效果:

![Uploading musicbutton3_707114.gif . . .]

这种效果与Jakub的最初的设计效果相比,弹性又太大了,所以我们仍然需要调整这种弹簧效果的阻尼(damping)值。将该值从0.3提高到0.6(这个值越接近1弹性效果就会越来越不明显)。因为我们仍然需要那么一点点的弹性效果,接下来看下现在的效果吧:

musicbutton4.gif

那,现在的效果已经也还可以了,通过使用iOS7的弹簧动画效果,可以修改某些参数来达到你想要实现的一些弹簧效果。但是,就我个人而言,我仍然觉得JNWSpringAnimation提供的一些方法更加方便,而且更能实现相对真实的物理弹簧运动效果。
现在我们重新对其它元素执行动画。每个元素都会比前一个元素稍微晚点执行动画。而且,我需要控制在应用出现后多久执行所有动画效果。

CGFloat initialDelay = 1.0f;
CGFloat stutter = 0.3f;

// Animate the top arrow image
[UIView animateWithDuration:2.1 delay:initialDelay
    usingSpringWithDamping:0.6 initialSpringVelocity:0 options:0 animations:^{
    [arrowView setFrame:CGRectMake(0, 0, windowWidth, 45)];
} completion:NULL];

// Animate the image label Ministry of Fun
[UIView animateWithDuration:2.1 delay:initialDelay + (1 * stutter)
    usingSpringWithDamping:0.6 initialSpringVelocity:0 options:0 animations:^{
    [ministryView setFrame:CGRectMake(0, 57, windowWidth, 28)];
} completion:NULL];

通过定义一个CGFloat类型的变量stutter来存储一个固定动画执行间隔。这个数值能够决定整个动画效果给用户所带来的感觉。各个元素动画间隔时间过长则会给用户一种动画执行不太连贯的效果,太短又会使得最终效果不像我们最初想要做的波浪效果。
第一个动画中的delay属性是设置的initialDelay属性值,因为这个是第一个执行动画的元素,而第二个执行动画的元素的delay属性值为initialDelay + (1 * stutter),并以此类推,效果如下所示:

musicbutton5.gif

到这里为止,动画已经差不多了,但是很难凭现在的两个元素的动画效果来断定动画效果的好坏,现在需要接着完成接下来的所有元素的动画效果。

[UIView animateWithDuration:2.1 delay:initialDelay + (2 * stutter)
    usingSpringWithDamping:0.6 initialSpringVelocity:0 options:0 animations:^{
    [addButton setFrame:CGRectMake(0, 102, windowWidth, 45)];
} completion:NULL];

[UIView animateWithDuration:2.1 delay:initialDelay + (3 * stutter)
    usingSpringWithDamping:0.6 initialSpringVelocity:0 options:0 animations:^{
    [firstRow setFrame:CGRectMake(0, 170, windowWidth, 80)];
} completion:NULL];

[UIView animateWithDuration:2.1 delay:initialDelay + (4 * stutter)
    usingSpringWithDamping:0.6 initialSpringVelocity:0 options:0 animations:^{
    [secondRow setFrame:CGRectMake(0, 170+80, windowWidth, 80)];
} completion:NULL];

[UIView animateWithDuration:2.1 delay:initialDelay + (5 * stutter)
    usingSpringWithDamping:0.6 initialSpringVelocity:0 options:0 animations:^{
    [thirdRow setFrame:CGRectMake(0, 170+160, windowWidth, 80)];
} completion:NULL];

[UIView animateWithDuration:2.1 delay:initialDelay + (6 * stutter)
    usingSpringWithDamping:0.6 initialSpringVelocity:0 options:0 animations:^{
    [fourthRow setFrame:CGRectMake(0, 170+240, windowWidth, 80)];
} completion:NULL];

[UIView animateWithDuration:2.1 delay:initialDelay + (7 * stutter)
    usingSpringWithDamping:0.6 initialSpringVelocity:0 options:0 animations:^{
    [fifthRow setFrame:CGRectMake(0, 170+320, windowWidth, 80)];
} completion:NULL];

现在整体的动画效果如下图所示:

musicbutton7.gif

但是这个效果仍然不是最好的,因为元素动画之间的间隔时间好像太长了,导致整体的动画效果不是特别流畅。我们将元素执行动画的时间间隔stutter从0.3降到0.15来看看具体的展示效果:

![Upload musicbutton8.gif failed. Please try again.]

这已经与我们的设想很接近了,我们仍然可以继续调整使得最终效果更加流畅统一,就好像元素微微的拉着下一个元素一样,将stutter继续降低到0.06:

musicbutton9.gif

现在我们可以看到,最终呈现的效果已经与最初的设计效果很接近了,而且并没有花费太长时间来实现。

有趣的position动画效果

当我的新闻应用启动的时候,首先通过发送网络请求来下拉刷新最新的文章。我们需要利用返回数据来构建UITableView数据,每一行表示一条文章数据。有的应用在返回数据之前隐藏整个列表,有的则会将cell一个接一个的展示在合适的位置,而其他的则是不运用任何动画效果。我选择刚才我们创建的那种动画效果来实现,但不是水平方向的动画,而是从底部垂直方向的波浪效果。

int.gif

要完成这个动画效果,我们需要一步步的分解整个动画效果:

  1. 当请求返回数据的时候调用[self.tableView reloadData]方法,会立即展示出列表。所以我们需要将列表的opacity属性设置为0,这样我就可以控制用户在什么时候看到列表。
  2. 然后调用[self.tableView reloadData]会利用返回数据刷新列表顶部数据,在这个时候所有的数据都在他们应该处在的位置,但是这个时候opaticy属性值仍然为0,所以这个时候屏幕上看不到任何元素。
  3. 遍历所有的UITableViewCell,通过改变他们的positon属性值来将他们移动到屏幕的最底部,直到所有的行都不在屏幕上为止。
  4. 因为所有行都在屏幕底部而不显示在屏幕上,通过将alpha属性设置为1,将列表显示,但是这个时候仍然看不到,因为他们的position全都在屏幕之外。
  5. 然后通过改变每行的position来将每行数据展示在适当的位置。
    这就是实现这种效果所需的所有步骤:
// Make the table invisible, then reload its data
self.tableView.alpha = 0.0f;
[self.tableView reloadData];

// Store a delta timing variable so I can tweak the timing delay
// between each row’s animation and some additional 
CGFloat diff = .05;
CGFloat tableHeight = self.tableView.bounds.size.height;
NSArray *cells = [self.tableView visibleCells];

// Iterate across the rows and translate them down off the screen
for (NSUInteger a = 0; a < [cells count]; a++) {
    UITableViewCell *cell = [cells objectAtIndex:a];
    if ([cell isKindOfClass:[UITableViewCell class]]) {

        // Move each cell off the bottom of the screen by translating its Y position
        cell.transform = CGAffineTransformMakeTranslation(0, tableHeight);
    }
}

// Now that all rows are off the screen, make the tableview opaque again
self.tableView.alpha = 1.0f;

// Animate each row back into place
for (NSUInteger b = 0; b < [cells count]; b++) {
    UITableViewCell *cell = [cells objectAtIndex:b];
    
    [UIView animateWithDuration:1.6 delay:diff*b usingSpringWithDamping:0.77
        initialSpringVelocity:0 options:0 animations:^{
        cell.transform = CGAffineTransformMakeTranslation(0, 0);
     } completion:NULL];
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,294评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,493评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,790评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,595评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,718评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,906评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,053评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,797评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,250评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,570评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,711评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,388评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,018评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,796评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,023评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,461评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,595评论 2 350

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,825评论 25 707
  • 动画翻译3 自然的动画 可以使用标准的时间曲线,但那并不是最优方案,并且不一定会带给用户令人赞叹和愉悦的感觉,因为...
    SHChen阅读 713评论 0 2
  • 问答题47 /72 常见浏览器兼容性问题与解决方案? 参考答案 (1)浏览器兼容问题一:不同浏览器的标签默认的外补...
    _Yfling阅读 13,737评论 1 92
  • 让自己努力赚钱,不是因为我爱钱,而是这辈子,我不想因为钱和谁在一起,也不想因为钱而离开谁!看了图中人物多么寒心,没...
    wonderfulbook阅读 164评论 0 1
  • 经常的一个错误,在开发环境中、html文件里面的一些链接有dev,pre,prod分别的。环境迁移时,很容易忘记修...
    FlynnHai阅读 1,145评论 0 0