一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第24天,点击查看活动详情。
今日职言:收敛锋芒,学会柔软。
在本章中,你将学会如何构建一个Banner轮播图。
在很多App首页中,我们可以看到在主要视图的顶部位置,常常有一个Banner轮播图的存在,它通过左右滑动图片的形式,展示给用户查看推荐的信息。
在本章中,我们就来实现下如何使用SwiftUI构建一个Banner轮播图。
那么,让我们开始吧。
首先,创建一个新项目,命名为SwiftUIBanner。
首先,我们先创建基本的卡片视图CardView。我们可以看到它由一张Image图片卡片和Text文字组成,我们先在Assets.xcassets中导入一批图片作为素材使用。
然后,我们完成下展示一张Image图片卡片,并在ContentView主视图中预览它。
struct CardView: View {
var body: some View {
ZStack {
GeometryReader { geometry in
Image("image01")
.resizable()
.scaledToFill()
.frame(width: geometry.size.width, height: geometry.size.height)
.cornerRadius(15)
.overlay(
Text("图片01")
.font(.system(.headline, design: .rounded))
.fontWeight(.heavy)
.padding(10)
.background(Color.white)
.padding([.bottom, .leading])
.opacity(1.0)
.frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity, alignment: .bottomLeading)
)
}
}
}
}
这里科普一个知识点。
我们发现代码和以前学习的不同,我们新增了GeometryReader几何容器,将我们的Image图片和Text文字包裹在里面。而且图片的大小.frame修饰符中,设置的Image图片尺寸是我们GeometryReader几何容器宽高。
GeometryReader几何容器简单来说,就是一个View,但不同的是,它的宽高是通过自动判断你的设备的屏幕尺寸的定的。这样,假设我们有一张4000*4000分辨率的图片的时候,我们就不用再设置它在屏幕中展示的固定大小了,屏幕多少,里面的图片就可以自动设置多大。
//GeometryReader使用方法
GeometryReader { geometry in
//代码块
}
我们将CardView卡片视图需要展示的内容的变量抽离出来,它是Image图片和Text文字内的内容,用于之后遍历数组模型使用。
let image: String
let imageName: String
下一步,我们定义好我们的Model部分的内容,我们新建一个Swift文件,命名为ImageModel.swift。
和之前的章节内容一样,我们定义好包含可被识别的参数的imageModel,当然还有演示的数据数组imageModels。
import Foundation
struct imageModel: Identifiable {
var id = UUID()
var image: String
var imageName: String
}
#if DEBUG
let imageModels = [
imageModel(image: "image01", imageName: "图片01"),
imageModel(image: "image02", imageName: "图片02"),
imageModel(image: "image03", imageName: "图片03"),
imageModel(image: "image04", imageName: "图片04"),
imageModel(image: "image05", imageName: "图片05"),
imageModel(image: "image06", imageName: "图片06"),
imageModel(image: "image07", imageName: "图片07"),
imageModel(image: "image08", imageName: "图片08"),
imageModel(image: "image09", imageName: "图片09")
]
#endif
还记得之前的章节么?我们使用ForEach循环遍历我们定义好的数组内容。
我们这里也尝试下在ContentView主页中显示所有CardView卡片视图的内容。
struct ContentView: View {
var body: some View {
GeometryReader { outerView in
ScrollView(.horizontal, showsIndicators: false) {
HStack(alignment: .center) {
ForEach(imageModels.indices,id:\.self) { index in
GeometryReader { innerView in
CardView(image: imageModels[index].image, imageName:imageModels[index].imageName)
}
}
.padding(.horizontal, 20)
.frame(width: outerView.size.width, height: outerView.size.height)
}
}
.frame(width: outerView.size.width, height: outerView.size.height, alignment: .leading)
}
}
}
这里科普一个知识点。
和之前引用数据数据不同,Xcode 13.3 RC 版本更新了一个内容,我们使用ForEach循环时,由于View是动态生成的,所以id需要指定为id:\.self。
ForEach(imageModels.indices,id:\.self) { index in
//代码块
}
不然Xcode会报错:
Non-constant range: argument must be an integer literal
这样改动后不仅不会有警告,而且动态创建的View将会通过id来作为唯一标识,需要动态增加和变化的话,不会重复进行创建。
好,我们看回代码本身的内容。
我们在GeometryReader几何容器中使用ScrollView和HStack创建了一个可以左右滑动的视图,然后通过ForEach循环遍历imageModels数组数据,创建了一个个CardView卡片视图。为了更好地根据屏幕大小控制CardView卡片视图大小,我们使用GeometryReader容器的outerView外部视图获取设备屏幕的大小,和innerView内部视图环绕卡片视图来控制其大小。
我们在模拟器中预览下效果。
这里我们发现一个问题,虽然我们实现了Banner图片轮播的左右滑动,但是滚动视图可以在任何位置停下,这个不是Banner图片轮播的设计初衷。
那么我们怎样才能让每一个CardView卡片视图能够左右滑动,而且能每次到达一个CardView卡片视图的分页边界时刚好停下来呢?
我们会在后面的章节一一实现。
快来动手试试吧!
如果本专栏对你有帮助,不妨点赞、评论、关注~