用 Ophiuchus 实现文字动画

来源:互联网 发布:时间序列异常检测算法 编辑:程序博客网 时间:2024/05/14 15:26

原文:Text animations with Ophiuchus
作者:Marin Todorov
译者:kmyhy

本教程适用于 Xcode 7/Swift 2 及兼容版本。

Ophiuchus 是一个由 Yalantis 编写的开源库——Yalantis 在 GitHub 上编写了大量和动画相关的开源库,这里列出了其中一些代表作:

  • 一个可收起的 tab bar
  • 动画侧边栏
  • 一个下拉刷新控件

本月度选择 Ophiuchus 作为话题,不仅仅因为它集合了各种框架的 API,能够创建令人难以置信的动画。同时也因为它创建的这些动画非常灵活,你可以根据自己的需要调整它。

这里有一个例子,来自于 Ophicuchus 的 GitHub 上的 reame:

如你所见,这是一个非同寻常的动画,确实能够让你的 app 档次提升不少。

在这个教程中,你将学习如何使用 Ophicuchus 创建一个显示“Think Different 广告词”的效果:

1. 开始使用 Ophiuchus

新建 Single View Xcode 项目,名为 ThinkDifferent。导入 cocoapod “Ophiuchus”,打开 对应的 xcworkspace 文件。

创建一个 Objective-C 桥接头文件,导入我们要使用的

#include "YALLabel.h"#include "YALPathFillAnimation.h"

注意:如果你不熟悉 Cocoapods,请看一下 iOS Animations by Tutorials 第二版的第 25 章“开始使用 Pop”。其中介绍了如何将 Cocoapod 安装到 Xcode 项目,以及如何创建 O-C 桥接头文件。

打开 target 的 General 标签页,禁用竖屏模式:

创建项目的最后一个步骤是将默认 view controller 的背景设置为黑色:

开始写代码了!

让我们添加几个字到屏幕上,你可以使用 YALLabel。因为你将在动画中多次创建 label——可以编写一个工厂方法用于创建 label。

在 ViewController.swift 中,添加新方法:

func labelWithText(text: String) -> YALLabel {    let label = YALLabel(frame: view.bounds)    label.center = view.center    label.text = text    label.fontName = "HelveticaNeue"    label.fontSize = 70.0    return label}

这段代码创建了一个 YALLabel,大小等同于屏幕并居中对齐。字体设置为 HelveticaNeue,字号为 70pt(让文本适应 iPhone 6s 的宽度)。

你应该看到了,它的 API 和 UILabel 稍有不同,但很容易上手。

接下来使用 YALLabel 自定义绘制。在 labelWithText(_:) 方法的 return 语句之前加入:

label.strokeColor = UIColor(red: 1.0, green: 1.0, blue: 0.5, alpha: 0.85)label.strokeWidth = 2label.fillColor = UIColor.redColor()label.backgroundFillColor = UIColor(white: 0.25, alpha: 1.0)

继续编写将 label 显示到屏幕的代码:

override func viewDidAppear(animated: Bool) {    super.viewDidAppear(animated)    let label = labelWithText("Think Different")    view.addSubview(label)}

运行项目,看看效果:

文字用 2pt 粗进行淡黄色的描边,描边颜色由 strokeColor 属性指定。每个字符以红色填充,填充色由 fillColor 属性指定。

干得不错!但 backgroundFillColor 是用来干嘛的?

如果文字不进行填充,则这会指定字符的背景色。现在你看不见背景色,因为所有字符都是 100% 的红色填充——但等一会我们将让填充色执行动画,你就可以看到 fillColor 和 backgroundFillColor 如何一起使用了。

2. 增加基本动画流程

你还要写一堆的代码用于实现后面的渐入渐出动画。如果你阅读过 iOS Animations by Tutorials 的“视图动画”,你会对接下来的内容非常熟悉。

在 ViewController 中添加一个助手方法:

func delay(seconds seconds: Double, completion:()->()) {    let popTime = dispatch_time(DISPATCH_TIME_NOW, Int64( Double(NSEC_PER_SEC) * seconds ))    dispatch_after(popTime, dispatch_get_main_queue()) {        completion()    }}

这是一个延迟执行某个闭包的简单方法。它会被用来调度动画的不同过程。

回到 labelWithText(_:) 方法,添加最后一段代码——就在 return 一句之前:

