Pular para o conteúdo
charmotion

Lottie no SwiftUI: LottieView, reprodução e loop

Por equipe Charmotion · Atualizado em

Ilustração de capa do guia de iOS e Swift: Lottie no SwiftUI: LottieView, reprodução e loop, com personagens de demonstração do Charmotion

Para usar Lottie no SwiftUI, adicione o pacote lottie-spm e escreva LottieView(animation: .named("idle")).looping(). Use .playing(loopMode: .playOnce) para uma reação única, .playbackMode(.playing(.fromProgress(0, toProgress: 1, loopMode: .loop))) para intervalos e .animationDidFinish para reagir quando ela termina. O Lottie suporta iOS, macOS, tvOS e visionOS, mas não watchOS.

Principais pontos

  • Instale a partir de https://github.com/airbnb/lottie-spm.git: segundo o Airbnb, o clone tem menos de 500 KB, contra mais de 300 MB do repositório principal.
  • LottieView é uma view do SwiftUI com modificadores encadeáveis: .looping(), .playing(loopMode:), .playbackMode(_:), .resizable().
  • Marcadores nomeados permitem que um arquivo contenha vários segmentos, que você seleciona com .marker(_:loopMode:).
  • Se o arquivo tiver um marcador "reduced motion", o Lottie pode tocá-lo no lugar quando Reduzir Movimento estiver ativado.
  • O LottieView não tem state machine com Data Binding; o Rive é a melhor opção para um mascote que reage ao estado do app.

Como instalar o Lottie com o Swift Package Manager?

No Xcode, escolha File → Add Package Dependencies e informe https://github.com/airbnb/lottie-spm.git. O README do Airbnb recomenda esse repositório wrapper para o SwiftPM porque o clone do repositório principal lottie-ios passa de 300 MB, enquanto o lottie-spm tem menos de 500 KB. Adicione a biblioteca Lottie ao target do app e use import Lottie.

Coloque o JSON da animação (ou o arquivo .lottie) no target do app. Por padrão, LottieAnimation.named("idle") procura idle.json no bundle principal.

O manifesto do lottie-spm declara iOS, macOS, tvOS e visionOS. Não há plataforma watchOS, o que importa se você compartilha código de interface com um app para Watch.

Como exibir um LottieView no SwiftUI?

LottieView faz parte do lottie-ios desde a versão 4.3.0. A forma mais simples:

import SwiftUI
import Lottie

struct LoadingMascot: View {
    var body: some View {
        LottieView(animation: .named("loading"))
            .looping()
            .resizable()
            .scaledToFit()
            .frame(width: 180, height: 180)
            .accessibilityLabel("Loading")
    }
}

.resizable() permite que a animação se ajuste ao frame, como uma Image redimensionável; sem ele, a view usa o tamanho intrínseco da animação. Outros inicializadores incluem LottieView(dotLottieFile:) para arquivos .lottie e variantes com closure assíncrona que carregam uma animação (por exemplo, de uma URL) e mostram um placeholder durante o carregamento.

Uma observação sobre custo: o Lottie renderiza formas vetoriais a cada frame. Para um mascote pequeno em loop, tudo bem; para muitas animações simultâneas em uma lista com rolagem, prefira uma imagem estática na lista e anime só o item em foco.

Como fazer um LottieView rodar em loop ou tocar uma vez?

Os modificadores de reprodução correspondem a LottiePlaybackMode:

// Loop forever
LottieView(animation: .named("idle")).looping()

// Play once and stop on the last frame
LottieView(animation: .named("success")).playing(loopMode: .playOnce)

// Play a progress range on loop
LottieView(animation: .named("idle"))
    .playbackMode(.playing(.fromProgress(0, toProgress: 1, loopMode: .loop)))

// Paused on the current frame
LottieView(animation: .named("idle")).paused(at: .currentFrame)

Para reagir quando uma animação de reprodução única termina, adicione .animationDidFinish { completed in … }. Para reiniciá-la sob demanda, controle o modo de reprodução a partir do estado e altere-o:

