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

2,539 阅读4分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第24天,点击查看活动详情

今日职言:收敛锋芒,学会柔软。

在本章中,你将学会如何构建一个Banner轮播图。

在很多App首页中,我们可以看到在主要视图的顶部位置,常常有一个Banner轮播图的存在,它通过左右滑动图片的形式,展示给用户查看推荐的信息。

在本章中,我们就来实现下如何使用SwiftUI构建一个Banner轮播图。

6.png

那么,让我们开始吧。

首先,创建一个新项目,命名为SwiftUIBanner

1.png

首先,我们先创建基本的卡片视图CardView。我们可以看到它由一张Image图片卡片和Text文字组成,我们先在Assets.xcassets中导入一批图片作为素材使用。

2.png

然后,我们完成下展示一张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)
                    )
            }
        }
    }
}

3.png

这里科普一个知识点。

我们发现代码和以前学习的不同,我们新增了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

4.png

还记得之前的章节么?我们使用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)
        }
    }
}

5.png

这里科普一个知识点。

和之前引用数据数据不同,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几何容器中使用ScrollViewHStack创建了一个可以左右滑动的视图,然后通过ForEach循环遍历imageModels数组数据,创建了一个个CardView卡片视图。为了更好地根据屏幕大小控制CardView卡片视图大小,我们使用GeometryReader容器的outerView外部视图获取设备屏幕的大小,和innerView内部视图环绕卡片视图来控制其大小。

我们在模拟器中预览下效果。

6.png

这里我们发现一个问题,虽然我们实现了Banner图片轮播的左右滑动,但是滚动视图可以在任何位置停下,这个Banner图片轮播的设计初衷。

那么我们怎样才能让每一个CardView卡片视图能够左右滑动,而且能每次到达一个CardView卡片视图的分页边界时刚好停下来呢?

我们会在后面的章节一一实现。

快来动手试试吧!

如果本专栏对你有帮助,不妨点赞、评论、关注~