label.transform = CGAffineTransformMakeTranslation(0.0, -view.frame.size.height/2 - 40)

用一个平移矩阵转换将 label 移动到屏幕上边以外:

然后添加一个方法用于将 label 移回屏幕中心,然后继续移动直到抵达屏幕底部。然后移除这个 label 因为新的 label 已经来到了顶部。

添加一个新方法,用于创建这个运动动画:

func animateLabel(label: YALLabel) {    UIView.animateWithDuration(1.0, delay: 0.0, options: .CurveEaseOut, animations: {        label.transform = CGAffineTransformIdentity        }, completion: nil)    UIView.animateWithDuration(1.0, delay: 2.5, options: .CurveEaseIn, animations: {        label.transform = CGAffineTransformMakeTranslation(0.0, self.view.frame.size.height/2)        label.alpha = 0.25        }, completion: {_ in            label.removeFromSuperview()    })}

这里创建了两个视图动画。第一个重置了 label 的 transform,这会将 label 运动到屏幕的中心。后一个动画创建了一个平移动画,将 label 移动到屏幕底部;completion 块中直接将 label 移出视图。

在 viewDidAppear 方法中添加这句,看看你的工作进展:

animateLabel(label)

运行 app,你会看到基本的动画:

这是一个不错的动画,是待会你要实现的、更炫的、文本填充动画的基础!

下面来结束这个长长的准备阶段吧!

首先修改 viewDidAppear 方法:

override func viewDidAppear(animated: Bool) {    super.viewDidAppear(animated)    let text = [        "Here’s to", "the crazy ones.", "The rebels.", "The troublemakers.", "The ones who see", "things differently.",        "While some", "may see them", "as the crazy ones,", "we see genius.", "Because the people", "who are crazy", "enough",        "to think they can", "change the world,", "are the ones", "who do."    ]    animate(text)}

在新代码中你创建了一个数组,包含了由“Think Different”广告中的短句(来源:http://en.wikipedia.org/wiki/Think_different#Text)。

然后将所有短句传递给 animate(_:) 方法,当然,这个方法还没写,让我们立即来完成这个方法:

func animate(var strings: [String]) {    if strings.count == 0 {        return    }    let label = labelWithText(strings.removeAtIndex(0))    view.addSubview(label)}

animate(_:) 方法会不停地调用自己,直到它已经将所有短句在屏幕上运动起来——所以一开始会有一个 if 语句检查 strings 参数中是否还有剩余的短句。

只要里面还有至少一条短句——你都会从 strings 中拿到它并创建和显示一个 label。

然后对 label 进行动画(动画代码已经写好),添加:

delay(seconds: 0.1, completion: {    self.animateLabel(label)})

这会在当前短句上运行 animateLabel(_:) 中的移动动画。当动画块完成时,你再显示下一句——在 animated(_:) 方法加入:

delay(seconds: 2.2, completion: {    self.animate(strings)})

这会每隔 2.2 秒调用 animate(_:) 方法——这个时间足够在文字消失之前完成每个短句的显示了。

Build & run,欣赏一下这个动画:

YALPathFillAnimation

在最后这一节你将用 YALPathFillAnimation 创建某种酷炫的填充动画。

每个 YALLabel 都有 3 个属性分别是:

  • strokeLayer
  • fillLayer
  • backgroundLayer

这 3 个 layer 都继承了 CAShapeLayer,这样你就可以对它们的进度、颜色进行动画。这个 3 个 layer 分别用于绘制 YALLabel 的描边、填充和背景。

YALPathFillAnimation 类允许你轻易实现文字填充动画,你将用它创建一个基本的动画。

首先你建从当前正在显示字符串中获取每个字符的 fillLayer。在 animateLabel(_:) 中加入:

let letters = label.fillLayer!.sublayers as! [YALProgressAnimatingLayer]

YALLabel 的 fillLayer 的 subLayers 会获得一个由每个字符的 fillLayer 构成的数组。这些 layer 都是 YALProgressAnimatingLayer 对象——这个类是 CAShapeLayer 子类,允许你实现形状填充动画。

现在遍历每个字符的 layer,并逐个执行填充动画,添加代码:

for letter in letters {    let fill = YALPathFillAnimation(path: letter.mask.path, andDirectionAngle: 180.0)    fill.duration = 1.5    fill.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseOut)    letter.mask.addAnimation(fill, forKey: nil)}

