精通 SwiftUI - iOS 16 版

第 3 章
图片与标签的处理

现在你应该对于 SwiftUI 有了基本概念,并了解如何显示文字内容,本章我们来看如何显示图片。我们也会学习标签(Label)的用法。

除了文字之外,图片是 iOS App 开发的另外一个基本组件。SwiftUI 提供一个名为 Image 的视图,来让开发者将图片渲染在屏幕上。首前一章的内容相似,我会建立一个简单的 App,以示范如何进行图片的处理。本章可归纳为以下几个主题:

  • 什么是SF Symbol,以及如何显示一个系统图片?
  • 如何显示我们自己的图片?
  • 如何调整图片大小?
  • 如何使用 ignoresSafeArea 来显示一个全屏幕的图片?
  • 如何建立一个圆形图片?
  • 如何在图片上应用重叠?

使用 SwiftUI 来建立新项目

首先,开启Xcode 并使用“App”模板( 在 iOS 分类下),来建立一个新项目。输入项目的名称,并设定为“SwiftUIImage”。关于组织名称,你可以设定为你自己公司或组织名称, 同样的,我使用“com.appcoda”,但你可以使用自己的值。要使用SwiftUI 的话,请确认在“User Interface”选项中选取 “SwiftUI”然后点击“Next”,选取要建立项目的数据夹, 如图 3.1 所示。

图 3.1. 建立一个新项目
图 3.1. 建立一个新项目

项目储存完成之后,Xcode 即会载入 ContentView.swift 档,并显示一个“设计/ 预览”画布,如图 3.2 所示。

图 3.2. 预览所产生的程序
图 3.2. 预览所产生的程序

SF Symbols 介绍

超过 4,000 个可设定的标志,SF Symbols 无缝整合了 Apple 平台的 San Francisco 系统字型,每一个标志皆设定了不同的权重与比例,可以自动对齐文字标签,并支持动态型态(Dynamic type)与粗体字(Bold Text)辅助功能,你也可以输出这些文字,以向量图形编辑工具来进一步编辑,建立具有共享特点与可用性的自订标志。SF Symbols 4 加入 700 多个新符号、增强的颜色自定义、新的检查器以及对自定义符号的改进支持。

在教你如何在屏幕显示图片之前,我们先谈一下关于图片的来源。当然,你可以在 App 中提供自己的图片。从 iOS 13 开始,Apple 导入名为“SF Symbols”的大量系统图片,可以让开发者在任何 App 中使用。iOS 16 进一步修改版释出了 SF Symbol 4 ,新增了 700 个标志,并增强的颜色设置。

这些图片是作为标志用,由于它整合了内建的 San Francisco 字型,因此使用这些标志, 并不需要额外的安装,只要你的 App 是部署在运行 iOS 13(或之后的版本)的装置,你就可以直接取得这些标志。但你要注意,现在有五组不同的符号需要考虑:

  • SF Symbols v1.1 适用于 iOS/iPadOS/tvOS/Mac Catalyst 13.0, watchOS 6.0 和 macOS 11.0
  • SF Symbols v2.0 适用于 iOS/iPadOS/tvOS/Mac Catalyst 14.0, watchOS 7.0 和 macOS 11.0
  • SF Symbols v2.1 适用于 iOS/iPadOS/tvOS/Mac Catalyst 14.2, watchOS 7.1 和 macOS 11.0
  • SF Symbols v2.2 适用于 iOS/iPadOS/tvOS/Mac Catalyst 14.5, watchOS 7.4 和 macOS 11.3
  • SF Symbols v3.0 适用于 iOS/iPadOS/tvOS/Mac Catalyst 15.0, watchOS 8.0 和 macOS 12.0
  • SF Symbols v4.0 适用于 iOS/iPadOS/tvOS/Mac Catalyst 16.0, watchOS 9.0 和 macOS 13.0

