跳到主要内容

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

鱼雪

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

上一篇 SwiftUI 布局 介绍了 SwiftUI 中使用 VStackHStackZStack 进行布局。

今天我们介绍 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()aspectRatiofontforegroundColorlineLimitcornerRadiusoverlaypadding 增强了 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 实现了一个可滚动的轮播图界面。 通过以下步骤,您不仅学会了如何使用 VStackHStack 进行布局,还掌握了如何通过垂直和水平滚动来优化用户体验:

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

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