¡Acostúmbrate a escribir juntos! Este es el día 18 de mi participación en el "Nuevo plan diario de Nuggets · Desafío de actualización de abril", haga clic para ver los detalles del evento .
Palabra del día: si el contenido es importante, repite el contenido importante.
En este capítulo, aprenderá a usar Gestures
gestos y Animations
animaciones para SwipeCard
deslizar tarjetas.
En muchos 交友类的App
de ellos, podemos ver que hay una 向左向右滑动卡片
interacción " ", el usuario puede deslizar hacia la derecha para dar una foto 喜欢点赞
, y deslizar hacia la izquierda puede dar un punto de foto 不喜欢
.
Entonces, en este capítulo, intentemos construir una SwipeCard
aplicación simple similar al efecto de interacción de deslizamiento de cartas.
Muy bien, ¡comencemos!
Primero, crea un nuevo proyecto llamado SwiftUISwipeCard
.
Primero Assets.xcassets
importamos un lote de imágenes para usarlas como materiales. Puede encontrar algunas imágenes de paisajes, imágenes de retratos, imágenes de alimentos, siempre que se trate de una serie de imágenes. No olvide cambiar el nombre de la imagen para que sea más fácil para nosotros encontrar y hacer referencia a la imagen en el código.
Antes de implementar la función de deslizamiento, UI
primero creemos la página principal, dividiremos la página principal en tres partes:
1. TopBarMenu
Barra de navegación superior
CardView
vista de tarjeta
3. BottomBarMenu
Barra de menú inferior
Barra de navegación superior TopBarMenu
Aquí creamos una nueva página de estructura para mostrar TopBarMenu
la vista de la barra de navegación superior, la llamamos TopBarMenu
.
el código se muestra a continuación:
//顶部导航栏
struct TopBarMenu: View {
var body: some View {
HStack {
Image(systemName: "ellipsis.circle")
.font(.system(size: 30))
Spacer()
Image(systemName: "heart.circle")
.font(.system(size: 30))
}.padding()
}
}
复制代码
Aquí no usamos .NavigationView
la barra de navegación superior porque, en muchos casos, nuestra barra de navegación necesita muchas funciones personalizadas y .NavigationView
puede ser difícil admitir nuestro negocio real en la barra de navegación superior, por lo “成熟的”
que a los programadores les gusta escribir su propia barra de navegación superior . estilo de barra.
上面我们做的TopBarMenu
顶部导航栏很简单,就2张图片,使用横向HStack
排布。然后我们在CardView
里引用TopBarMenu
顶部导航栏视图,效果如下:
CardView卡片视图
接着,我们创建一个新的结构体页面来展示卡片视图,命名为CardView
。
代码如下:
//卡片视图
struct CardView: View {
var body: some View {
Image("image01")
.resizable()
.frame(minWidth: 0, maxWidth: .infinity)
.cornerRadius(10)
.padding(.horizontal, 15)
.overlay(
VStack {
Text("图片01")
.font(.system(.headline, design: .rounded)).fontWeight(.bold)
.padding(.horizontal, 30)
.padding(.vertical, 10)
.background(Color.white)
.cornerRadius(5)
}
.padding([.bottom], 20), alignment: .bottom
)
}
}
复制代码
CardView
卡片视图也非常简单,我们放在一个Image
图片,让将一个Text
文字“悬浮”在图片底部。我们在CardView
里引用CardView
卡片视图,由于CardView
卡片视图和TopBarMenu
顶部导航栏是纵向排列,我们使用VStack
包裹住。
struct ContentView: View {
var body: some View {
VStack {
TopBarMenu()
CardView()
}
}
}
复制代码
BottomBarMenu底部菜单栏
底部导航栏也是如此,我们创建一个新的结构体页面叫做BottomBarMenu。
代码如下:
// 底部导航栏
struct BottomBarMenu: View {
var body: some View {
HStack {
Image(systemName: "xmark")
.font(.system(size: 30))
.foregroundColor(.black)
Button(action: {
}) {
Text("立即选择")
.font(.system(.subheadline, design: .rounded)).bold()
.foregroundColor(.white)
.padding(.horizontal, 35)
.padding(.vertical, 15)
.background(Color.black)
.cornerRadius(10)
}.padding(.horizontal, 20)
Image(systemName: "heart")
.font(.system(size: 30))
.foregroundColor(.black)
}
}
}
复制代码
BottomBarMenu
底部导航栏也是我们自己写的,使用3个元素,2个Image
图片,1个Text
文字按钮。然后也在ContentView
主要页面中展示它,效果如下:
我们进一步美化下样式,使用Spacer()
分开CardView
卡片视图和BottomBarMenu
底部导航栏视图,我们保持最小20
的区域,就得到了下面的效果。
struct ContentView: View {
var body: some View {
VStack {
TopBarMenu()
CardView()
Spacer(minLength: 20)
BottomBarMenu()
}
}
}
复制代码
好了,基础的样式我们做完了。
交互逻辑分析
接下来,可以实现SwipeCard
卡片滑动的效果了。先解释一下SwipeCard
卡片滑动的原理,你可以它想象成一组叠在一起的卡片,每张卡片都显示一张照片。
我们将最上面
的那张卡,即第一张
图片,稍微向左或向右
刷一下,就会打开
下面的下一张卡片,也就是第二张图片
。
如果你放开
卡片,卡片会回到原来
的位置。但如果你用力滑动
图片卡片,就可以将图片卡片“丢掉”
,系统就会将把第二张图片
向前拉变成最上面的图片
展示。
我们了解了原理后,我们先实现CardView
卡片部分的内容。这里使用ZStack
将一堆卡片“堆在”一起,而图片卡片的遍历方式之前的章节已经学过。
//创建Album定义变量
struct Album: Identifiable {
var id = UUID()
var name: String
var image: String
}
//创建演示数据
var album = [
Album(name: "图片01", image: "image01"),
Album(name: "图片02", image: "image02"),
Album(name: "图片03", image: "image03"),
Album(name: "图片04", image: "image04"),
Album(name: "图片05", image: "image05"),
Album(name: "图片06", image: "image06"),
Album(name: "图片07", image: "image07"),
Album(name: "图片08", image: "image08"),
Album(name: "图片09", image: "image09")
]
复制代码
由于我们之前定义的CardView
卡片视图中使用的是Image
图片和Text
文字。
这里我们定义两个常量替换它,这样我们就可以在ContentView
主视图定义的值了。
let name: String
let image: String
复制代码
然后,我们在ContentView
主视图使用ZStack
包裹CardView
卡片视图,再使用ForEach
循环遍历album
数组所有数据。
//卡片视图
ZStack {
ForEach(album) { album in
CardView(name: album.name, image: album.image)
}
}
复制代码
我们发现,模拟器突然换了一张图片,这是因为我们定义的album
图片数组,使用ForEach
循环时是一张张遍历的,最后遍历完是album
图片数组最后一张图片。
在ForEach
循环中,第一张图片放在了最底下。因此,最后一张图片也就成了最上面的照片。
因此,虽然我们实现了album
图片数组的遍历,但还是存在两个问题:
1、本该是第一张图片,现在变成了最后一张。
2、现在我们只有9张图片卡片,但如果之后我们有更多的图片卡片的时候,我们是否应该为每张图片创建一个卡片视图?
album数组图片排序问题
我们一个个解决,首先第一个问题,卡片顺序的问题。好在SwiftUI
提供了zIndex
修饰符来来确定ZStack
中视图的顺序,zIndex
值越高,视图层级
也就越高。
Creamos un método para obtener el zIndex
valor de la vista de la tarjeta.
//获得图片zIndex值
func isTopCard(cardView: Album) -> Bool {
guard let index = album.firstIndex(where: { $0.id == cardView.id }) else {
return false
}
return index == 0
}
复制代码
La función del método anterior toma una vista de tarjeta y encuentra su índice, diciéndonos si esta vista de tarjeta es la superior.
A continuación, hacemos CardView
referencia a este método en la vista de tarjeta.
.zIndex(self.isTopCard(cardView: album) ? 1 : 0)
复制代码
Agregamos modificadores a cada vista de tarjeta zIndex
, y para la tarjeta superior le asignamos un 更高
valor zIndex
.
Entonces, obtuvimos la primera imagen como una 顶部卡片
pantalla.
Ver problemas de jerarquía
A continuación, resolvemos 第二个问题
.
Si tenemos una cantidad infinita de tarjetas en el futuro, obviamente no es realista crear una cantidad infinita de vistas. ¿Podemos pensar en otros métodos?
El método también es muy simple, de hecho, piénsalo, solo tenemos que 2个卡片视图
hacerlo , deslizar una vez más y volver a nuestra primera vista, pero la imagen que se muestra es diferente. De esta forma, por muchas fotografías que hagamos, solo nos falta conseguir el efecto que queremos.滑动一个
显示另一个卡片视图
2个卡片视图来回切换
Hazlo.
De acuerdo con el principio, no necesitamos inicializar tantas estructuras de imagen, solo se necesitan las dos primeras. Cuando se descarta la primera vista de tarjeta, agregamos la segunda.
//创建2个卡片视图
var albums: [Album] = {
var views = [Album]()
for index in 0..<2 {
views.append(Album(name: album[index].name, image: album[index].image))
}
return views
}()
复制代码
Dado que hemos definido una nueva matriz albums
, no olvide reemplazar el valor leído por el parámetro con la matriz de juicio zIndex
en el método de obtención del valor de la imagen .index
albums
De esta manera, solo construimos 2 vistas y completamos la visualización transversal de la matriz de imágenes.
El código completo es el siguiente:
import SwiftUI
//创建Album定义变量
struct Album: Identifiable {
var id = UUID()
var name: String
var image: String
}
//创建演示数据
var album = [
Album(name: "图片01", image: "image01"),
Album(name: "图片02", image: "image02"),
Album(name: "图片03", image: "image03"),
Album(name: "图片04", image: "image04"),
Album(name: "图片05", image: "image05"),
Album(name: "图片06", image: "image06"),
Album(name: "图片07", image: "image07"),
Album(name: "图片08", image: "image08"),
Album(name: "图片09", image: "image09")
]
//创建2个卡片视图
var albums: [Album] = {
var views = [Album]()
for index in 0..<2 {
views.append(Album(name: album[index].name, image: album[index].image))
}
return views
}()
struct ContentView: View {
var body: some View {
VStack {
//顶部导航栏
TopBarMenu()
//卡片视图
ZStack {
ForEach(albums) { album in
CardView(name: album.name, image: album.image)
.zIndex(self.isTopCard(cardView: album) ? 1 : 0)
}
}
Spacer(minLength: 20)
//底部导航栏
BottomBarMenu()
}
}
//获得图片zIndex值
func isTopCard(cardView: Album) -> Bool {
guard let index = albums.firstIndex(where: { $0.id == cardView.id }) else {
return false
}
return index == 0
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
// 顶部导航栏
struct TopBarMenu: View {
var body: some View {
HStack {
Image(systemName: "ellipsis.circle")
.font(.system(size: 30))
Spacer()
Image(systemName: "heart.circle")
.font(.system(size: 30))
}.padding()
}
}
//卡片视图
struct CardView: View {
let name: String
let image: String
var body: some View {
Image(image)
.resizable()
.frame(minWidth: 0, maxWidth: .infinity)
.cornerRadius(10)
.padding(.horizontal, 15)
.overlay(
VStack {
Text(name)
.font(.system(.headline, design: .rounded)).fontWeight(.bold)
.padding(.horizontal, 30)
.padding(.vertical, 10)
.background(Color.white)
.cornerRadius(5)
}
.padding([.bottom], 20), alignment: .bottom
)
}
}
// 底部导航栏
struct BottomBarMenu: View {
var body: some View {
HStack {
Image(systemName: "xmark")
.font(.system(size: 30))
.foregroundColor(.black)
Button(action: {
}) {
Text("立即选择")
.font(.system(.subheadline, design: .rounded)).bold()
.foregroundColor(.white)
.padding(.horizontal, 35)
.padding(.vertical, 15)
.background(Color.black)
.cornerRadius(10)
}.padding(.horizontal, 20)
Image(systemName: "heart")
.font(.system(size: 30))
.foregroundColor(.black)
}
}
}
复制代码
Continuará
Dado que el SwipeCard
efecto de deslizamiento de cartas implica demasiado contenido, para ayudar a la digestión, se divide en dos capítulos.
SwipeCard卡片滑动效果的使用(上)
Solo se han completado el estilo básico y algunos trabajos preparatorios en la parte de .
¡Ven y pruébalo!
Si esta columna es útil para usted, haga clic en Me gusta, comente y siga ~