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.
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:
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:
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:
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:
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!