一起养成写作习气!这是我参加「日新方案 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)
}
}
}

SwiftUI极简教程25:构建一个Banner图片轮播(中)

嗯?为什么图片会变成展现“图片05”了?

咱们能够停止预览,点击模拟器中的CardView卡片视图,看看卡片的排布方法。

SwiftUI极简教程25:构建一个Banner图片轮播(中)

哦~,在imageModels图片数组中有9个条目图片,而每个卡片视图的宽度等于屏幕宽度,水平仓库视图向屏幕外扩展,中心展现的便是图片05

假如咱们要展现第一个图片“图片01”的话,也很简单,只需要将整个HStack横向视图的对齐方法变成.leading左面就行了,体系都是默认.center居中的。

.frame(width: outerView.size.width, height: outerView.size.height, alignment: .leading)

SwiftUI极简教程25:构建一个Banner图片轮播(中)

调整完结后,咱们再看下CardView卡片视图的排布方法。

SwiftUI极简教程25:构建一个Banner图片轮播(中)

如同能够了,但又如同不可,咱们发现CardView卡片视图之间有缝隙,这样可能导致咱们完结左右滑动的时分,欠好核算方位,这里调整HStack横向视图的距离spacing0,趁便增加下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)
}
}
}

SwiftUI极简教程25:构建一个Banner图片轮播(中)

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)
}
}
}

SwiftUI极简教程25:构建一个Banner图片轮播(中)

咱们界说了当前方位currentIndex5,依照核算,它会基于第一张图片再向左移动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
})
)

SwiftUI极简教程25:构建一个Banner图片轮播(中)

上面的代码中,咱们在拖动卡片视图时将调用updating拖动更新函数,将水平拖动距离保存到dragOffset变量里。

当拖动结束onEnded时,咱们查看拖动距离是否超越阈值(屏幕宽度的65%),并核算新的索引newIndex

核算出newIndex之后,咱们验证它是否在imageModels图片数组,假如在,咱们就将新的偏移量newIndex的值赋给当前偏移量currentIndex,体系就会更新UI显现下一张图片啦。

别的,咱们还需要在拖动HStack横向视图中调用偏移量。

.offset(x: self.dragOffset)

这样,咱们拖动的时分,就能够看到CardView左右拖动的作用啦~

SwiftUI极简教程25:构建一个Banner图片轮播(中)

至此,咱们现已完结了怎样运用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
})
)
}
}
}

快来动手试试吧!

假如本专栏对你有协助,不妨点赞、谈论、关注~