Criando animações em SwiftUI com ChatGPT

Gabriel Okura
Apple Developer Academy PUCPR
5 min readOct 28, 2023

O intuito desse tutorial é te ajudar a entender um pouco mais sobre como funcionam as animações implícitas e explícitas do SwiftUI e compartilhar uma das formas que encontrei de usar o ChatGPT de forma bastante eficiente.

Resultado final (animações, sorting e quiz):

OBS: assim ficou o projeto completo para o desafio de uma semana. O código pode ser acessado através do link do Github.

🔗 Link do Github do projeto: https://github.com/gabrielokura/swiftui-sorting-algorithms

1. Criando vários retângulos alinhados com alturas diferentes

Vamos começar criando a view com os retângulos que serão animados. Para isso utilizamos uma HStack com um loop ForEach dentro. Criaremos também uma lista de valores aleatórios que representarão as alturas de cada retângulo.

import SwiftUI

struct ExemploView: View {
// altura aleatória para cada retângulo desenhado
var rectanglesHeights: [CGFloat] = [100, 200, 140, 40, 80, 260, 240, 160, 180, 60, 120]

var body: some View {
HStack {
// loop para criar um Rectangle para cada valor da lista de alturas
ForEach(rectanglesHeights, id: \.self) { height in
Rectangle()
.fill(.blue)
.frame(width: 15, height: height)
// o segundo frame precisa ser maior que o maior retângulo,
// assim todos ficarão alinhados no mesmo plano.
.frame(height: 300, alignment: .bottom)
}
}
}
}

struct ExemploView_Previews: PreviewProvider {
static var previews: some View {
ExemploView()
}
}

2. Animando os retângulos com animações implícitas e explícitas

A primeira mudança necessária para fazê-los animarem é adicionar @State na lista de alturas. Assim podemos alterar seu valor durante a execução do app e a view será redesenhada para cada alteração.

Para a animação, podemos optar por dois caminhos: animações implícitas e explícitas. As animações implícitas são executadas através do modifier animation() direto na view. Enquanto que as explícitas precisam estar dentro do método withAnimation {} para acontecer.

Caso tenha interesse em aprender mais sobre os dois jeitos, recomendo o artigo “The Secret to Flawless SwiftUI Animations…”.

import SwiftUI

struct ExemploView: View {
@State var rectanglesHeights: [CGFloat] = [100, 200, 140, 40, 80, 260, 240, 160, 180, 60, 120]

var body: some View {
HStack {
ForEach(rectanglesHeights, id: \.self) { height in
Rectangle()
.fill(.blue)
.frame(width: 15, height: height)
.frame(height: 300, alignment: .bottom)
}
}.onTapGesture {
// animando de forma explícita
withAnimation {
// embaralhando a lista de alturas cada vez
// que a tela é tocada
rectanglesHeights.shuffle()
}
}
}
}

struct ExemploView_Previews: PreviewProvider {
static var previews: some View {
ExemploView()
}
}

3. Implementando Sorting Algorithms

A ideia aqui é embaralhar uma lista de valores e, utilizando esses algoritmos, organizá-la e mostrar cada passo da organização (cada troca de posição) na animação da view. Para isso, implementaremos um novo tipo de objeto chamado SwapAnimation que representa cada “frame” da nossa animação.

Por exemplo: o valor da posição 1 troca de lugar com o próximo valor → salvamos essa animação → o algoritmo segue organizando os próximos valores. No final teremos uma lista dos valores ordenados e uma lista de SwapAnimation (usado para mostrar cada troca para o usuário).

class SwapAnimation {
let firstIndex: Int
let secondIndex: Int

init(firstIndex: Int, secondIndex: Int) {
self.firstIndex = firstIndex
self.secondIndex = secondIndex
}

// retorna uma nova lista com os valores localizados
// no firstIndex e secondIndex trocados.
func swap(_ list: [CGFloat]) -> [CGFloat] {
var copy = list

copy[firstIndex] = list[secondIndex]
copy[secondIndex] = list[firstIndex]
return copy
}
}

Também precisamos criar o algoritmo responsável por ordenar a lista. Para esse exemplo utilizaremos o Bubble Sort.

class BubbleSorter {
var type: String = "Bubble sort"
var swapCounter: Int = 0

func sort(components: [CGFloat]) -> [SwapAnimation]{
var copy = components
var swapped = false
var swaps: [SwapAnimation] = []

repeat {
swapped = false

for i in 0..<(copy.count - 1) {
let current = copy[i]
let next = copy[i+1]

if current > next {
copy[i] = next
copy[i + 1] = current
swapCounter += 1
swapped = true
swaps.append(SwapAnimation(firstIndex: i, secondIndex: i+1))
}
}
} while swapped

return swaps
}
}

Pronto, agora temos um algoritmo que organiza uma lista de valores do tipo CGFloat e guarda cada etapa desse processo como um SwapAnimation. Agora como faremos para mostrar todas essas etapas?

A resposta está no pedaço de código a seguir:

func sort() {
isAnimating = true

// recebe uma lista com todos os "frames" da animação
let animations = sorter.sort(components: rectanglesHeights)
animate(animations)
}

func animate(_ animations: [SwapAnimation]) {
if !isAnimating {
return
}

var copy = animations

if copy.isEmpty {
// acabaram os "frames" -> animação acabou
isAnimating = false
return
}

// recebe e remove o primeiro frame da lista
let current = copy.removeFirst()
// atualiza a lista de alturas
rectanglesHeights = current.swap(rectanglesHeights)

// chama a mesma função, animando um novo frame daqui
// 0.5 segundos
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
self.animate(copy)
}
}

Aplicando tudo na view anterior:

4. Por fim, utilizando o ChatGPT para gerar novos algoritmos.

Na última etapa do nosso tutorial, iremos solicitar ao ChatGPT novos pedaços de código que implementem outros algoritmos de Sorting. Para isso, criaremos um protocolo para garantir que o Chat nos responda com códigos funcionais que já retornem o tipo SwapAnimation. (Assim só teremos o trabalho de copiar e colar).

protocol Sorter {
var type: String { get }
var swapCounter: Int { get set }
func sort(components: [CGFloat]) -> ([SwapAnimation])
}

Seguindo para os prompts ou “conversas” com o Chat, a ideia era fazer ele entender a lógica por trás das animações e como elas são formadas, mostrar o protocolo, mostrar as classes BubbleSorter e SwapAnimation e depois pedir novos algoritmos. Foi um sucesso!! O Chat é muito bom em entender pedaços de códigos e a lógica por trás, além disso, é ótimo em resolver esse tipo de exercício.

Vou deixar aqui toda a conversa que gerei caso você tenha interesse:

🔗 Conversa com o ChatGPT completa

Isso é tudo! Obrigado pela leitura até aqui e espero que esse mini artigo tenha te ajudado com alguma coisa.

--

--