
六个月前,我们推出了Badoo最令人印象深刻的功能之一-
实时流媒体 。 除其他事项外,它还允许用户以礼物的形式对自己喜欢的彩带表示感谢。 我们希望使这些礼物尽可能明亮和吸引人,因此我们决定复兴它们-换句话说,赋予生命。 为了使它更加有趣,我们计划每几周更新一次礼物和动画。
iOS工程师必须已经猜到有什么工作要做:为了删除旧动画并添加新动画,必须在客户端执行许多操作。 为此,Android和iOS团队必须参与每个版本,并且在App Store中批准更新所需的时间,这意味着每个发布带有更新动画的版本可能要花费几天的时间。 但是,我们设法解决了这个问题,现在我将告诉您如何解决。
解决方案架构
到那时,我们已经知道如何使用Lottie库
以我们的iOS应用程序可以
理解的格式导出Adobe After Effects动画(以下称为AAE) 。 这次我们走得更远:我们决定将所有相关的动画存储在服务器上,并根据需要下载它们。

通过以下方式获得的应用程序中真实动画的示例:
但是,在本文中,我将以自己创建的简单动画为例。 它不像Badoo那样具有创造力,但是非常适合演示我们的方法。
导出动画
可以
在GitHub上找到我使用的AAE项目以及
其他资源 。 因此,打开位于
_raw/animations/Fancy/Fancy.aep
,您将看到一个窗口:

现在,我不是在讨论在AAE中创建动画的过程,而是在讨论如何使用
Bodymovin插件从AAE将现有动画导入适合iOS应用程序的格式。
安装插件后,通过
从菜单中选择
Window / Extensions / Bodymovin来打开它:

将显示“ Bodymovin”窗口,您可以在其中选择要导出的动画,用于保存结果文件的文件夹并打开导出设置:

在动画设置中,我们可以通过选择
Assets / Include in json来要求Bodymovin在JSON文件中包括资源:

最后,通过按“
渲染”按钮,我们将选定的动画构图导出并保存到文件中。
在服务器上存储动画
假设我们将渲染的动画JSON文件上传到Web服务器。 在我们的案例中,为简单起见,我将它们放在GitHub上的项目存储库中。 动画在这里可用:
基本链接
https://raw.githubusercontent.com/chupakabr/server-provided-animations/master/_raw/rendered-animations/动画ID:
clouds.json
fireworks.json
注意:是否正在寻找用于动画的Swift Web服务器? 该解决方案在这里可用,并且本文中有详细的说明。
因此,我们有一台具有动画的工作服务器,因此该着手最有趣的部分了:在屏幕上渲染动画。
动画展示
现在,我建议您为
我们的iOS应用程序打开一个
演示项目,因为它包含所有必要的代码和设置。
下载动画
鉴于用于接收数据的REST API已经准备就绪,是时候介绍数据提供者的协议并添加其实现了,该实现将从服务器下载数据:
import Lottie protocol AnimationsProviderProtocol { typealias Completion = (_ animation: LOTComposition?) -> Void func loadAnimation(byId id: String, completion: @escaping Completion) } final class ServerAnimationProvider: AnimationsProviderProtocol { private let endpoint: URL init(endpoint: URL) { self.endpoint = endpoint } func loadAnimation(byId id: String, completion: @escaping Completion) { let path = "/\(id).json" guard let animationUrl = URL(string: path, relativeTo: self.endpoint) else { completion(nil) return } URLSession.shared.invalidateAndCancel() let task = URLSession.shared.dataTask(with: animationUrl) { (data, response, error) in guard error == nil, let data = data, let json = self.parseJson(from: data) else { completion(nil) return } let animation = LOTComposition(json: json) completion(animation) } task.resume() } private func parseJson(from data: Data?) -> [AnyHashable : Any]? { guard let data = data else { return nil } do { let json = try JSONSerialization.jsonObject(with: data, options: []) as? [AnyHashable : Any] return json } catch { return nil } } }
该数据提供程序类允许我们根据请求从服务器下载JSON格式的动画,并将其存储在内存中以呈现到UI。 假设我们遵循MVVM模式-那么很容易在
ViewModel
实体中使用它,如下所示:
// ... private let animationProvider: AnimationsProviderProtocol private(set) var animationModel: LOTComposition? // … func loadAnimation(byId animationId: String) { self.animationProvider.loadAnimation(byId: animationId) { [weak self] (animationModel) in self?.animationModel = animationModel } } // ...
ViewModel
从带有非空JSON对象的服务器接收到正确的HTTP响应时,
ViewModel
更新所选动画的属性。 表示层使用此数据来显示动画。
表示层
现在,我们可以使用
ViewModel
来访问动画数据,并使用附加到按钮的内置点击动作处理程序将其显示在UI上:
class ViewController: UIViewController { // ... @IBOutlet weak var animationContainer: UIView! override func viewDidLoad() { super.viewDidLoad() // ... self.animationView = { let view = LOTAnimationView(frame: self.animationContainer.bounds) self.animationContainer.addSubview(view) return view }() } @IBAction func onPlayAnimationAction(_ sender: Any) { self.animationView.stop() self.animationView.sceneModel = self.viewModel.animationModel self.animationView.play() } }
单击按钮时,将使用
ViewModel
的最新数据更新LOTAnimationView实例。
看起来是这样的:
仅此而已。 现在,该应用程序显示从REST API下载的动画
(来自服务器)。
提示与限制
技巧:
- AAE支持大多数类型的对象,包括光栅和矢量图像。
- Bodymovin允许您使用Base64将所有资源嵌入最终的JSON文件中,因此,您可以避免在客户端分别加载资源。
- 您可以直接在AAE中绘制矢量,也可以简单地以Adobe Illustrator格式导入矢量图像。
不幸的是,我无法将SVG文件导入AAE(我尝试过!)。
您可以从我的同事
Radoslaw Sesiva的这篇
有趣的文章中了解更多有关技巧和解决可能出现的问题的
信息 。
结论
那么,从服务器下载动画会给我们带来什么呢? 这种方法最明显的好处是可以共享动画更新过程中的所有参与者。 换句话说,要发布新的炫酷动画,设计人员只需要向服务器团队提供适当的JSON文件即可。 要在客户端上删除动画,只需将其从服务器上删除即可。 简单快速。
同样很酷的是,可以在所有受支持的平台(iOS,Android,Web)上实现相同的功能,而无需直接在客户端上更改客户端-服务器协议,服务器代码和动画文件本身。
仅此而已。 感谢您的关注!
有用的链接