跳到主要内容

掌握 SwiftUI 中的 ScrollView:构建轮播图界面

鱼雪

SwiftUI 革新了开发者构建 iOS、macOS、watchOS 和 tvOS 用户界面的方式。

上一篇 SwiftUI 布局 介绍了 SwiftUI 中使用 VStack 和 HStack及ZStack 进行布局。

今天我们介绍 SwiftUI 提供的强大组件之一是 ScrollView,它让创建可滚动内容变得轻而易举。 在本全面教程中,我们将深入了解 ScrollView,并指导您构建一个令人惊叹的轮播图界面。 通过本指南,您将拥有一个灵活且动态的 UI 组件,提升您 SwiftUI 应用的用户体验。

目录​

  1. SwiftUI 入门
  2. 创建卡片式 UI
  3. 准备图像资源
  4. 实现 CardView
  5. 初步布局:使用 VStack
  6. 引入 ScrollView 进行垂直滚动
  7. 构建水平轮播图界面
  8. 优化 ScrollView 外观
  9. 分组视图内容以简化代码
  10. 自动调整文本大小
  11. 完善轮播图界面
  12. 总结

SwiftUI 入门​

在深入了解 ScrollView 之前,确保您对 SwiftUI 的基础知识有一个扎实的理解。 如果您是 SwiftUI 新手或需要复习基础概念, 建议查看 SwiftUI 基础 以掌握基础知识。

在本教程中,我们将使用 ContentView.swift 并创建一个单独的 CardView.swift 来保持代码的清晰和组织性。

创建卡片式 UI​

首先,我们创建一个卡片视图,作为轮播图界面的构建模块。卡片视图通常包含图像和描述性文本。

  1. 打开 Xcode,使用 App 模板在 iOS 下创建一个新项目。
  2. 命名您的项目为 SwiftUIScrollView(或任何您喜欢的名称),并确保选择 SwiftUI 作为界面选项。
  3. 组织代码:不将所有内容都编码在 ContentView.swift 中,而是为卡片创建一个单独的 SwiftUI 视图。

创建 CardView.swift​

  1. 在 项目导航器中,右键点击并选择 新建文件。
  2. 选择 SwiftUI View 并命名为 CardView。
  3. 将文件保存到您的项目目录中。

首先,CardView.swift 初始内容如下:

import SwiftUI

struct CardView: View {
var body: some View {
Text("Card View")
}
}

struct CardView_Previews: PreviewProvider {
static var previews: some View {
CardView()
}
}

准备图像资源​

在实现卡片视图之前,我们需要准备和导入必要的图像。

  1. 下载示例图像:如果您没有自己的图像,可以从 SwiftUIScrollViewImages.zip 下载示例图像。
  2. 导入图像:
    • 解压下载的压缩包。
    • 打开项目中的 Assets.xcassets。
    • 将所有图像拖放到资产目录中。

实现 CardView​

现在,让我们构建包含图像和文本描述的 CardView。

步骤 1:添加图像​

将占位文本替换为图像视图:

struct CardView: View {
var body: some View {
Image("swiftui-button")
.resizable()
.aspectRatio(contentMode: .fit)
}
}

步骤 2:添加文本描述​

将文本视图封装在 VStack 中,以显示类别、标题和作者:

struct CardView: View {
var body: some View {
VStack {
Image("swiftui-button")
.resizable()
.aspectRatio(contentMode: .fit)
HStack {
VStack(alignment: .leading) {
Text("SwiftUI")
.font(.headline)
.foregroundColor(.secondary)
Text("Drawing a Border with Rounded Corners")
.font(.title)
.fontWeight(.black)
.foregroundColor(.primary)
.lineLimit(3)
Text("Written by Simon Ng".uppercased())
.font(.caption)
.foregroundColor(.secondary)
}
Spacer()
}
.padding()
}
.cornerRadius(10)
.overlay(
RoundedRectangle(cornerRadius: 10)
.stroke(Color(.sRGB, red: 150/255, green: 150/255, blue: 150/255, opacity: 0.1), lineWidth: 1)
)
.padding([.top, .horizontal])
}
}

解释​

  • VStack:垂直排列图像和文本。
  • HStack 与 Spacer:将文本对齐到左侧。
  • 修饰符:resizable()、aspectRatio、font、foregroundColor、lineLimit、cornerRadius、overlay 和 padding 增强了 UI 的外观和响应性。

初步布局:使用 VStack​