struct SaveButton: View {
    @State private var playback: LottiePlaybackMode = .paused(at: .progress(0))

    var body: some View {
        VStack {
            LottieView(animation: .named("success"))
                .playbackMode(playback)
                .animationDidFinish { _ in playback = .paused(at: .progress(0)) }
                .frame(width: 120, height: 120)
            Button("Save") {
                playback = .playing(.fromProgress(0, toProgress: 1, loopMode: .playOnce))
            }
        }
    }
}

Outros modos de loop incluem .autoReverse, .repeat(_:) e .repeatBackwards(_:).

Posso trocar de humor no Lottie sem arquivos separados?

Sim, com marcadores (markers). Se o designer adicionar marcadores nomeados no After Effects ou no Lottie Creator (por exemplo idle, happy, sad), você pode tocar um segmento por vez:

enum Mood: String { case idle, happy, sad }

struct MarkerMascot: View {
    let mood: Mood

    var body: some View {
        LottieView(animation: .named("mascot"))
            .playbackMode(.playing(.marker(mood.rawValue, loopMode: .loop)))
            .resizable()
            .scaledToFit()
    }
}

Também existem .fromMarker(_:toMarker:loopMode:) e .markers([…]), que toca vários marcadores em sequência, útil para "reagir e depois voltar ao normal".

A limitação está nas transições. Trocar o marcador corta para o novo segmento onde quer que o anterior estivesse; nada se mistura, a menos que a arte seja feita para que todo segmento comece e termine na mesma pose. É exatamente esse o trabalho que uma state machine faz por você.

Como o LottieView lida com o Reduzir Movimento?

O Lottie 4.3.0 adicionou suporte a movimento reduzido. Se uma animação tiver um marcador chamado reduced motion (o código-fonte também aceita reducedMotion, reduced_motion e reduced-motion), o Lottie toca esse marcador em vez da animação completa quando a opção de movimento reduzido está ativa. Por padrão, ele segue a configuração do sistema.

Se o seu arquivo não tiver esse marcador, trate isso você mesmo:

struct AdaptiveLottie: View {
    @Environment(\.accessibilityReduceMotion) private var reduceMotion

    var body: some View {
        LottieView(animation: .named("idle"))
            .playbackMode(reduceMotion ? .paused(at: .progress(0)) : .playing(.fromProgress(0, toProgress: 1, loopMode: .loop)))
            .resizable()
            .scaledToFit()
    }
}

Escolha um frame de pausa que funcione bem como pose parada; o frame 0 costuma ser uma pose neutra, mas nem sempre. Mais padrões: Reduzir Movimento no SwiftUI e no UIKit.

Como controlar um LottieView a partir do estado do app?

Mantenha a decisão no seu modelo e deixe a view escolher a animação. Um enum pequeno mantém os nomes de arquivo em um só lugar e deixa as reações únicas explícitas:

enum UploadState: Equatable {
    case idle, uploading, done, failed

    var animationName: String {
        switch self {
        case .idle: return "idle"
        case .uploading: return "loading"
        case .done: return "success"
        case .failed: return "error"
        }
    }

    var loopMode: LottieLoopMode {
        switch self {
        case .idle, .uploading: return .loop
        case .done, .failed: return .playOnce
        }
    }
}

struct UploadMascot: View {
    let state: UploadState

    var body: some View {
        LottieView(animation: .named(state.animationName))
            .playing(loopMode: state.loopMode)
            .resizable()
            .scaledToFit()
            .id(state)                 // new view per state, so each clip starts from frame 0
            .transition(.opacity)
            .animation(.easeInOut(duration: 0.2), value: state)
    }
}

.id(state) força uma view nova quando o estado muda, o que reinicia a reprodução do começo e permite que um crossfade curto esconda o corte. Sem isso, o SwiftUI pode reutilizar a view e manter o progresso da animação anterior. Se preferir um único arquivo com marcadores, substitua o nome da animação por .playbackMode(.playing(.marker(…, loopMode:))), como mostrado acima.

