一起养成写作习气!这是我参加「日新方案 4 月更文应战」的第25天,点击查看活动详情。
今天职言:成见真是太可怕了,它会毁了一个人对这个国际的夸姣神往。
承接上一章的内容,咱们继续完结下怎样运用SwiftUI
构建一个Banner
轮播图。
问题点
上一章咱们运用了ScrollView
翻滚视图的方法创立了一个相似Banner
轮播图的样式,但咱们假如了解过ScrollView
翻滚视图,咱们会发现ScrollView
翻滚视图没有分页界限,不知道在哪个方位能够停下,别的,ScrollView
翻滚视图是一整个视图,这样咱们也没有方法完结点击单个CardView
卡片视图进入它的详情。
因此,运用ScrollView
翻滚视图创立Banner
轮播图的方法是不对的,至少目前不太可行
。
那咱们有没有方法自己做
一个翻滚视图呢?
咱们在之前的章节中学过SwipeCard
卡片滑动作用的运用,咱们用ZStack
层叠和Gestures
手势做了一个能够左右滑动
丢掉CardView
卡片视图的事例,那个真的花了好长时间写。
咱们拓宽
下思想,Banner
轮播图是左右横向滑动的,咱们是不是能够运用HStack
横向视图和Gestures
手势做一个Banner
轮播图呢?
说干就干。
CardView卡片视图构建
首要,咱们拿掉ScrollView
翻滚视图,这样就得到了一个只有一个CardView
卡片视图。
struct ContentView: View {
var body: some View {
GeometryReader { outerView in
HStack {
ForEach(imageModels.indices, id: \.self) { index in
GeometryReader { innerView in
CardView(image: imageModels[index].image, imageName: imageModels[index].imageName)
}
.frame(width: outerView.size.width, height: outerView.size.height)
}
}
.frame(width: outerView.size.width, height: outerView.size.height)
}
}
}
嗯?为什么图片会变成展现“图片05
”了?
咱们能够停止预览,点击模拟器中的CardView
卡片视图,看看卡片的排布
方法。
哦~,在imageModels
图片数组中有9
个条目图片,而每个卡片视图的宽度
等于屏幕宽度
,水平仓库视图向屏幕外扩展,中心展现
的便是图片05
。
假如咱们要展现第一个图片“图片01
”的话,也很简单,只需要将整个HStack
横向视图的对齐方法变成.leading
左面就行了,体系都是默认.center
居中的。
.frame(width: outerView.size.width, height: outerView.size.height, alignment: .leading)
调整完结后,咱们再看下CardView
卡片视图的排布方法。
如同能够了,但又如同不可,咱们发现CardView
卡片视图之间有缝隙,这样可能导致咱们完结左右滑动的时分,欠好核算方位
,这里调整HStack
横向视图的距离spacing
为0
,趁便增加下CardView
卡片视图和屏幕的边距。
struct ContentView: View {
var body: some View {
GeometryReader { outerView in
HStack (spacing:0) {
ForEach(imageModels.indices, id: \.self) { index in
GeometryReader { innerView in
CardView(image: imageModels[index].image, imageName: imageModels[index].imageName)
}
.padding(.horizontal)
.frame(width: outerView.size.width, height: outerView.size.height)
}
}
.frame(width: outerView.size.width, height: outerView.size.height, alignment: .leading)
}
}
}
CardView卡片视图移动
上面咱们构建了单张CardView
卡片视图,咱们怎样能让它左右移动
呢?
很简单,咱们用GeometryReader
几何视图的特性得到了屏幕的宽度
,那么一张卡片的宽度
咱们也知道了,咱们移动卡片的时分,移动到第一个卡片的方位
就行了,比如:第一张卡片的起始方位
为0
,因为卡片宽度为375
,那么第二张卡片起始方位
便是375
,原理便是这样。
首要,咱们先界说一个卡片的索引
方位,然后咱们让CardView
卡片视图依据咱们界说的方位进行偏移
看看静态作用。
struct ContentView: View {
@State var currentIndex = 5
var body: some View {
GeometryReader { outerView in
HStack (spacing:0) {
ForEach(imageModels.indices, id: \.self) { index in
GeometryReader { innerView in
CardView(image: imageModels[index].image, imageName: imageModels[index].imageName)
}
.padding(.horizontal)
.frame(width: outerView.size.width, height: outerView.size.height)
}
}
.frame(width: outerView.size.width, height: outerView.size.height, alignment: .leading)
.offset(x: -CGFloat(self.currentIndex) * outerView.size.width)
}
}
}
咱们界说了当前方位currentIndex
为5
,依照核算,它会基于第一张图片再向左移动5
个方位,那么体系就会展现第6
张图片。
接下来,咱们来增加DragGesture拖动手势
。
首要,咱们声明一个变量dragOffset
来保存拖动偏移量:
@GestureState var dragOffset: CGFloat = 0
然后,咱们完结下DragGesture
拖动的代码。
// 拖动事情
.gesture(
DragGesture()
.updating(self.$dragOffset, body: { value, state, transactionin
state = value.translation.width
})
.onEnded({ value in
let threshold = outerView.size.width * 0.65
var newIndex = Int(-value.translation.width / threshold) + self.currentIndex
newIndex = min(max(newIndex, 0), imageModels.count - 1)
self.currentIndex = newIndex
})
)
上面的代码中,咱们在拖动卡片视图时将调用updating
拖动更新函数,将水平拖动距离保存到dragOffset
变量里。
当拖动结束onEnded
时,咱们查看拖动距离是否超越阈值(屏幕宽度的65%
),并核算新的索引newIndex
。
核算出newIndex
之后,咱们验证它是否在imageModels
图片数组,假如在,咱们就将新的偏移量newIndex
的值赋给当前偏移量currentIndex
,体系就会更新UI
显现下一张图片啦。
别的,咱们还需要在拖动HStack
横向视图中调用偏移量。
.offset(x: self.dragOffset)
这样,咱们拖动的时分,就能够看到CardView
左右拖动的作用啦~
至此,咱们现已完结了怎样运用HStack
横向视图和Gestures
手势做一个Banner
轮播图的逻辑啦~
未完待续
但咱们只完结
了基本的交互逻辑,下一章
,咱们将学习Banner
轮播图的交互,包含移动Banner
轮播图的动画
,以及点击Banner
轮播图进入DatailView
详情页。
本章ContentView
完好代码如下:
import SwiftUI
struct ContentView: View {
@State var currentIndex = 5
@GestureState var dragOffset: CGFloat = 0
var body: some View {
GeometryReader { outerView in
HStack(spacing: 0) {
ForEach(imageModels.indices, id: \.self) { index in
GeometryReader { innerView in
CardView(image: imageModels[index].image, imageName: imageModels[index].imageName)
}
.padding(.horizontal)
.frame(width: outerView.size.width, height: outerView.size.height)
}
}
.frame(width: outerView.size.width, height: outerView.size.height, alignment: .leading)
.offset(x: -CGFloat(self.currentIndex) * outerView.size.width)
.offset(x: self.dragOffset)
// 拖动事情
.gesture(
DragGesture()
.updating(self.$dragOffset, body: { value, state, transaction in
state = value.translation.width
})
.onEnded({ value in
let threshold = outerView.size.width * 0.65
var newIndex = Int(-value.translation.width / threshold) + self.currentIndex
newIndex = min(max(newIndex, 0), imageModels.count - 1)
self.currentIndex = newIndex
})
)
}
}
}
快来动手试试吧!
假如本专栏对你有协助,不妨点赞、谈论、关注~