现在,我们在 ContentView.swift 中使用 VStack 来排列多个 CardView。 初步尝试会发现所有卡片堆叠在一起,显得比较拥挤。

示例代码​

struct ContentView: View {
var body: some View {
VStack {
CardView(
image: "swiftui-button",
category: "SwiftUI",
heading: "Drawing a Border with Rounded Corners",
author: "Ng"
)
CardView(
image: "macos-programming",
category: "macOS",
heading: "Building a Simple Editing App",
author: "Theodoropoulos"
)
CardView(
image: "flutter-app",
category: "Flutter",
heading: "Building a Complex Layout with Flutter",
author: "Tan"
)
CardView(
image: "natural-language-api",
category: "iOS",
heading: "What's New in Natural Language API",
author: "Kambampati"
)
}
}
}

结果​

使用 VStack 布局多个卡片时,所有卡片会垂直堆叠在一起。如果卡片数量较多,所有内容会挤在一起,且卡片之间可能显得过于拥挤。

引入 ScrollView 进行垂直滚动​

为了让用户能够滚动查看所有卡片,我们引入 ScrollView 并将 VStack 包裹在其中。 这样,用户可以在垂直方向上滚动浏览卡片。

修改 ContentView.swift​

struct ContentView: View {
var body: some View {
ScrollView {
VStack {
CardView(
image: "swiftui-button",
category: "SwiftUI",
heading: "Drawing a Border with Rounded Corners",
author: "Ng"
)
CardView(
image: "macos-programming",
category: "macOS",
heading: "Building a Simple Editing App",
author: "Theodoropoulos"
)
CardView(
image: "flutter-app",
category: "Flutter",
heading: "Building a Complex Layout with Flutter",
author: "Tan"
)
CardView(
image: "natural-language-api",
category: "iOS",
heading: "What's New in Natural Language API",
author: "Kambampati"
)
}
}
}
}

结果​

将 VStack 包裹在 ScrollView 中后,卡片变得可滚动,用户可以通过上下滑动来查看所有内容。 然而,这种垂直滚动方式在某些场景下可能不够直观,特别是当需要展示多张图片时。

除了垂直滚动,我们还可以将 ScrollView 设置为水平滚动,以创建类似轮播图的界面。 这种布局方式特别适合展示多个卡片,用户可以通过左右滑动来浏览。

修改 ContentView.swift 为水平滚动​

struct ContentView: View {
var body: some View {
ScrollView(.horizontal) {
HStack {
CardView(
image: "swiftui-button",
category: "SwiftUI",
heading: "Drawing a Border with Rounded Corners",
author: "Ng"
)
.frame(width: 300)

CardView(
image: "macos-programming",
category: "macOS",
heading: "Building a Simple Editing App",
author: "Theodoropoulos"
)
.frame(width: 300)

CardView(
image: "flutter-app",
category: "Flutter",
heading: "Building a Complex Layout with Flutter",
author: "Tan"
)
.frame(width: 300)

CardView(
image: "natural-language-api",
category: "iOS",
heading: "What's New in Natural Language API",
author: "Kambampati"
)
.frame(width: 300)
}
}
}
}

关键更改​

  1. 水平 ScrollView:通过传递 .horizontal 指定 ScrollView 为水平滚动。
  2. HStack:将堆栈视图从 VStack 更改为 HStack,以并排排列卡片。
  3. 固定宽度:为每个 CardView 设置宽度为 300 点,以保持一致性并防止卡片过于拥挤。

结果​

您的轮播图现在应水平排列卡片,允许用户通过左右滑动来浏览。这种布局更加直观,特别适合展示多张图片或信息卡片。

优化 ScrollView 外观​

默认情况下,ScrollView 显示一个滚动指示器,这可能不符合您的设计偏好。您可以隐藏它以获得更简洁的外观。

隐藏滚动指示器​

ScrollView(.horizontal, showsIndicators: false) {
// HStack 包含 CardViews
}

通过将 showsIndicators 设置为 false,滚动指示器将被隐藏,使界面更加简洁。

分组视图内容以简化代码​

为了避免对每个 CardView 重复使用 .frame(width: 300) 修饰符,可以使用 Group 视图来集体应用修饰符。

重构带有 Group 的 HStack​

HStack {
Group {
CardView(
image: "swiftui-button",
category: "SwiftUI",
heading: "Drawing a Border with Rounded Corners",
author: "Ng"
)
CardView(
image: "macos-programming",
category: "macOS",
heading: "Building a Simple Editing App",
author: "Theodoropoulos"
)
CardView(
image: "flutter-app",
category: "Flutter",
heading: "Building a Complex Layout with Flutter",
author: "Tan"
)
CardView(
image: "natural-language-api",
category: "iOS",
heading: "What's New in Natural Language API",
author: "Kambampati"
)
}
.frame(width: 300)
}

优点​

  • 简洁代码:通过分组相似视图,减少重复代码。
  • 易于维护:对分组视图应用修饰符确保所有分组视图的一致性。

自动调整文本大小​

有时,文本内容可能过长,导致截断。SwiftUI 提供了修饰符来优雅地处理这种情况。

实现 .minimumScaleFactor​

在 CardView.swift 中,调整标题文本:

Text(heading)
.font(.title)
.fontWeight(.black)
.foregroundColor(.primary)
.lineLimit(3)
.minimumScaleFactor(0.5)

解释​

  • .minimumScaleFactor(0.5):允许文本缩小至其原始大小的 50%,以适应可用空间,防止截断。

整合所有组件,完善您的轮播图界面。

完整的 ContentView.swift​

import SwiftUI

struct ContentView: View {
@State private var currentDate = Date()
private var formattedDate: String {
let formatter = DateFormatter()
formatter.dateFormat = "EEEE, MMM d"
return formatter.string(from: currentDate)
}

var body: some View {
VStack {
// 头部区域
HStack(spacing: 18) {
VStack(alignment: .leading) {
Text("\(formattedDate)")
.font(.headline)
.foregroundColor(.secondary)
Text("Your Reading")
.font(.largeTitle)
.fontWeight(.black)
}
Spacer()
}
.padding()

// 轮播图 ScrollView
ScrollView(.horizontal, showsIndicators: false) {
HStack {
Group {
CardView(
image: "swiftui-button",
category: "SwiftUI",
heading: "Drawing a Border with Rounded Corners",
author: "Ng"
)
CardView(
image: "macos-programming",
category: "macOS",
heading: "Building a Simple Editing App",
author: "Hal"
)
CardView(
image: "flutter-app",
category: "Flutter",
heading: "Building a Complex Layout in Flutter",
author: "Hal"
)
CardView(
image: "natural-language-api",
category: "iOS",
heading: "Adding Speech Recognition to Your App",
author: "Hal"
)
}
.frame(width: 300)
}
}

Spacer()
}
}
}

struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}

完整的 CardView.swift​

import SwiftUI

struct CardView: View {
var image: String
var category: String
var heading: String
var author: String

var body: some View {
VStack {
Image(image)
.resizable()
.aspectRatio(contentMode: .fit)
HStack {
VStack(alignment: .leading) {
Text(category)
.font(.headline)
.foregroundColor(.secondary)
Text(heading)
.font(.title)
.fontWeight(.black)
.foregroundColor(.primary)
.lineLimit(3)
.minimumScaleFactor(0.5)
Text("Written by: \(author)".uppercased())
.font(.caption)
.foregroundColor(.secondary)
}
Spacer()
}
.padding()
}
.cornerRadius(10)
.overlay(
RoundedRectangle(cornerRadius: 10)
.stroke(Color(.sRGB, red:150/255, green: 150/255, blue: 150/255, opacity: 0.1), lineWidth: 1)
)
.padding([.top, .horizontal])
}
}

struct CardView_Previews: PreviewProvider {
static var previews: some View {
CardView(
image: "swiftui-button",
category: "SwiftUI",
heading: "Drawing a Border with Rounded Corners",
author: "Ng"
)
}
}

总结​

恭喜您!您已经成功在 SwiftUI 中使用 ScrollView 实现了一个可滚动的轮播图界面。 通过以下步骤,您不仅学会了如何使用 VStack 和 HStack 进行布局,还掌握了如何通过垂直和水平滚动来优化用户体验:

  1. 初步布局:使用 VStack 排列多个卡片,发现卡片过于拥挤。
  2. 引入 ScrollView:将 VStack 包裹在 ScrollView 中,实现垂直滚动。
  3. 构建水平滚动:将 ScrollView 设置为水平滚动,并使用 HStack 排列卡片,创建轮播图效果。
  4. 优化外观:隐藏滚动指示器,使用 Group 简化代码,自动调整文本大小,确保界面整洁美观。

通过创建一个灵活的 CardView 并优化布局,您构建了一个动态组件,提升了应用的互动性。 记住,持续练习和尝试不同的 SwiftUI 组件,将进一步提升您的技能。