Animações em aplicativos iOS nascidos no servidor



Seis meses atrás, apresentamos um dos recursos mais impressionantes do Badoo - transmissão ao vivo . Entre outras coisas, ele permite que os usuários expressem gratidão aos seus streamers favoritos na forma de presentes. Queríamos tornar esses presentes o mais brilhantes e atraentes possíveis, por isso decidimos revivê-los - em outras palavras, animar. E para torná-lo ainda mais interessante, planejamos atualizar presentes e animações a cada poucas semanas.

Os engenheiros do iOS provavelmente adivinharam quanto trabalho está sendo discutido: para remover animações antigas e adicionar novas animações, é necessário executar várias ações no lado do cliente. Para fazer isso, as equipes do Android e iOS devem estar envolvidas em cada versão e, juntamente com o tempo necessário para aprovar a atualização na App Store, isso significa que o lançamento de cada versão com animações atualizadas pode levar vários dias. No entanto, conseguimos resolver esse problema e agora vou lhe dizer como.

Arquitetura da solução


Naquela época, já sabíamos exportar animações do Adobe After Effects (daqui em diante - AAE) em um formato compreensível para nosso aplicativo iOS usando a biblioteca Lottie. Desta vez, fomos um pouco mais longe: decidimos armazenar todas as animações relevantes no servidor e baixá-las conforme necessário.



Um exemplo de animação real em nossa aplicação, obtida desta maneira:



No entanto, neste post, como exemplo, pegarei uma animação simples que eu mesmo criei. Não é tão criativo quanto no Badoo, mas é bastante adequado para demonstrar nossa abordagem.

Exportar animações


O projeto AAE que eu uso pode ser encontrado junto com outras fontes no GitHub . Então, abrindo o projeto localizado em _raw/animations/Fancy/Fancy.aep , você verá uma janela:



Agora, não estou falando sobre o processo de criação de animações no AAE, mas sobre como importar as animações existentes do AAE para um formato adequado para um aplicativo iOS usando o plug- in Bodymovin .

Após instalar o plugin, abra-o selecionando Window / Extensions / Bodymovin no menu :



Aparecerá a janela Bodymovin, na qual é possível selecionar a animação para exportação, uma pasta para salvar o arquivo resultante e abrir as configurações de exportação:



Nas configurações de animação, podemos pedir ao Bodymovin para incluir recursos no arquivo JSON, selecionando Ativos / Incluir no json :



Por fim, pressionando o botão Render , exportamos e salvamos a composição animada selecionada em um arquivo.

Armazenando animações no servidor


Suponha que carregemos arquivos JSON animados renderizados em um servidor da web. No nosso caso, para simplificar, eu os coloquei no repositório do projeto no GitHub. As animações estão disponíveis aqui:



Link base https://raw.githubusercontent.com/chupakabr/server-provided-animations/master/_raw/rendered-animations/

IDs de animação:

  • clouds.json
  • fireworks.json


Nota: Procurando um servidor da Web Swift para animações? A solução está disponível aqui e uma explicação detalhada está neste artigo .


Portanto, temos um servidor ativo com animações, então é hora de passar para a parte mais interessante: renderizar animações na tela.

Exibição de animação


Agora, aconselho a abrir um projeto de demonstração para o nosso aplicativo iOS , pois ele contém todo o código e configurações necessários.

Download de animações


Dado que a API REST para recebimento de dados já está pronta, é hora de introduzir o protocolo do provedor de dados e adicionar sua implementação, que baixa dados do servidor:

 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       }   } } 


Essa classe de provedor de dados nos permite baixar animações no formato JSON do servidor mediante solicitação e armazená-las na memória para renderização na interface do usuário. Suponha que sigamos o padrão MVVM - então é fácil usá-lo na entidade ViewModel seguinte maneira:

  // ... 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 atualiza a propriedade da animação selecionada quando recebe a resposta HTTP correta de um servidor com um objeto JSON não vazio. Esses dados são usados ​​pela camada de apresentação para exibir a animação.

Camada de apresentação


Agora podemos usar o ViewModel para acessar os dados da animação e exibi-los na interface do usuário usando o manipulador de ação de toque integrado ao botão:

 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()   } } 


Quando um botão é clicado, a instância LOTAnimationView é atualizada com os dados mais recentes do ViewModel .

Aqui está o que parece:



Isso é tudo. Agora, o aplicativo exibe a animação baixada da nossa API REST
(do servidor).

Dicas e Limitações


Truques:

  • O AAE suporta a maioria dos tipos de objetos, incluindo imagens raster e vetoriais;
  • O Bodymovin permite incorporar todos os recursos no arquivo JSON final usando o Base64 e, graças a isso, você pode evitar o carregamento de recursos separadamente no lado do cliente;
  • Você pode desenhar diretamente em um vetor em AAE ou simplesmente importar imagens vetoriais no formato Adobe Illustrator.

Infelizmente, não consegui importar arquivos SVG para o AAE (tentei!).

Você pode aprender mais sobre truques e resolver possíveis problemas neste interessante artigo do meu colega Radoslaw Sesiva .

Conclusão


Então, o que o download de animações do servidor nos dá? O benefício mais óbvio dessa abordagem é a capacidade de compartilhar todos os participantes no processo de atualização da animação. Em outras palavras, para lançar uma nova animação interessante, os designers precisam apenas fornecer à equipe do servidor o arquivo JSON apropriado. Para remover a animação no cliente, basta removê-la do servidor. Fácil e rápido.

Também é muito legal que as mesmas funções possam ser implementadas em todas as plataformas suportadas (iOS, Android, Web), sem fazer alterações no protocolo cliente-servidor, código do servidor e arquivos de animação diretamente no cliente.

Isso é tudo. Obrigado pela atenção!


Links úteis


Source: https://habr.com/ru/post/pt439368/


All Articles