Ilustração com dois iphones sugerindo uma navegação entre telas.

Como interpretar o arrastar do dedo no Swift e SwiftUI

Como utilizar o DragGesture do SwiftUI

Thierri Cegantin

Criado à 4 meses atrás

No desenvolvimento do seu aplicativo é bem provável que você queira interpretar os gestos que seu usuário faz com o dedo na tela. Felizmente o SwiftUI tem ferramentas que tornam esse processo bem simples.
É possível com muita facilidade implementar e interpretar os seguintes gestos:
  • DragGesture: gesto de arrastar um dedo na tela.
  • TapGesture: gesto de "clicar" com o dedo um elemento na tela.
  • MagnifyGesture: gesto de pinça, onde o usuário que "aumentar", dar um zoom, em um elemento com dois dedos.
  • OnLongPressGesture: gesto de "clicar" por um tempo longo em um elemento na tela.
  • RotateGesture: semelhante ao "MagnifyGesture", porém com o intuito de girar o elemento. O usuário usa dois dedos na tela e rotaciona o elemento.

Exemplo de uso do DragGesture

Para demonstrar o uso do DragGesture no desenvolvimento de um aplicativo iOS, vou criar uma View bem simples com um retangulo com bordas arredondas.
import SwiftUI 

struct ContentView: View { 
    var body: some View { 
        Spacer() 
        RoundedRectangle(cornerRadius: 10) 
            .foregroundStyle(.blue) 
            .frame(width: 300, height: 200) 
    }
}
Nada de especial foi feito. Apenas o deslocamento do retângulo para a parte inferior da tela através do uso do Spacer() .
IPhone com um retangulo na tela Uma foto de um iphone com um retangulo azul na parte inferior da tela
Para utilizar o DragGesture, a ideia é que o retângulo fique parcialmente fora da tela, exibindo apenas uma parte de cima. Quando o usuário arrastar o retângulo para cima, ele deverá se revelar completamente.
Para esconder o retângulo fora da tela, vou utilizar a propriedade offset que causa um deslocamento do elemento. No nosso caso será um descolamento no eixo Y (vertical). O valor desse deslocamento será armazenado em uma nova variável dinâmica nomeada deslocamentoVertical.
Note que as coordenadas do eixo Y (vertical) no SwiftUI são organizadas da seguinte forma: o centro é representado pelo valor zero, enquanto os valores negativos correspondem à área acima do centro e os valores positivos se referem à parte abaixo dele.
import SwiftUI 
struct ContentView: View { 
    // desloca 200 unidades para baixo do ponto inicial 
    @State var deslocamentoVertical : CGFloat = 200 

    var body: some View { 
        Spacer() 
        RoundedRectangle(cornerRadius: 10) 
            .foregroundStyle(.blue) 
            .frame(width: 300, height: 200) 
                    .offset(y: deslocamentoVertical)
    } 
} 
IPhone com um retangulo na tela Uma foto de um iphone com um retangulo azul parcialmente exibido na tela
Agora vamos implementar um gesto que permitirá que o usuário arraste esse retângulo para cima. Para isso eu vou usar a propriedade gesture. Dentro dela vou usar o método DragGesture que, por sua vez, tem dois métodos disponíveis: onChanged e onEnded.
.gesture(
    DragGesture()
        .onChanged { value in
            // Executado durante o gesto de arrasto
        }
        .onEnded { value in
            // Executado quando o usuário termina o gesto. 
            // Quando ele tira o dedo da tela.
        }
)
Como descrito no código acima oonChanged é o trecho de código que é executado durante o gesto de arrasto do usuário enquanto ele está com o dedo encostado na tela. Esse método recebe um parâmetro que contém todas as informações de localização e deslocamento do dedo do usuário na tela.
Usaremos esse parâmetro para mover o retângulo para cima, alterando o valor da variável deslocamentoVertical. Especificamente, utilizaremos o atributo y da propriedade location, que representa a localização no eixo Y (eixo vertical) do dedo do usuário na tela.
Para tornar o movimento mais fluído vou encapsular esse trecho de código com o método withAnimation. Conheça mais sobre animações neste artigo: Animações no SwiftUI .
.gesture(
    DragGesture()
        .onChanged { value in
            // Executado durante o gesto de arrasto
            withAnimation{
                deslocamentoVertical = value.location.y
            }
        }
        .onEnded { value in
            // Executado quando o usuário termina o gesto. 
            // Quando ele tira o dedo da tela.
        }
)
Note que o código pode ser interpretado da seguinte maneira:
  • Quando o usuário começa o gesto de arrastar o elemento Retângulo um trecho de código é executado.
  • Neste trecho alteramos o valor da variável deslocamentoVertical (equivalente ao offset do retângulo) para o valor da localização do dedo do usuário na tela.