你为每个字符创建一个 YALPathFillAnimation,这需要用字符的 mask 路径和角度作为参数。第二个参数指定填充方向。如果设置为 0,填充动画为向上填充,设置为 180 度,则填充动画向下填充。

YALPathFillAnimation 继承了 CABasicAnimation,因此你完全可以在 CALayer 动画中使用 druation、timingFunction、fillMode 等属性。

注意:如果你需要回顾一下图层动画、fillMode、timingFunction 类似的东西,你可以参考 iOS Animations by Tutorials 第二版的第 8 章“图层动画-开始”。

这个填充动画一开始是这个样子:

很好!你已经走在通向成功的路上了——有几个地方稍微修改一下就可以开始动画了。

Time offsetting

接下来,你需要给每个字符的填充动画加上一点时间差。

在 for 循环之前插入:

    let deltaTimeOffset = 2.0 / Double(letters.count)    var timeOffset = CACurrentMediaTime() + 0.5

将整个动画的时长 2 秒除以字符数,保存到 deltaTimeOffset 变量。

每当开始一个字符动画,就在 timeOffset 的基础上加上 deltaTimeOffset,作为每个动画开始的时间。

找到这句:letter.mask.addAnimation(fill, forKey: nil) ,在它之前插入:

fill.fillMode = kCAFillModeBackwardsfill.beginTime = timeOffsettimeOffset += deltaTimeOffset

这段代码会对文字用背景色进行填充(0% 填充时会看到这个颜色,感谢这个动画填充模式),每个字符的填充动画会有一个 deltaTimeOffset 的时间差。

运行 app:

每个动画之间的时间差导致了一种“波浪形动画”的效果——越来越有意思了!

从左到右或反方向

为了让效果的变化更加丰富,我们可以改变每个短句波浪形填充的方向。

首先添加一个类属性,用于代表动画丰富:

var isOdd = true

找到这行: let letters = label.fillLayer!.sublayers as! [YALProgressAnimatingLayer]。

将它替换为:

let letters = (isOdd ? label.fillLayer!.sublayers : label.fillLayer!.sublayers!.reverse())  as! [YALProgressAnimatingLayer]isOdd = !isOdd

你将每个短句动画中的文字流方向进行了变化。当你运行 app 时,你会看到“波浪形填充”会从左到右来一次然后又从右到左来一次,循环往复。

你也可以对字符进行随机或乱序,以产生更疯狂的效果。

添加彩虹效果

最后,你将修改每个字符的填充色为各不相同,以实现一种“彩虹效果”,这样的动画就更炫了。

要为每个字符生成不同的颜色,需要用 UIColor(hue:,saturation:,brightness:,alpha) 方法。这个初始化方法用 0-1 之间的灰度值表示,这个值表示的是颜色在色盘中的位置:

要获得全部颜色也简单——将 1.0 除以字符数就可以得到两个字符之间的灰度值偏差。然后在创建每个字符的动画之前,设置它们的 fillColor 就能产生彩虹效果。

回到 animateLabek(_:),在 for 循环之前添加这两句:

let deltaHue: CGFloat = 1.0 / CGFloat(letters.count)var currentHue: CGFloat = 0.0

对于每个短句,你将从灰度值 0.0(红色)开始,每个字符递增 deltaHue,最后一个字符就是 1.0(还是红色)。

然后来写代码,在 for 循环之后添加:

letter.fillColor = UIColor(hue: currentHue, saturation: 1.0, brightness: 1.0, alpha: 1.0).CGColorcurrentHue += deltaHue

这段代码设置每个字符的 fillColor ,并递增 currentHue 给下个字符。超简单!

运行项目,查看最终的彩虹效果:

这个动画就完成了!我希望你喜欢这次课程,请访问 Ophiuchus 的 GitHub 库。

对于那些喜欢 Think Different 广告词的人来说——还有一个加分的部分。在 viewDidAppear(_:) 中添加下段代码,可以看到最终完整的动画:

let apple = UILabel(frame: view.bounds)apple.font = UIFont(name: "HelveticaNeue", size: 160.0)apple.textColor = UIColor(white: 1.0, alpha: 0.5)apple.text = ""apple.textAlignment = NSTextAlignment.Centerapple.alpha = 0.0view.addSubview(apple)UIView.animateWithDuration(5.0, delay: 40.0, options: [], animations: {    apple.alpha = 1.0    }, completion: nil)
原创粉丝点击