É aqui também que os limites do Lottie para personagens ficam evidentes: você mesmo está escrevendo a state machine, um switch de cada vez.

Quais plataformas o Lottie suporta?

Plataformalottie-spmRiveRuntime
iOSYes14.0+
macOSYes13.1+
tvOSYes16.0+
visionOSYes1.0+
watchOSNão declaradoNão declarado

Confira a lista platforms em cada Package.swift para saber o mínimo exato da versão que você fixar; ele muda entre releases. Nenhum dos pacotes declara watchOS, então o Apple Watch precisa de animadores do SwiftUI ou de frames pré-renderizados; veja Animação de personagens no watchOS e no visionOS.

No visionOS, o LottieView funciona dentro de uma janela SwiftUI comum exatamente como no iPhone. No macOS, o mesmo código SwiftUI compila, e apps AppKit podem usar o LottieAnimationView diretamente. Para telas UIKit no iOS, o LottieAnimationView é a classe de view por trás do LottieView; ela expõe play(), loopMode e animationSpeed como propriedades e métodos.

Quando escolher Rive em vez de Lottie para um mascote?

Lottie (LottieView)Rive
FormatoJSON ou .lottie, geralmente do After Effects ou do Lottie Creator.riv binário exportado do editor do Rive
Mudar de estado pelo códigoEscolher um arquivo, marcador ou intervalo de progressoDefinir um número ou disparar um trigger; a state machine faz a transição
Mistura entre estadosFeita à mão; caso contrário, cortaEmbutida nas transições da state machine
Dados bidirecionaisNoPropriedades de Data Binding e streams de triggers
Indicado paraClipes fixos: animações de carregamento, checks de sucesso, ilustraçõesPersonagens que reagem ao app

Muitos apps usam os dois: Lottie para alguns clipes decorativos, Rive para o mascote. Um personagem no Rive com um número mood e triggers substitui o que, de outra forma, seria uma pasta de arquivos Lottie e uma lógica de troca. Veja State machines: Lottie vs Rive para um comparativo mais aprofundado.

Adicione uma animação Lottie em loop a uma view SwiftUI

  1. Adicione o pacote. No Xcode, adicione https://github.com/airbnb/lottie-spm.git e vincule a biblioteca Lottie.
  2. Inclua o arquivo no app. Adicione idle.json (ou um arquivo .lottie) ao target do app.
  3. Exiba a animação. Escreva LottieView(animation: .named("idle")).looping().resizable().scaledToFit() com um frame.
  4. Trate as reações únicas. Use .playing(loopMode: .playOnce) e .animationDidFinish, ou controle .playbackMode a partir do estado.
  5. Respeite o Reduzir Movimento. Adicione um marcador "reduced motion" ao arquivo ou pause em um frame parado quando accessibilityReduceMotion for true.

Perguntas frequentes

Por que usar o lottie-spm em vez do repositório lottie-ios?

Os dois entregam a mesma biblioteca Lottie. A Airbnb recomenda o lottie-spm para o Swift Package Manager porque o repositório principal lottie-ios passa de 300 MB para clonar, enquanto o lottie-spm tem menos de 500 KB, o que torna a resolução de pacotes muito mais rápida no Xcode e no CI.

O LottieView funciona no iOS 13 ou 14?

Confira a lista platforms no Package.swift do lottie-spm para a versão que você fixar, porque os mínimos mudam entre releases. O LottieView em si chegou no lottie-ios 4.3.0. Se o seu deployment target estiver abaixo do que o pacote declara, o Xcode se recusa a resolvê-lo para esse target.

O LottieView consegue tocar um arquivo .lottie (dotLottie)?

Sim. O LottieView tem um inicializador que recebe um DotLottieFile, além de uma variante assíncrona que carrega o arquivo e mostra um placeholder enquanto isso. O dotLottie é um contêiner compactado que pode guardar várias animações e suas imagens em um só arquivo, que costuma ser menor que o JSON equivalente e mantém os assets juntos.

Respostas rápidas deste guia

Fontes

Guias relacionados