Apesar de serem duas etapas sequenciais elas acontecem de maneira tão rápida que a impressão que o usuário tem é que ele está arrastando o retângulo na tela:
IPhone com um retangulo na tela
Para finalizar a nossa tela, ao término do movimento de arrasto, vou posicionar o retângulo em duas opções: retornar ao ponto inicial, exibindo apenas a ponta, ou ficar completamente visível na parte inferior da tela.
Como é uma decisão a ser tomada no fim da execução do gesto de arrasto do dedo, vou implementar esse trecho dentro do método onEnded. Se o deslocamentoVertical for menor que 30 unidades significa que o usuário arrastou o retângulo até um ponto que ele ficou totalmente visível. Neste caso, vou atribuir o valor zero a variável deslocamentoVertical fazendo com que o retângulo fique totalmente exposto na tela.
Caso contrário, significa que o usuário não arrastou o retângulo o suficiente e ele deve voltar a posição inicial onde ele fica quase completamente escondido. Isto é atribuir o valor descolamentoVertical 200 unidades.
O código final ficará da seguinte maneira:
import SwiftUI 
struct ContentView: View { 
    // desloca 200 unidades para baixo do ponto inicial
    @State var deslocamentoVertical : CGFloat = 200  

    var body: some View { 
        Spacer() 
        RoundedRectangle(cornerRadius: 10) 
            .foregroundStyle(.blue) 
            .frame(width: 300, height: 200) 
            .offset(y: descolamentoVertical)
            .gesture(
                DragGesture()
                    .onChanged { value in
                        withAnimation{
                            deslocamentoVertical = value.location.y
                        }
                    }
                    .onEnded { value in
                        withAnimation{
                            if(descolamentoVertical < 30){
                                descolamentoVertical = 0
                            }else{
                                descolamentoVertical = 200
                            }
                        }
                    }
            )
    } 
} 
O resultado final será:
IPhone com um retangulo na tela

Conclusão e outras formas de uso do DragGesture

Através do exemplo codificado foi possível conhecer a mecânica do uso dos gestos de arrasto no SwiftUI. Agora você pode fazer a sua implementação para o seu caso de uso.
Lembre-se que o parâmetro recebido nas funções onChanged e onEnded contém a tanto a posição do eixo Y como do eixo X, permitindo que você realize movimentações para qualquer ponto e direção da tela.
Além disso, você pode ajustar os parâmetros da animação para deixar o movimento mais rápido, mais elástico ou mais lento conforme for a sua necessidade e contexto. Veja o artigo Animações no SwiftUI sobre animações para entender melhore esses parâmetros.
Por fim, é possível modificar qualquer atributo de outros elementos na tela, em vez de apenas mover o objeto. Por exemplo, você pode aumentar o tamanho do retângulo ou alterar sua cor enquanto o usuário arrasta o dedo na tela.

Para ir além e aprender mais

Outros artigos interessantes também podem te ajudar na sua jornada de aprendizado:
Documentação oficial sobre gestos do Swift:
Dicas e tutoriais de como implementar Gestures: