Swift Playgrounds App 2 - 引导演示
晴猫编程
编辑于 2022年04月23日 14:29
收录于文集
共14篇

Xcode 版本:13.2.1(Swift 5.5.2)

Swift Playgrounds(iPad)版本:4.0.2

cut-off

(本文技术参考主要来自 LeonardoLu【1】 制作的引导内容制作教学网页【2】,方便好上手,推荐大家看看 ^_−☆)

Swift Playgrounds app 和常规的 SwiftUI 项目还有一个不同之处,就是支持引导内容,我们可以在官方的项目中看到 apple 为每个项目都设置了引导内容。

官方项目 开始使用 app 引导截屏

在开始介绍引导内容的制作之前,需要做一个提醒。

apple 官方并没有提供任何关于引导内容制作的文档资料,或许这个功能仅仅只限于引导初学者通过官方发布的项目来上手 SwiftUI 和这个新格式的编辑,而不是让开发者使用到自己的项目里。

因为引导内容仅在 Swift Playgrounds 中编辑 app 项目时有效,并且因为 Swift Playgrounds app 不像 Playground Book 那样可以还原,所以引导过程是不可逆的,比如读完一个引导内容后会出现一个绿色的打勾,这个状态永远不会消除。

毕竟,Swift Playgrounds app 被设计成能够在 iOS 、iPadOS 上运行并允许上架的完整程序格式,我们更应该在 app 内部实现对用户的引导,本文提到的“引导内容”只能让项目在 Swift Playgrounds 的编辑体验上起到锦上添花的效果。

比如 Swift 学生挑战赛的参赛作品,如果能够在程序预览阶段能够有一个合适的引导内容,还是非常不错的。

本文所描述的操作演示录屏:

配置

在 Finder 中查看官方项目的目录,都会发现项目结构基本上都会和下图显示的差不多。

带引导内容的 app 结构

引导内容的秘密就在 Guide 文件夹里,下面可以参考这个形式,制作一个带有引导演示内容 Swift Playgronds app 项目。

首先创建一个空白的 Swift Playgronds app 项目(使用 iPad 或 Xcode 均可,本文使用 Xcode),在 Finder 中新建文件夹,将结构设置成下图所示。

设置项目结构

用 Xcode 打开项目,在 Guide 目录下添加 Tutorial 文件(File - New - File... - Tutorial File)并命名(如 Guide.tutorial),并清空默认内容。

Tutorial 文件

接着在 Guide 目录下创建一个 Resources 目录,引导内容的资源(包括本地化文件、图片、代码)都要放在 Resources 目录里,并且因为引导内容必须要通过本地化文件进行展示,所以我们应该在 Resources 目录下至少添加一个本地化文件目录(本例添加了英文和简体中文),同时每个本地化文件目录里都创建一个空白的 Localizable.strings 文件和一个空白的 Glossary.plist文件(.plist 文件用来实现词汇表)。

设置 Guide 结构

这样基本的结构就设置好了,但是因为我们修改了结构,Xcode 找不到 app 的位置了,这需要我们编辑一下 Package.swift,告诉 Xcode  从哪里打开程序。

在 Finder 中打开项目的 Package.swift 文件 ,将 .executableTarget 的 path 参数设置为 app,即当前程序的路径。

代码块
Swift
自动换行
复制代码
// /Package.swift

	targets: [
        .executableTarget(
            name: "AppModule",
            path: "App"
        )
    ]
复制成功

此时将项目 AirDrop 到 iPad 上运行,如果 Swift Playgrounds 将 Guide 目录隐藏,那么结构的设置就是正确的。

iPad 会将 Guide 目录隐藏

设置引导内容

基本结构

回到 Xcode ,我们可以在 Guide.tutorial 文件中确定引导内容的结构,基本的设置如下:

代码块
Swift
自动换行
复制代码
// /Guide/Guide.tutorial:

@GuideBook(title: "MyApp", icon: title.png, background: titleBackground.png, firstFile: MyApp.swift) {
    @Guide {
        @Step(title: "Look Around Code") {
            @ContentAndMedia {

            }
        }
    }
}
复制成功

Guide.tutorial 中至少要包含以上内容才能够显示引导页面,如果不按照上述结构编写或者结构缺失会导致引导页不显示或打开项目时 Swift Playgorunds 崩溃,没错,就是直接崩溃,总之很刺激就是了。

讲一下上面几个内容结构:

  • GuideBook:包含引导内容的全部结构。icon 和 background 参数的值是固定值,目前没有测试出这两个参数有任何效果,firstFile 参数可以确定进入引导后打开的第一个 Swift 文件

  • Guide:用来对引导内容进行结构上的划分,一个 GuideBook 里至少要有一个 Guide。

  • Step:用来确定一个引导页面结构(引导内容可以由多个页面组成)。

  • ContentAndMedia:引导页面顶部的介绍,可以排版文字、图片内容。

Step 和 ContentAndMedia

上面的内容输入以后,在 Swift Playgrounds 打开项目就会发现支持引导内容显示了,但是里面的内容看起来好像有点不正常。

显示未设置内容的引导页

这是因为 Guide.tutorial 只能确定引导内容的结构,具体显示的内容还需要在本地化文件中设置,如下所示(本例只对中文内容进行编辑,即 zh_CN.lproj 目录下的文件,其他语言的编辑方式是一样的):

代码块
Swift
自动换行
复制代码
// /Guide/Resources/zh_CN.lproj/Localizable.strings

"GuideBook..Guide0..StepLook Around Code..title" = "参观 MyApp.swift";
复制成功

此时再在 Swift Playgrounds 中打开就会看到内容按预期显示了:

引导页标题

添加引导内容

下面我们在 Guide.tutorial 中继续添加内容:

代码块
Swift
自动换行
复制代码
// /Guide/Guide.tutorial

@GuideBook(title: "MyApp", icon: title.png, background: titleBackground.png, firstFile: MyApp.swift) {
    @Guide {
        @Step(title: "Look Around Code") {
            @ContentAndMedia {
                这个 Swift Playgrounds App 是使用 SwiftUI 构建的。
                
                ![](swiftui_logo.png)
                
                程序的入口在 MyApp.swift 文件里设置。
            }
            
            @Task(type: walkthrough, title: "MyApp.swift Code", id: "myAppSwiftCode", file: MyApp.swift) {
                看一下 MyApp.swift 里的代码吧!
                @Page(id: "MyApp.import", title: "page0") {
                    SwiftUI 在这里导入。
                }
                @Page(id: "MyApp.app", title: "page1") {
                    App 协议是用来表示应用程序结构和行为的类型。
                }
                @Page(id: "MyApp.struct", title: "page2") {
                    这里通过声明一个符合 App 协议的结构来创建一个应用程序。
                }
                @Page(id: "MyApp.main", title: "page3") {
                    在结构声明之前加上 @main 属性,以表明您的自定义 App 协议符合器提供了进入您的应用程序的入口点。
                }
                @Page(id: "MyApp.body", title: "page4") {
                    程序的视图在 body 属性中实现,现在程序只有一个叫做 ContentView 的视图,它是现在 ContentView.swift 文件中,我们一起去看一看吧!
                }
            }
        }
    }
}
复制成功

在 ContentAndMedia 中添加了三个段落的内容,然后添加了新的引导结构 Task 和 Page

  • Task:引导页面中可点击的引导任务,点击后会将自己包含的 Page 依次显示在顶部进行引导,type: walkthrough 表示任务为演示任务,用户把 Page 看完后自动进入到下一个任务,file 参数用于设置进入此引导任务时打开的 Swift 文件。

  • Page:进入某个引导任务后显示在 Swift Playgrounds 顶部内容。

Task 和 Page

此时在 Swift Playgrounds 中打开项目就会看到结构添加上去了。

未设置内容的引导页

到这一步应该可以更清楚的感受到,Swift Playgrounds 只会根据在 Guide.tutorial 的内容创建引导页面的结构,并根据一种规律给每个部分进行命名,也就是说,Guide.tutorial 中的每个段落都改成一个字,这个结构都不会改变,我们在 Guide.tutorial 中写的文字只是为了方便自己理解而已。如果要让具体的内容显示出来,还需要修改 Localizable.strings 文件。

在修改之前,先讲一下这些结构名的规律,相信大家第一眼也能看出大概了,举几个例子看一下就能知道:

代码块
Swift
自动换行
复制代码
// Step的 标题
GuideBook..Guide[序号]..Step[title]..title
// Step - ContentAndMedia 内容
GuideBook..Guide[序号]..Step[title]..LearningCenterContent..Paragraph[序号]
// Step - Task 标题
GuideBook..Guide[序号]..Step[title]..Task[id]..title
// Step - Task - 在引导页中的描述内容
GuideBook..Guide[序号]..Step[title]..Task[id]..Paragraph[序号]
// Step - Task - Page 内容
GuideBook..Guide[序号]..Step[title]..Task[id]..Page[title]..Paragraph[序号]
复制成功

这个规律不一定靠谱,仅作参考,因为在添加结构的过程中,之前的结构名可能会因为新的结构添加而改变,如果看到这样的情况,在  Localizable.strings 里对应进行设置即可

还有一点尤其需要注意,上面提到的序号是面向全局的,从 GuideBook  的第一个文本内容开始计

比如根据刚才的修改,ContentAndMedia 理由三个段落,那么 ContentAndMedia 里三个 Paragraph 的序号分别是 0、1、2,所以ContentAndMedia 后面的 Task 里 Paragraph 会从 3 开始记。

引导结构的序号是全局有效的

这就意味着如果我们在 ContentAndMedia 中把某个段落删掉,或者添加一个段落,对后面所有 Paragraph 的序号都会有影响,Localizable.strings 中的内容也需要进行对应的修改,因此最好将引导的内容完全设计好后再进行实现,避免重复、频繁的修改

下面在 Localizable.strings 中设置对应的内容:

代码块
Swift
自动换行
复制代码
// /Guide/Resources/zh_CN.lproj/Localizable.strings:

"GuideBook..Guide0..StepLook Around Code..title" = "参观 MyApp.swift";
"GuideBook..Guide0..StepLook Around Code..LearningCenterContent..Paragraph0" = "这个 Swift Playgrounds App 是使用 SwiftUI 构建的。";
"GuideBook..Guide0..StepLook Around Code..LearningCenterContent..Paragraph1" = "![](swiftui_logo.png)";
"GuideBook..Guide0..StepLook Around Code..LearningCenterContent..Paragraph2" = "程序的入口在 MyApp.swift 文件里设置。";
"GuideBook..Guide0..StepLook Around Code..TaskmyAppSwiftCode..Paragraph3" = "看一下 MyApp.swift 里的代码吧!";
"GuideBook..Guide0..StepLook Around Code..TaskmyAppSwiftCode..title" = "阅读 MyApp.swift 代码";
"GuideBook..Guide0..StepLook Around Code..TaskmyAppSwiftCode..Pagepage0..Paragraph4" = "SwiftUI 在这里导入。";
"GuideBook..Guide0..StepLook Around Code..TaskmyAppSwiftCode..Pagepage1..Paragraph5" = "App 协议是用来表示应用程序结构和行为的类型。";
"GuideBook..Guide0..StepLook Around Code..TaskmyAppSwiftCode..Pagepage2..Paragraph6" = "这里通过声明一个符合 App 协议的结构来创建一个应用程序。";
"GuideBook..Guide0..StepLook Around Code..TaskmyAppSwiftCode..Pagepage3..Paragraph7" = "在结构声明之前加上 @main 属性,以表明您的自定义 App 协议符合器提供了进入您的应用程序的入口点。";
"GuideBook..Guide0..StepLook Around Code..TaskmyAppSwiftCode..Pagepage4..Paragraph8" = "程序的视图在 body 属性中实现,现在程序只有一个叫做 ContentView 的视图,它是现在 ContentView.swift 文件中,我们一起去看一看吧!";
复制成功

除此之外,图片资源需要加入到 /Guide/Resources 中。

添加图片

在 Swift Playgrounds 中打开项目,会看到内容显示了。

显示引导内容

因为 Localizable.strings 未编译可能导致的问题

如果 Guide.tutorial 和 Localizable.strings 内容确认没有错误,但是在使用 Swift Playgrounds 打开后引导页面仍然只显示结构,并没有显示内容,或者只显示了部分内容,这是因为 Swift Playgrounds 加载 Localizable.strings 时出现了问题,退出 Swift Playgrounds 再重新打开,就能够看到内容正确显示了。

造成这种问题的根本原因是使用了未编译的 Localizable.strings,我们将本地化文件编译后使用就能避免这个问题了,这一步适合在引导内容完成后进行,本文后面会说。

指示代码片段

在官方的 Swift Playgrounds 项目中,Page 引导结构在显示时总是能够通过淡蓝背景色将代码提示出来。

提示代码片段

这个功能需要用到的语法是:

代码块
Swift
自动换行
复制代码
标记任务输入位置://#-learning-task(Task id)
在 Page 需要选中的内容开头输入:/*#-code-walkthrough(Page id)*/
在 Page 需要选中的内容结尾输入:/*#-code-walkthrough(Page id)*/
复制成功

表明代码片段需要用到 Page 的 id ,所以我们在 Guide.tutorial 中编写内容时应该尽可能保证每个 Page id 是唯一的。

//#-learning-task(myappSwiftCode) 标记的作用和代码输入有关,如果 Page 中有代码,用户可以将展示的代码一件输入到标记位置。如果 Page 中没有代码,仅展示文字内容,这个标记是可有可无的,但是最好还是放在文件开头(因为官方的项目也是这么做的)。

目前为止本例的引导内容都和 Myapp.swit 有关,要让 Page 提示对应的代码,Myapp.swift 的 内容应为:

代码块
Swift
自动换行
复制代码
// /App/MyApp.swift:

//#-learning-task(myAppSwiftCode)
/*#-code-walkthrough(MyApp.import)*/
import SwiftUI
/*#-code-walkthrough(MyApp.import)*/
/*#-code-walkthrough(MyApp.main)*/
@main
/*#-code-walkthrough(MyApp.main)*/
/*#-code-walkthrough(MyApp.struct)*/
struct MyApp:/*#-code-walkthrough(MyApp.app)*/ App /*#-code-walkthrough(MyApp.app)*/{
    /*#-code-walkthrough(MyApp.body)*/
    var body: some Scene {
        WindowGroup {
            ContentView()
        }
    }
    /*#-code-walkthrough(MyApp.body)*/
}
/*#-code-walkthrough(MyApp.struct)*/

复制成功

这样在 Swift Playgrounds 中运行时,就可以在 Page 显示时提示对应的代码了。

提示代码效果

欢迎词和开始引导按钮

欢迎词可以在程序打开后自动显示在顶部。

官方项目 图像画廊 中的欢迎词

如果不设置欢迎词,项目在 Swift Playgrounds 打开后会优先显示引导页面而不是预览,欢迎词设置以后,项目在 Swift Playgrouds 中打开后会优先显示预览,但是会弹出欢迎词,点击欢迎词弹窗右上角的 “了解更多” 按钮后会跳转到引导页面

欢迎词应该作为 GuideBook 的第一个结构:

代码块
Swift
自动换行
复制代码
// /Guide/Guide.tutorial:

