场景包含应用程序的视图层次结构。
SwiftUI 提供了一些构建块,帮助您创建应用的用户界面。其中一个构建块是 Scene ,它包含定义应用用户界面的视图层次结构。您可以在 SwiftUI 提供的场景中指定应用的视图层次结构,也可以创建自定义场景。本教程将引导您完成这两种方法。
要试验代码,请下载项目文件并在 Xcode 中打开示例。
第 1 部分
向应用程序添加场景 此示例以日记应用为例。为了描述应用用户界面的视图层次结构, My App 结构体声明了一个场景及其内容。我们来看看该结构体及其场景。
步骤 1
该示例使用 @main 属性和结构 My App 定义了一个入口点,该入口点符合 App 协议。
笔记 入口点和 My App 结构体负责应用的启动。每个 SwiftUI 应用都有且仅有一个入口点和主应用结构体。
import SwiftUI
@main
struct MyApp: App {
var body: some Scene {
WindowGroup {
TabView {
ContentView()
.tabItem {
Label("Journal", systemImage: "book")
}
SettingsView()
.tabItem {
Label("Settings", systemImage: "gear")
}
}
}
}
}
第 2 步
My App 结构实现了计算属性 body ,它返回一个场景。
计算的 body 属性可以返回一个或多个主要场景和次要场景。
import SwiftUI
@main
struct MyApp: App {
var body: some Scene {
WindowGroup {
TabView {
ContentView()
.tabItem {
Label("Journal", systemImage: "book")
}
SettingsView()
.tabItem {
Label("Settings", systemImage: "gear")
}
}
}
}
}
步骤3
在此示例中, body 返回主场景 Window Group ,该组描述了示例主窗口的视图层次结构。
Window Group 场景是较为常用的场景之一。它为您的 app 提供平台特定的行为,例如在 macOS 和 iPadOS 中支持多窗口。如需了解更多关于此场景以及 SwiftUI 提供的其他场景的信息,请参阅场景 。
import SwiftUI
@main
struct MyApp: App {
var body: some Scene {
WindowGroup {
TabView {
ContentView()
.tabItem {
Label("Journal", systemImage: "book")
}
SettingsView()
.tabItem {
Label("Settings", systemImage: "gear")
}
}
}
}
}
步骤4
视图层次结构的根节点是 Tab View ,它是一个容器视图,提供人们可以用来在不同的子视图之间切换的选项卡。
import SwiftUI
@main
struct MyApp: App {
var body: some Scene {
WindowGroup {
TabView {
ContentView()
.tabItem {
Label("Journal", systemImage: "book")
}
SettingsView()
.tabItem {
Label("Settings", systemImage: "gear")
}
}
}
}
}
步骤5
Tab View 包含两个子视图, Content View 和 Settings View 。
两者都是自定义视图。 Content View 显示日记条目列表, Settings View 显示其他视图,允许用户编辑应用的设置,例如与日记关联的帐户。
import SwiftUI
@main
struct MyApp: App {
var body: some Scene {
WindowGroup {
TabView {
ContentView()
.tabItem {
Label("Journal", systemImage: "book")
}
SettingsView()
.tabItem {
Label("Settings", systemImage: "gear")
}
}
}
}
}
步骤6
这些视图中的每一个都应用了 tab Item(_😃 修饰符,它告诉 Tab View 在每个选项卡中显示的图像和文本。
步骤7
当示例应用程序运行时,它会显示 Window Group 场景中描述的视图层次结构,这是一个带有两个选项卡的选项卡界面:Journal 和 Settings。
第 2 节
定义另一个视图层次结构 示例应用可在多种设备上运行,包括 iPhone 和 Mac。但上一节中描述的视图层次结构在 macOS 中看起来不太正确。因此,示例声明了另一个视图层次结构,以利用 Mac 特有的功能。
步骤 1
这是上一节讨论的视图层次结构。
该示例使用包含 Tab View 的 Window Group 场景来定义视图层次结构。反过来, Tab View 又包含两个子视图: Content View 和 Settings View 。
import SwiftUI
@main
struct MyApp: App {
var body: some Scene {
#if os(iOS)
WindowGroup {
TabView {
ContentView()
.tabItem {
Label("Journal", systemImage: "book")
}
SettingsView()
.tabItem {
Label("Settings", systemImage: "gear")
}
}
}
#elseif os(macOS)
WindowGroup {
AlternativeContentView()
}
Settings {
SettingsView()
}
#endif
}
}
让我们看一下示例为 macOS 定义的视图层次结构。
第 2 步
该示例定义了 Window Group 场景中的其他视图层次结构。
import SwiftUI
@main
struct MyApp: App {
var body: some Scene {
#if os(iOS)
WindowGroup {
TabView {
ContentView()
.tabItem {
Label("Journal", systemImage: "book")
}
SettingsView()
.tabItem {
Label("Settings", systemImage: "gear")
}
}
}
#elseif os(macOS)
WindowGroup {
AlternativeContentView()
}
Settings {
SettingsView()
}
#endif
}
}
步骤3
与上一个层次结构不同,此层次结构的根节点是自定义视图,即 Alternative Content View 。
笔记 前一个视图层次的根节点是容器视图, Tab View 。
import SwiftUI
@main
struct MyApp: App {
var body: some Scene {
#if os(iOS)
WindowGroup {
TabView {
ContentView()
.tabItem {
Label("Journal", systemImage: "book")
}
SettingsView()
.tabItem {
Label("Settings", systemImage: "gear")
}
}
}
#elseif os(macOS)
WindowGroup {
AlternativeContentView()
}
Settings {
SettingsView()
}
#endif
}
}
步骤4
该示例使用辅助场景 Settings 来提供应用程序菜单中可用的设置菜单项,这是 Mac 应用程序的常见功能。
笔记 Settings 场景仅在 macOS 中可用。
步骤5
Settings 场景包含自定义视图 Setting View ,该视图在“设置”菜单项提供的窗口中显示应用程序设置。
定义了两个单独的视图层次结构后,示例必须根据目标平台指定使用哪一个。
步骤6
为了编译 iOS 的第一个视图层次结构,该示例使用平台条件编译块,该块告诉 Swift 编译器仅当目标平台为 i OS 时才编译代码。
笔记 条件编译块指示 Swift 编译器根据一个或多个编译条件的值对代码块进行条件编译。有关条件编译块的更多信息,请参阅编译器控制语句.
import SwiftUI
@main
struct MyApp: App {
var body: some Scene {
#if os(iOS)
WindowGroup {
TabView {
ContentView()
.tabItem {
Label("Journal", systemImage: "book")
}
SettingsView()
.tabItem {
Label("Settings", systemImage: "gear")
}
}
}
#elseif os(macOS)
WindowGroup {
AlternativeContentView()
}
Settings {
SettingsView()
}
#endif
}
}
步骤7
该应用程序使用单独的平台条件来编译 Mac 应用程序使用的场景。
import SwiftUI
@main
struct MyApp: App {
var body: some Scene {
#if os(iOS)
WindowGroup {
TabView {
ContentView()
.tabItem {
Label("Journal", systemImage: "book")
}
SettingsView()
.tabItem {
Label("Settings", systemImage: "gear")
}
}
}
#elseif os(macOS)
WindowGroup {
AlternativeContentView()
}
Settings {
SettingsView()
}
#endif
}
}
步骤7
该应用程序使用单独的平台条件来编译 Mac 应用程序使用的场景。
import SwiftUI
@main
struct MyApp: App {
var body: some Scene {
#if os(iOS)
WindowGroup {
TabView {
ContentView()
.tabItem {
Label("Journal", systemImage: "book")
}
SettingsView()
.tabItem {
Label("Settings", systemImage: "gear")
}
}
}
#elseif os(macOS)
WindowGroup {
AlternativeContentView()
}
Settings {
SettingsView()
}
#endif
}
}
步骤8
这是在 iPhone 和 Mac 上运行该应用程序时场景的呈现方式。
该应用程序的 iPhone 版本显示带有选项卡式用户界面的场景,而 Mac 版本则显示带有分屏视图界面的场景。当您在应用程序菜单下选择“首选项”项时,Mac 应用程序还会显示辅助场景“设置”。
第 3 节
创建自定义场景 My App 结构中的源代码负责定义示例应用不同版本的视图层次结构,但代码冗长,难以维护。一项改进可以帮助提高代码的可读性和维护性,那就是使用自定义场景。自定义场景是由其他场景组合而成的场景。
步骤 1
为了描述在 iOS 设备上显示的场景,示例包含自定义场景 My Scene ,它是一个符合 Scene 协议的结构。
import SwiftUI
struct MyScene: Scene {
var body: some Scene {
WindowGroup {
TabView {
ContentView()
.tabItem {
Label("Journal", systemImage: "book")
}
SettingsView()
.tabItem {
Label("Settings", systemImage: "gear")
}
}
}
}
}
第 2 步
符合 Scene 的结构必须实现计算属性 body ,就像符合 App 协议的结构一样。
import SwiftUI
struct MyScene: Scene {
var body: some Scene {
WindowGroup {
TabView {
ContentView()
.tabItem {
Label("Journal", systemImage: "book")
}
SettingsView()
.tabItem {
Label("Settings", systemImage: "gear")
}
}
}
}
}
步骤3
body 的实现使用了来自 My App 结构中的相同代码,本教程的向应用程序添加场景部分中介绍了该代码。
import SwiftUI
struct MyScene: Scene {
var body: some Scene {
WindowGroup {
TabView {
ContentView()
.tabItem {
Label("Journal", systemImage: "book")
}
SettingsView()
.tabItem {
Label("Settings", systemImage: "gear")
}
}
}
}
}
步骤4
对于 macOS,示例包含另一个自定义场景 My Alternative Scene ,这是另一个符合 Scene 的结构。
import SwiftUI
struct MyAlternativeScene: Scene {
var body: some Scene {
WindowGroup {
AlternativeContentView()
}
#if os(macOS)
Settings {
SettingsView()
}
#endif
}
}
步骤5
它也实现了计算的 body 属性。
import SwiftUI
struct MyAlternativeScene: Scene {
var body: some Scene {
WindowGroup {
AlternativeContentView()
}
#if os(macOS)
Settings {
SettingsView()
}
#endif
}
}
步骤6
此场景的代码与 My App 结构中的代码相同,在 “定义另一个视图层次结构” 部分中讨论过。
笔记 计算出的 body 属性包含辅助场景 Settings 。此场景仅在 macOS 中可用,因此它被放置在平台条件编译块内。
import SwiftUI
struct MyAlternativeScene: Scene {
var body: some Scene {
WindowGroup {
AlternativeContentView()
}
#if os(macOS)
Settings {
SettingsView()
}
#endif
}
}
第 4 节
重构代码以使用自定义场景 有了 My Scene 和 My Alternative Scene 后,最后一步是重构 My App 结构中的代码,以便它使用自定义场景。
步骤 1
在重构 My App 结构以使用自定义场景之前,代码相当长且复杂。
这种方法会使计算属性 body 的实现更加难以维护。
import SwiftUI
@main
struct MyApp: App {
var body: some Scene {
#if os(iOS)
WindowGroup {
TabView {
ContentView()
.tabItem {
Label("Journal", systemImage: "book")
}
SettingsView()
.tabItem {
Label("Settings", systemImage: "gear")
}
}
}
#elseif os(macOS)
WindowGroup {
AlternativeContentView()
}
Settings {
SettingsView()
}
#endif
}
}
第 2 步
然而,重构代码之后, My App 结构更易于阅读和维护。
实验 更改代码,以便示例在 macOS 中使用 My Scene ,在 iOS 中 My Alternative Scene 。
import SwiftUI
@main
struct MyApp: App {
var body: some Scene {
#if os(iOS)
MyScene()
#elseif os(macOS)
MyAlternativeScene()
#endif
}
}