Como implementar cartões Swipe Tinder no SwiftUI

Em junho, ouvimos falar do SwiftUI pela primeira vez - uma maneira totalmente nova de criar e trabalhar com elementos de interface do usuário em aplicativos iOS e macOS (também iPadOS). Parecia Natal no verão. É novo, é declarativo, é sexy! E agora, apenas algumas semanas após o lançamento do iOS 13, podemos começar a usar o SwiftUI em todos os nossos projetos. Vamos aprender a usar essa incrível ferramenta que a Apple nos deu, para criar os clássicos cartões Swipe-esque do Tinder.

Neste artigo, gostaria de mostrar a você como obter uma visualização e um comportamento do cartão do tipo Tinder (deslize para a ação), com apenas algumas linhas de código.

Para conseguir isso, precisamos fazer o seguinte, em ordem:

  • Criar UserView
  • Criar visão de navegação
  • Criar BottomBarView
  • Criar visualização de swip
  • Coloque tudo isso junto no ContentView

Então, vamos começar.

tinder swiftui

Userview


O UserView é construído a partir de duas sub - visualizações , uma é a NameView, que contém o nome do usuário, a idade e os hobbies, e a segunda é apenas uma visualização de avatar que exibe a foto do perfil do usuário.

struct NameView: View { let name: String let age: Int let hobby: String var body: some View { VStack(alignment: .leading) { Spacer() Text("\(name), \(age)") .font(.title) .fontWeight(.semibold) .foregroundColor(.white) Text(hobby) .font(.system(size: 16)) .fontWeight(.regular) .foregroundColor(.white) } .padding() } } 

Primeiro, precisamos definir o NameView , que representará o nome do usuário, a idade e o hobby. O NameView está em conformidade com o protocolo View , usado para definir visualizações personalizadas no SwiftUI. O protocolo de exibição tem apenas um requisito e é a definição da propriedade do corpo que deve retornar a estrutura da exibição e descrever seu comportamento. Você pode verificar mais sobre o protocolo View na documentação oficial da Apple .

Vamos dividir os objetos que usamos para definir esta Visualização :

  • VStack, que atua como um contêiner para todos os objetos, alinhando-os verticalmente
  • Espaçador que informa ao SwiftUI que esta visualização deve estar alinhada na parte inferior
  • Texto que representa o rótulo com nome e idade, com as seguintes propriedades:
  • Segundo objeto de texto que possui propriedades semelhantes e exibe o hobby do usuário

Observe que não estamos usando uma declaração de retorno aqui, dentro da propriedade body, mas estamos retornando um VStack. O SwiftUI está usando a proposta de omit-return implementada no Swift 5.0. Você pode conferir mais sobre isso aqui .

Avatarview


É assim que o AvatarView é definido:

 struct AvatarView: View { let image: UIImage var body: some View { Image(uiImage: image) .resizable() .overlay( Rectangle() .fill(LinearGradient(gradient: Gradient(colors: [.clear, .black]), startPoint: .center, endPoint: .bottom)) .clipped() ) .cornerRadius(12.0) } } 

Vamos mergulhar nos componentes que estão criando esta unidade de avatar:

  • Imagem - que exibe a imagem do usuário
  • redimensionável - esse método especifica que a imagem deve ser redimensionada para caber no local em que está incorporada
  • overlay (Rectangle) - aqui estamos definindo o gradiente que será um bom plano de fundo para o NameView; esse gradiente começa no centro da imagem e termina na parte inferior; possui cores claras no início e preto na parte inferior
  • cornerRadius - a imagem terá um raio de canto

E agora vamos incorporar essas duas visualizações em uma única exibição de contêiner, denominada UserView .

Userview


 struct UserView: View { let userModel: UserModel var body: some View { ZStack(alignment: .leading) { AvatarView(image: userModel.image) NameView(name: userModel.name, age: userModel.age, hobby: userModel.hobby) } .shadow(radius: 12.0) .cornerRadius(12.0) } } 

Aqui está o que está acontecendo:

  • ZStack - Esta é uma exibição de pilha que alinhará seus filhos no mesmo eixo. Você pode ler mais sobre o ZStack aqui
  • AvatarView - Nossa visualização de avatar que contém a imagem fornecida via UserModel
  • NameView - Nossa visualização de nome exibindo o nome com base no modelo do usuário

Após todas essas etapas, execute o aplicativo. Você verá a seguinte tela:

tinder avatar

Vamos adicionar um pequeno método auxiliar agora. Antes de mostrar como o NavigationView está definido, vamos criar um método auxiliar, que se parece com isso:

 struct ViewFactory { static func button(_ name: String, renderingMode: Image.TemplateRenderingMode = .original) -> some View { Button(action: {}) { Image(name) .renderingMode(renderingMode) } } } 

Aqui, definimos um método de fábrica de botões, que cria um novo botão a partir de uma determinada imagem e um modo de renderização. Não há manipulador de ações, pois isso fica fora do escopo deste artigo.

Navegação


 struct NavigationView: View { var body: some View { HStack { ViewFactory.button("profile_icon") Spacer() ViewFactory.button("fire_icon") .scaleEffect(2) Spacer() ViewFactory.button("chat_icon") } } } 

O SwiftUI criará automaticamente os espaçadores de largura igual e fornecerá a seguinte visualização de navegação:

vista de navegação

Bottombarview


 struct BottomBarView: View { var body: some View { HStack { ViewFactory.button("back_icon", renderingMode: .template) .foregroundColor(.orange) .background( GeometryReader { geometry in Circle() .offset(x: 2.5) .foregroundColor(.white) .shadow(color: .gray, radius: 12) .frame(width: geometry.size.width * 1.5, height: geometry.size.height * 1.5) } ) Spacer() ... } 

No snippet de código acima, definimos o primeiro botão em nossa visualização em barra. Aqui está o que está acontecendo:

  • ViewFactory.button - aqui estamos usando nosso método auxiliar para definir o botão com a imagem com renderingMode .template, que permite colocar uma cor personalizada para esta imagem
  • .foregroundColor - definindo a cor da nossa visualização
  • .background - esse método define a visualização em segundo plano do objeto especificado
  • GeometryReader - uma exibição de contêiner que define seu conteúdo como uma função de seu próprio tamanho e espaço de coordenadas. Estamos usando isso para obter o tamanho atual de um botão e definir o círculo do plano de fundo com o quadro especificado. Saiba mais sobre os leitores de geometria aqui .
  • Círculo - define a forma do plano de fundo
  • .offset - deslocamento do eixo x do círculo
  • .foregroundColor - cor da tonalidade do círculo
  • .shadow - sombra do círculo
  • .frame - define o quadro do círculo usando o tamanho do leitor de geometria (aqui estamos definindo um círculo de fundo, 1,5x maior que o botão atual)

Agora vamos implementar o restante dos botões:

 struct BottomBarView: View { var body: some View { HStack { ViewFactory.button("back_icon", renderingMode: .template) .foregroundColor(.orange) .background( GeometryReader { geometry in Circle() .offset(x: 2.5) .foregroundColor(.white) .shadow(color: .gray, radius: 12) .frame(width: geometry.size.width * 1.5, height: geometry.size.height * 1.5) } ) Spacer() ViewFactory.button("close_icon", renderingMode: .template) .foregroundColor(.red) .background( GeometryReader { geometry in Circle().foregroundColor(.white) .frame(width: geometry.size.width * 2, height: geometry.size.height * 2) .shadow(color: .gray, radius: 12) } ) Spacer() ViewFactory.button("approve_icon", renderingMode: .template) .foregroundColor(.green) .background( GeometryReader { geometry in Circle() .foregroundColor(.white) .shadow(color: .gray, radius: 12) .frame(width: geometry.size.width * 2, height: geometry.size.height * 2) } ) Spacer() ViewFactory.button("boost_icon", renderingMode: .template) .foregroundColor(.purple) .background( GeometryReader { geometry in Circle() .foregroundColor(.white) .shadow(color: .gray, radius: 12) .frame(width: geometry.size.width * 1.5, height: geometry.size.height * 1.5) } ) } .padding([.leading, .trailing]) } } 

E, como resultado, agora temos essa bela vista:

vista de namoro da barra de guias

Swipeview


Esta seção é para SwiftUI mais avançado. É aqui que as coisas ficam interessantes. Gostaríamos de implementar o gesto de furto na exibição de ação. Esse comportamento é um bom caso de uso para um PageViewController, mas esse controlador de exibição será histórico em breve; portanto, é aqui que podemos mostrar o verdadeiro poder do SwiftUI.

Então, vamos ver como o SwipeView é implementado:

 struct SwipeView: View { @State private var offset: CGFloat = 0 @State private var index = 0 let users = [...] let spacing: CGFloat = 10 var body: some View { GeometryReader { geometry in return ScrollView(.horizontal, showsIndicators: true) { HStack(spacing: self.spacing) { ForEach(self.users) { user in UserView(userModel: user) .frame(width: geometry.size.width) } } } .content.offset(x: self.offset) .frame(width: geometry.size.width, alignment: .leading) .gesture( DragGesture() .onChanged({ value in self.offset = value.translation.width - geometry.size.width * CGFloat(self.index) }) .onEnded({ value in if -value.predictedEndTranslation.width > geometry.size.width / 2, self.index < self.users.count - 1 { self.index += 1 } if value.predictedEndTranslation.width > geometry.size.width / 2, self.index > 0 { self.index -= 1 } withAnimation { self.offset = -(geometry.size.width + self.spacing) * CGFloat(self.index) } }) ) } } } 

Aqui usamos alguns novos conceitos interessantes do SwiftUI:

  • @ State - Um valor persistente de um determinado tipo, através do qual uma visão lê e monitora o valor, o que significa que sempre que essa propriedade for alterada, a visão será recarregada para se ajustar à atualização de estado especificada. Você pode conferir mais sobre o Estado aqui .
  • DragGesture - este objeto será usado para reconhecer cada golpe que o usuário faz na tela. Você pode ler mais sobre isso aqui: developer.apple.com/documentation/swiftui/draggesture
  • @ Deslocamento de var privado do estado: CGFloat = 0 - essa propriedade será usada para definir o deslocamento atual da exibição de rolagem quando os usuários passarem o mouse pela tela
  • @ State private var index = 0 - essa propriedade define qual visualização do usuário está atualmente na tela
  • ScrollView - visualização de rolagem horizontal sem indicadores, que será um contêiner para a visualização do usuário
  • HStack - visualização em pilha horizontal que contém todas as visualizações do usuário
  • content.offset (self.offset) - está criando uma conexão entre o estado do deslocamento e o deslocamento do conteúdo da exibição de rolagem. Isso significa que sempre que a propriedade offset for alterada, o deslocamento da exibição de rolagem também será atualizado

Estamos enumerando os usuários existentes criando um UserView para cada elemento:

  • .frame - aqui estamos definindo o quadro de exibição de rolagem que deve caber na largura da tela e deve ser alinhado adequadamente ao seu contêiner
  • .gesture - aqui estamos adicionando nosso objeto DragGesture

DragGesture é um pouco complicado, mas, no entanto, adiciona toda a lógica de paginação em apenas algumas linhas de código. Vamos dividir o DragGesture :
  • onChanged () - esse bloco é chamado sempre que o usuário inicia e está na hora do gesto de arrastar, aqui estamos calculando o deslocamento atual da visualização do usuário que segue o dedo do usuário
  • onEnded () - aqui somos informados quando o gesto de arrastar termina, aqui precisamos calcular se o usuário gostaria de deslizar esta visualização (esquerda ou direita), ou se esse gesto foi marcado, e o usuário gostaria de permanecer nesta tela
  • withAnimation - esse fechamento é chamado com animação e permite alterar o deslocamento de maneira irregular

Visualização de conteúdo


 struct ContentView: View { var body: some View { VStack { NavigationView() .padding(.bottom) SwipeView() .padding(.bottom) BottomBarView() } .padding() } } 

Nossa visualização de conteúdo é extremamente simples neste momento - ela compõe todas as visualizações criadas anteriormente, dentro de uma pilha vertical ( VStack ). Para o NavigationView e SwipeView , adicionamos alguns preenchimentos padrão na parte inferior e todo o VStack possui preenchimentos adicionados a todas as bordas.

É isso. Concluído É assim que nosso aplicativo se parece agora:

tinder swiftui

Considerações finais


Como podemos ver, o SwiftUI é uma ferramenta muito poderosa, oferecendo uma maneira fácil de definir e manipular a interface do usuário em um código declarativo curto. Reagir Os desenvolvedores nativos reconheceriam esse paradigma declarativo imediatamente.

Mas lembre-se: o SwiftUI ainda está em desenvolvimento e pode ser extremamente instável por enquanto. Se você deseja verificar toda a base de código desse projeto, pode encontrá-la no Github .

Se você tiver alguma dúvida ou dúvida sobre o SwiftUI, sinta-se à vontade para compartilhá-los nos comentários. Além disso, se você gostou deste artigo, compartilhe-o com sua comunidade para nos ajudar a espalhar a notícia!

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


All Articles