@GuideBook(title: "MyApp", icon: title.png, background: titleBackground.png, firstFile: MyApp.swift) {
    @WelcomeMessage(title: "MyApp") {
        欢迎来到这个项目,它为了介绍引导内容的制作而生!
    }
    @Guide {
        @Step(title: "Look Around Code") {
            @ContentAndMedia {
......
复制成功

WelcomeMessage 加上去以后,我们会发现的显示乱套了。

因为序号显示错乱的引导内容

因为 WelcomeMessage 里面有一个段落( Paragraph),这个段落作为引导结构的第一个段落,它的序号是 Paragraph0。而刚才说过,引导结构名中的序号排列是全局的,这也就意味着后面所有的段落的序号都要加 1 才准确,这些都要在  Localizable.strings 文件里进行修改。

下面在 Localizable.strings 中设置对应的内容:

代码块
Swift
自动换行
复制代码
// /Guide/Resources/zh_CN.lproj/Localizable.strings:

"GuideBook..WelcomeMessageMyApp..title" = "你好,开发者!";
"GuideBook..WelcomeMessageMyApp..Paragraph0" = "欢迎来到这个项目,它为了介绍引导内容的制作而生!";

"GuideBook..Guide0..StepLook Around Code..title" = "参观 MyApp.swift";
"GuideBook..Guide0..StepLook Around Code..LearningCenterContent..Paragraph1" = "这个 Swift Playgrounds App 是使用 SwiftUI 构建的。";
"GuideBook..Guide0..StepLook Around Code..LearningCenterContent..Paragraph2" = "![](swiftui_logo.png)";
"GuideBook..Guide0..StepLook Around Code..LearningCenterContent..Paragraph3" = "程序的入口在 MyApp.swift 文件里设置。";
"GuideBook..Guide0..StepLook Around Code..TaskmyAppSwiftCode..Paragraph4" = "看一下 MyApp.swift 里的代码吧!";
"GuideBook..Guide0..StepLook Around Code..TaskmyAppSwiftCode..title" = "阅读 MyApp.swift 代码";
"GuideBook..Guide0..StepLook Around Code..TaskmyAppSwiftCode..Pagepage0..Paragraph5" = "SwiftUI 在这里导入。";
"GuideBook..Guide0..StepLook Around Code..TaskmyAppSwiftCode..Pagepage1..Paragraph6" = "App 协议是用来表示应用程序结构和行为的类型。";
"GuideBook..Guide0..StepLook Around Code..TaskmyAppSwiftCode..Pagepage2..Paragraph7" = "这里通过声明一个符合 App 协议的结构来创建一个应用程序。";
"GuideBook..Guide0..StepLook Around Code..TaskmyAppSwiftCode..Pagepage3..Paragraph8" = "在结构声明之前加上 @main 属性,以表明您的自定义 App 协议符合器提供了进入您的应用程序的入口点。";
"GuideBook..Guide0..StepLook Around Code..TaskmyAppSwiftCode..Pagepage4..Paragraph9" = "程序的视图在 body 属性中实现,现在程序只有一个叫做 ContentView 的视图,它是现在 ContentView.swift 文件中,我们一起去看一看吧!";
复制成功

此时在 Swift Playgrounds 中打开项目,引导内容就可以正常显示了。

接下来我们实现开始引导按钮,引导按钮是显示在 ContentAndMedia 底部,点击以后就会进入第一个引导任务。

引导按钮

引导按钮一般放在第一个引导页面(Step)的 ContentAndMedia 结尾。

代码块
Swift
自动换行
复制代码
// /Guide/Guide.tutorial:

......
		@Guide {
        @Step(title: "Look Around Code") {
            @ContentAndMedia {
                这个 Swift Playgrounds App 是使用 SwiftUI 构建的。
                
                ![](swiftui_logo.png)
                
                程序的入口在 MyApp.swift 文件里设置。
                
                @GuideButton(type: walkthrough, title: "Start Guide", description: "AX description for button")
            }
......
复制成功

代码块
Swift
自动换行
复制代码
// /Guide/Resources/zh_CN.lproj/Localizable.strings:

......
"GuideBook..Guide0..StepLook Around Code..LearningCenterContent..GuideButtonStart Guide..title" = "开始演示!";
......
复制成功

此时在 Swift Playgrounds 中打开项目,可以看到开始引导按钮正常显示。

在 Page 中显示代码

在 Guide.tutorial 中新添加一个 Task,内容与 ContentView.swift 有关

代码块
Swift
自动换行
复制代码
// /Guide/Guide.tutorial

......
            @Task(type: walkthrough, title: "ContentView.swift Code", id: "contentViewSwiftCode", file: ContentView.swift) {
                看一下 ContentView.swift 里面的代码吧
                @Page(id: "ContentView.vstack", title: "page0") {
                    ContentView 的结构使用一个 VStack 容器布局
                }
                @Page(id: "ContentView.controls", title: "page1") {
                    里面有 Image 和 Text 两个控件
                }
                @Page(id: "ContentView.addtext", title: "page2") {
                    可以试试在现有的图片和文字下面再添加一个文本,你可以使用下面的代码:
                    
                    ```
                    Text("WWDC22")
                    ```
                }
                @Page(id: "ContentView.biggerfont", title: "page3", isAddable: false) {
                    试试让字体更大一点,你可以参考下面的代码:
                    
                    ```
                    VStack {
                    ...
                    }
                    .font(.largeTitle)
                    ```
                }
            }
......
复制成功

代码块
Swift
自动换行
复制代码
// /Guide/Resources/zh_CN.lproj/Localizable.strings:

"GuideBook..Guide0..StepLook Around Code..TaskcontentViewSwiftCode..Paragraph10" = "看一下 ContentView.swift 里面的代码吧";
"GuideBook..Guide0..StepLook Around Code..TaskcontentViewSwiftCode..Pagepage3..title" = "阅读 ContentView.swift 代码";
"GuideBook..Guide0..StepLook Around Code..TaskcontentViewSwiftCode..Pagepage0..Paragraph11" = "ContentView 的结构使用一个 VStack 容器布局";
"GuideBook..Guide0..StepLook Around Code..TaskcontentViewSwiftCode..Pagepage1..Paragraph12" = "里面有 Image 和 Text 两个控件";
"GuideBook..Guide0..StepLook Around Code..TaskcontentViewSwiftCode..Pagepage2..Paragraph13" = "可以试试在现有的图片和文字下面再添加一个文本,你可以使用下面的代码:";
"GuideBook..Guide0..StepLook Around Code..TaskcontentViewSwiftCode..Pagepage3..Paragraph14" = "试试让字体更大一点,你可以参考下面的代码:";
复制成功

代码块
Swift
自动换行
复制代码
// /App/ContentView.swift:

import SwiftUI

struct ContentView: View {
    var body: some View {
        /*#-code-walkthrough(ContentView.vstack)*/
        VStack {
            /*#-code-walkthrough(ContentView.controls)*/
            Image(systemName: "globe")
                .imageScale(.large)
                .foregroundColor(.accentColor)
            Text("Hello, world!")
            /*#-code-walkthrough(ContentView.controls)*/
            //#-learning-task(contentViewSwiftCode)
        }
        /*#-code-walkthrough(ContentView.vstack)*/
    }
}

复制成功

注意,必须要在文件中对应位置标记 //#-learning-task(contentViewSwiftCode) ,Page 在显示代码时才能有一个 “添加” 按钮,这个按钮只能用一次,点击以后会自动输入到 标记位置。新添加的 Task 里面,第三个 Page 设置了 isAddable: false ,这表示不显示 “添加按钮” 只让用户阅读。

继续添加引导内容

可以参考现在的结构,继续添加新的 Page、Task、Step 或 Guide 结构来扩展引导内容,如果内容结构里有超过 1 个 Step(引导页面),右上角会出现一个目录按钮,本文就不做演示了,大家可以自己尝试。

引导目录

词汇表

词汇表能够让制定的文本内容编程按钮,点击以后会有弹窗对选定内容进行更详细的解释。

词汇表

要使用词汇表,需要在 Localizable.strings 文件中的对应位置使用专门的修饰语法:

代码块
Swift
自动换行
复制代码
// /Guide/Resources/zh_CN.lproj/Localizable.strings:

......
"GuideBook..Guide0..StepLook Around Code..LearningCenterContent..Paragraph1" = "这个 Swift Playgrounds App 是使用 [SwiftUI](glossary://SwiftUI) 构建的。";
......
复制成功

Guide.tutorial 中的内容不做修改是没有问题的,但是为了理解方便,最好进行对应修改,让内容和 Localizable.strings 保持一致。

glossary 后面的内容是词汇表用于检索的 Key ,由开发者自己设置,一般直接使用要解释的名词。

一些原生开发框架内的对象可以引用 Swift Playgrounds 内置的文档,如:

代码块
Swift
自动换行
复制代码
[HStack](doc://com.apple.documentation/documentation/swiftui/hstack)
复制成功

接下来在本地化文件的 Glossary.plist 文件中添加对应的键值对即可:

/Guide/Resources/zh_CN.lproj/Glossary.plist:

中文 Glossary.plist SwiftUI 设置

每个需要词汇表解释的对象都需要加入到上图中的 Terms 字典里,而 _LOCALIZABLE_、Definition 、Title 是固定格式,每一条词汇释义都按照这个格式添加就可以了。

使用编译后的 Localizable.strings

最后,我们需要将 Localizable.strings 文件编译后使用,确保其在每次打开项目时都能被 Swift Playgrounds 顺利读取。

新建一个 Xcode app 项目,将本地化文件(本例中为 en.lproj 和 zh_CN.lproj)目录拖入项目中。

将本地化文件加入项目中

编译项目(菜单 - Product - Build 或快捷键 command + B)后,在 菜单 - Product - Show Build Folder in Finder 进入编译目录,找到 .app 文件,右键 - 显示包内容 进入目录,在本地化目录中找到编译后的 Localizable.strings 文件,用来替换掉 Swift Playgrounds app 项目中的对应文件即可。

找到编译后的本地化文件

编译过后的 Localizable.strings 文件内容已无修改可能,为了方便以后修改,请对未编译的文件进行备份,替换编译文件的步骤最好在所有引导内容设置好以后进行。

以上就是本文全部内容,如有疏漏或错误之处,欢迎私信交流 。

参考

【1】Leonardo 的 GitHub 主页:

https://github.com/LeonardoLu

【2】Leonardo Lu - Meet .swiftpm app:

https://leonardolu.github.io/appleBox/tutorials/welcome/spm_writetutorials/