当要使用这些标志时,你需要准备的是找到标志名称。由于有超过 4,000 个符号可供使用,Apple 释出一个名为 SF Symbols (https://developer.apple.com/sf-symbols/) 个标志可以让你使用,因此你可以很容易地找到你要的标志来配合你的设计。我极力推荐你在进行下一节之前,先安装这个 App。

图 3.3 SF Symbols App
图 3.3 SF Symbols App

显示系统图片

想要在屏幕上显示系统图片的话,你可以初始化一个 Image 视图,加上 systemName 参数,如下所示:

Image(systemName: "cloud.heavyrain")

这将会建立一个图片视图,并载入指定的系统图片。如前所述,SF Symbols 与 San Francisco 字型无缝整合。你可以很容易地应用 font 修饰器来进行图片的缩放。

Image(systemName: "cloud.heavyrain")
    .font(.system(size: 100))

你可以修改字型大小来观察图片的反应,如图 3.4 所示。

图 3.4. 显示系统图片
图 3.4. 显示系统图片

同样的,因为系统图片实际上是一个字型,你可以应用其他的修饰器如之前所学过的 foregroundColor,来修改它的外观。

例如:要修改颜色为蓝色,你可以撰写程序如下:

Image(systemName: "cloud.heavyrain")
    .font(.system(size: 100))
    .foregroundColor(.blue)

要加入一个下拉式阴影效果(drop shadow effect ),你只需要使用 shadow 修饰器:

Image(systemName: "cloud.heavyrain")
    .font(.system(size: 100))
    .foregroundColor(.blue)
    .shadow(color: .gray, radius: 10, x: 0, y: 10)

使用自己的图片

在前面,我们使用了Apple 内建的图片。不过,你可能会在App 中使用自己的图片。那么,要如何使用 Image 视图来载入图片呢?

Note: 你可以任意使用自己的图片,若是你没有合适的图片,也可以至 unsplash.com 下载图片 (https://unsplash.com/photos/Q0-fOL2nqZc) ,以方便继续后面的内容。图片下载完成之后,请将档名改成“paris.jpg”。

在你的项目能够使用图片之前,首先你必须先将图片汇入素材目录( Asset Catalog ), 也就是 Assets。假设你已经准备好图片(paris.jpg ),按下 command+0 键来开启项目导航器(Project Navigator ),并选取 Assets。现在开启 Finder,并将图片拖曳至大纲视图(Outline View ),如图 3.5 所示。

图 3.5 将图片加至素材目录
图 3.5 将图片加至素材目录

如果你是 iOS App 开发新手,这个素材目录是你储存应用程序资源如图片、颜色与数据的地方。当你将图片放入素材目录时,你可以参照它的名称来下载图片。另外,你可以设定要载入图片的装置(例如:只限 iPhone )。

要在屏幕上显示图片,可撰写程序如下:

Image("paris")

你只需要指定图片名称,可在预览画布中见到图片。然而,由于图片是高解析度图片( 4437×6656 像素),你只能见到部分图片,如图 3.6 所示。

图 3.6. 载入自订的图片
图 3.6. 载入自订的图片

调整图片大小

那么,如何调整图片呢?你可以使用 resizable 修饰器,如下所示:

Image("paris")
    .resizable()

默认上,图片大小调整是使用“延伸”(stretch )模式。这表示原始图片将会被放大到填满整个屏幕画面(除了顶部与底部区域之外),如图 3.7 所示。

图 3.7. 以resizable 修饰器调整图片大小
图 3.7. 以resizable 修饰器调整图片大小

技术上而言,这个图片填满了整个 iOS 所定义的安全区域(Safe Area )。安全区域的概念已经存在一段颇长的时间了,定义为安全布局UI 组件的视图区域。举例而言,如图 3.7 所示,这个安全区域不包含顶部列(Top Bar )(也就是状态列)与底部列(Bottom Bar ) 的区域。安全区域可以避免你不小心隐藏了系统 UI 组件,例如:状态列(Status Bar )、导航列(Navigation Bar )与标签列(Tab Bar )。

既然我们正好说到这件事, 若是你想要显示全屏幕图片, 你可以设定 ignoringSafeArea 修饰器来忽略安全区域,如图 3.8 所示。

图 3.8 忽略安全区域
图 3.8 忽略安全区域

你可以针对特定的边缘来忽略安全区域,例如:要忽略底部边缘的安全区域,你只要传送 .bottom 作为参数即可:

.ignoresSafeArea(.container, edges: .bottom)

Aspect Fit 与 Aspect Fill

如果你看一下前一小节这两张图片,并与原来的做比对,你应该会发现长宽比(aspect ratio )有些失真。这个延伸模式不会去管图片的长宽比,它只是将每一边延伸来填满安全区域而已。如图 3.9 所示,要保持原来的长宽比的话,你可以像这样应用 scaledToFit 修饰器:

Image("paris")
    .resizable()
    .scaledToFit()
 图 3.9 缩放图片保持原来的长宽比
图 3.9 缩放图片保持原来的长宽比

另外,你可以使用 aspectRatio 修饰器,并设定内容模式(Content Mode)为. fit,这会达成同样的结果。

Image("paris")
    .resizable()
    .aspectRatio(contentMode: .fit)

在某些情况下,你想要图片的长宽比保持一样,但尽可能地延伸,则你可以应用 .fill 内容模式:

Image("paris")
    .resizable()
    .aspectRatio(contentMode: .fill)

我们试着来限制图片的大小,这有助于你对这两种模式能更了解。frame 修饰器可以让你控制视图的大小。举例而言,设定框架(frame )的宽度为“300 点”,图片的宽度将会限制在 300 点,如图 3.10 所示。

图 3.10. 利用 frame 控制图片的宽度
图 3.10. 利用 frame 控制图片的宽度

现在将 Image 程序以下面的代码取代:

Image("paris")
    .resizable()
    .aspectRatio(contentMode: .fill)
    .frame(width: 300)

这张图片会被缩小,但是仍保持原始长宽比。如果你修改内容模式为 .fill,图片看起来与图 3.7 几乎相同,然而当你使用selectable 模式并仔细看图 3.11,长宽比是维持不变的。

图 3.11. 使用.fill 内容模式
图 3.11. 使用.fill 内容模式

你可能会注意到图片的宽度不是我们想要的,它依然占满整个屏幕宽度。要正确运作的话,你必须使用 clipped 修饰器来消除视图额外的部分,如图 3.12 所示。

图 3.12. 使用.clipped 来裁切视图
图 3.12. 使用.clipped 来裁切视图

建立圆形图片

除了将图片裁切为长方形之外,SwiftUI 提供其他修饰器来让你将图片裁成不同的形状,例如:圆形。举例而言,如果你想要建立圆形图片,你可以像这样使用 clipShape 修饰器:

Image("paris")
    .resizable()
    .aspectRatio(contentMode: .fill)
    .frame(width: 300)
    .clipShape(Circle())

这里我们指定将图片裁成圆形。你可以传送不同参数(如 Ellipse() )来建立不同形状的图片,图 3.13 为一些示例。

图 3.13. 使用.clipShape 修饰器来建立不同形状的图片
图 3.13. 使用.clipShape 修饰器来建立不同形状的图片

不透明度的调整

SwiftUI 内建了名为 opacity 的修饰器,你可以使用这个修饰器来控制图片(或者任何视图)的不透明度。你传送一个介于0 与1 的值来指定图片的不透明度。这里,“0”代表视图完全透明,“1”则代表完全不透明。

举例而言,如果你对图片视图应用了opacity 修饰器,并设定其值为“0.5”,则这个图片将会呈现半透明(partially transparent ),如图 3.14 所示。

图 3.14 调整不透明度为50 %
图 3.14 调整不透明度为50 %

应用图片重叠

在设计App 时,有时你需要将另一个图片或文字分层放置在图片视图之上。SwiftUI 框架提供一个名为“overlay”的修饰器给开发者,来将图片进行重叠(Overlay ),例如:你要重叠一个系统图片(如 heart.fill )至目前的图片,你可以撰写程序如下:

Image("paris")
    .resizable()
    .aspectRatio(contentMode: .fill)
    .frame(width: 300)
    .clipShape(Circle())
    .overlay(
        Image(systemName: "heart.fill")
            .font(.system(size: 50))
            .foregroundColor(.black)
            .opacity(0.5)
    )

程序中的 .overlay 修饰器带入一个 View 作为参数。在上列的程序中,我们建立其他的图片(如 heart.fill ),并将其重叠在目前图片(如 Paris )之上,如图 3.15 所示。

图 3.15 在目前的图片上应用重叠
图 3.15 在目前的图片上应用重叠

事实上,你可以采用任何视图来做重叠。例如:你可以重叠一个 Text 视图在图片之上。程序写法如下:

Image("paris")
    .resizable()
    .aspectRatio(contentMode: .fit)
    .overlay(

        Text("If you are lucky enough to have lived in Paris as a young man, then wherever you go for the rest of your life it stays with you, for Paris is a moveable feast.\n\n- Ernest Hemingway")
            .fontWeight(.heavy)
            .font(.system(.headline, design: .rounded))
            .foregroundColor(.white)
            .padding()
            .background(Color.black)
            .cornerRadius(10)
            .opacity(0.8)
            .padding(),

        alignment: .top

    )

overlay 修饰器,你只需要建立一个 Text 视图,而这个文字视图将会作为图片上的重叠物件。我相信你已经熟悉了 Text 视图的修饰器,我们在前面的章节已经有介绍过。我们只是修改字型与颜色,除此之外,我们加入了间距与背景颜色。另外,要特别说明的是 alignment 参数,对于 overlay 修饰器,你可以提供一个可选值(Optional Value )来调整视图的对齐。默认是置中对齐,这里我们想要将文字重叠于图片之上,如图 3.16 所示。你可以自己将值修改为“.center”或者“.bottom”,来看看结果为何。

图3.16 在目前的图片上应用重叠
图3.16 在目前的图片上应用重叠

以重叠来使图片变暗

你不止可以重叠图片或文字在另一张图片上,你也可以应用重叠来使图片变暗。将 Image 程序以下列程序替代,并看一下效果:

Image("paris")
    .resizable()
    .aspectRatio(contentMode: .fit)
    .overlay(
        Rectangle()
            .foregroundColor(.black)
            .opacity(0.4)
    )

我们在图片上画一个矩形(Rectangle ),并将其前景色设定为“black”。为了达到变暗(darken )效果,我们设定不透明度为“0.4”,也就是 40% 的不透明度,图片现在应该变暗了。

也可以将程序重写如下完成一样的效果:

Image("paris")
    .resizable()
    .aspectRatio(contentMode: .fit)
    .overlay(
        Color.black
            .opacity(0.4)
    )

在 SwiftUI,正好 Color 也是一个视图,因此我们可以使用 Color.black 作为上层来将下层的图片变暗。

当你想要将一些亮色文字重叠在太亮的图片上时,这个技术尤其有用。我们将 Image 程序替换如下:

Image("paris")
    .resizable()
    .aspectRatio(contentMode: .fit)
    .frame(width: 300)
    .overlay(
        Color.black
            .opacity(0.4)
            .overlay(
                Text("Paris")
                    .font(.largeTitle)
                    .fontWeight(.black)
                    .foregroundColor(.white)
                    .frame(width: 200)
            )
    )

如前所述,这个 overlay 修饰器不限于 Image,你也可以将它应用在其他视图上。在上列的程序中,我们使用“Color.black”来使图片变暗。此外,我们也重叠并放置了一个 Text。如果你的步骤都正确的话,你应该会见到一个“Paris”的文字放置在深色的图片上面,如图 3.17 所示。

图 3.17. 使图片变暗并应用文字重叠
图 3.17. 使图片变暗并应用文字重叠

使用有颜色的 SF Symbols

自 iOS 15 推出后,SF Symbols 提供四种渲染模式,可以将颜色应用于符号。 根据所选择的模式,你可以为符号填上单色或多种颜色。 例如,cloud.sun.rain 是一个支持Palette Rendering 的符号。 你可以对符号应用两种或多种对比色。 下图显示如何使用 SF Symbols 应用程序测试调色板。

图 3.18. 在 SF Symbols 中使用调色板
图 3.18. 在 SF Symbols 中使用调色板

在 SwiftUI 中,你可以加入 symbolRenderingMode 修饰器来修改模式。 要建立具有多种颜色的相同符号,你可以编写如下代码:

Image(systemName: "cloud.sun.rain")
    .symbolRenderingMode(.palette)
    .foregroundStyle(.indigo, .yellow, .gray)

我们在代码中指定使用palette 模式,然后使用foregroundStyle 修饰器使用颜色。

可变颜色(Variable Colors)

在 iOS 16 中,SF Symbols 添加了一个名为 Variable Color 的新功能。 你可以通过修改百分比值来调整符号的颜色。 这在使用某些符号表示进度时特别有用。

你可以下载 SF Symbols 4 (https://developer.apple.com/sf-symbols/) 以试用此新功能。 安装应用程序后,选择 Variable 类别并选择其中一个符号。 在检查器中,你可以选取 Variable Color 按钮来启用该功能。 当你修改百分比值时,符号会对修改做出反应并填充其中的一部分。 以 slowmo 符号为例。 当你将百分比值设置为 60% 时,仅填充一些条形以表示进度。

图 3.19. 在 SF Symbols app 使用 Variable Colors
图 3.19. 在 SF Symbols app 使用 Variable Colors

可变颜色适用于 SF Symbols 内的每种渲染模式(Rendering mode)。 你可以试试修改为其他渲染模式以查看效果。

至于,要在代码中设置百分比值,你可以在建立 Image 视图时,加入 variableValue 参数并将其与百分比值一起传递:

Image(systemName: "slowmo", variableValue: 0.6)
    .symbolRenderingMode(.palette)
    .foregroundStyle(.indigo)
    .font(.largeTitle)

本章小结

在本章中,我介绍了如何以 SwiftUI 处理图片。SwiftUI 已经让开发者很容易显示图片, 并让我们使用不同的修饰器来产生各种的图片效果。如果你是独立开发者,这些新导入的 SF Symbols 会节省你寻找第三方图示的时间。

在本章所准备的示例档中,有完整的项目可以下载: