在本章中,你将学会如何构建一个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卡片视图的分页边界时刚好停下来呢?
我们会在后面的章节一一实现。
快来动手试试吧!






