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