这个项目将是另一个游戏,尽管实际上这只是我介绍更多Swift和SwiftUI知识的一种偷偷摸摸的方式!游戏将向玩家显示一个随机的八个字母的单词,并要求他们用单词来制作单词。例如,如果入门单词为“ alarming”,则它们可能拼写为“ alarm”,“ ring”,“ main”等。

沿途你会见到的方式List,onAppear(),Bundle,fatalError(),等等-所有有用的技能,你会使用几年来。你还可以得到一些练习用@State,Alert,NavigationView,多,你应该一边可以欣赏-这是我们最后一次容易的项目!

List 的使用

我们有的时候使用List作为列表时,列表的内容会分为静态数据和动态数据。

静态

1
2
3
4
5
List {
Text("Hello World")
Text("Hello World")
Text("Hello World")
}

动态

1
2
3
4
5
List {
ForEach(0..<5) {
Text("Dynamic row \($0)")
}
}

自己动

1
2
3
List(0..<5) {
Text("Dynamic row \($0)")
}

动静结合

1
2
3
4
5
6
7
8
9
10
11
List {
Text("Static row 1")
Text("Static row 2")

ForEach(0..<5) {
Text("Dynamic row \($0)")
}

Text("Static row 3")
Text("Static row 4")
}

分组动静结合

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
List {
Section(header: Text("Section 1")) {
Text("Static row 1")
Text("Static row 2")
}

Section(header: Text("Section 2")) {
ForEach(0..<5) {
Text("Dynamic row \($0)")
}
}

Section(header: Text("Section 3")) {
Text("Static row 3")
Text("Static row 4")
}

}

更改List样式

1
.listStyle(GroupedListStyle())

遍历数组

1
2
3
4
5
6
7
8
9
struct ContentView: View {
let people = ["Finn", "Leia", "Luke", "Rey"]

var body: some View {
List(people, id: \.self) {
Text($0)
}
}
}

同理,ForEach也可以这样写:

1
2
3
4
5
List {
ForEach(people, id: \.self) {
Text($0)
}
}

捆绑包

当我们使用Image视图时,SwiftUI会知道在应用程序的资产目录中查找插图,它甚至会自动调整插图,以便为当前的屏幕分辨率加载正确的图片–这就是我们之前查看的@ 2x和@ 3x内容。

对于其他数据,例如文本文件,我们需要做更多的工作。如果你具有特定的数据格式(例如XML或JSON),这也适用-无论加载哪种文件类型,它都需要完成相同的工作。

Xcode构建你的iOS应用时,会创建一个称为“捆绑包”的东西。这种情况在包括macOS在内的所有Apple平台上都会发生,它允许系统将一个应用程序的所有文件存储在一个位置-二进制代码(我们编写的实际已编译Swift东西),所有插图,我们提供的任何其他文件需要,我们的Info.plist文件,以及更多,全部集中在一个地方。

将来,随着技能的提高,你将学习如何在单个应用程序中实际包含多个捆绑软件,并允许你在单个iOS应用程序捆绑软件中编写Siri扩展,iMessage应用程序,watchOS应用程序等内容。 ,称为主捆。

所有这些都很重要,因为通常要在捆绑包中查找放置在其中的文件。这使用了一种称为的新数据类型URL,该数据类型几乎存储了你的实际想法:一个URL,例如https://www.hackingwithswift.com。但是,URL不仅比存储网址更强大-它们还可以存储文件的位置,这就是为什么它们在这里有用的原因。

让我们开始编写一些代码。如果要读取主应用程序捆绑包中文件的URL,请使用Bundle.main.url()。如果该文件存在,则会将其发送回给我们,否则我们会回来的nil,所以这是可选的URL。这意味着我们需要像这样拆开包装:

1
2
3
if let fileURL = Bundle.main.url(forResource: "some-file", withExtension: "txt") {
// we found the file in our bundle!
}

里面的内容URL并不重要,因为iOS使用了无法猜测的路径-我们的应用程序位于自己的沙箱中,我们不应该尝试在其外部读取内容。

有了网址后,我们可以使用特殊的初始化程序将其加载到字符串中String(contentsOf:)。我们给它一个文件URL,如果可以加载,它将发送一个包含该文件内容的字符串。如果无法加载,则会引发错误,因此你需要使用try或try?类似方式调用它:

1
2
3
if let fileContents = try? String(contentsOf: fileURL) {
// we loaded the file into a string!
}

通过某个符号分割字符串为数组

使用.components(separateBy:<>)

1
2
let input = "a b c"
let letters = input.components(separatedBy: " ")
1
2
3
4
5
6
let input = """
a
b
c
"""
let letters = input.components(separatedBy: "\n")

数组返回一个随机项

还记得SwiftUI 学习笔记 21:项目 2-2 制作猜国旗应用吗?在这里选择随机数组用的是先生成一个随机数var correctAnswer = Int.random(in: 0...2),然后处理这个随机数这种方法。但是我们如果只是单纯需要返回一个随机项呢?

Swift提供了另一个有用的选择:该randomElement()方法从数组中返回一个随机项。

例如,这将从我们的数组中读取一个随机字母:

1
let letter = letters.randomElement()

删除字符串开头和结尾某种字符

另一个有用的字符串方法是trimmingCharacters(in:),它要求Swift从字符串的开头和结尾删除某些种类的字符。这使用了一种称为的新类型CharacterSet,但是大多数时候我们想要一种特殊的行为:删除空格和换行符–指的是同时包含空格,制表符和换行符。

这种行为非常普遍,它内置在CharacterSet结构中,因此我们可以要求Swift在字符串的开头和结尾处修剪所有空格,如下所示:

1
let trimmed = letter?.trimmingCharacters(in: .whitespacesAndNewlines)

检查错误单词

我想介绍一下字符串功能的最后一部分,那就是检查拼写错误的单词的能力。

该功能通过类提供UITextChecker。你可能没有意识到这一点,但是该名称的“ UI”部分带有两个附加含义:

此类来自UIKit。但是,这并不意味着我们正在加载所有旧的用户界面框架。我们实际上是通过SwiftUI自动获取的。
它是使用Apple的较旧语言Objective-C编写的。我们不需要编写Objective-C来使用它,但是对于Swift用户来说,API有点笨拙。
检查字符串中拼写错误的单词总共需要四个步骤。首先,我们创建一个要检查的单词以及一个UITextChecker可以用来检查该字符串的实例:

1
2
let word = "swift"
let checker = UITextChecker()

其次,我们需要告诉检查器我们要检查多少字符串。如果你想象一个文字处理应用程序中的拼写检查器,则可能只想检查用户选择的文本,而不是整个文档。

但是,有一个陷阱:Swift使用非常聪明,非常先进的字符串处理方式,从而使其可以使用复杂字符(例如表情符号)的方式与使用英语字母的方式完全相同。然而,Objective-C中并没有使用存储字母的这种方法,这意味着我们需要问斯威夫特利用我们的所有字符的整个长度,这样创造一个Objective-C字符串范围:

1
let range = NSRange(location: 0, length: word.utf16.count)

UTF-16是所谓的字符编码 -一种将字母存储在字符串中的方法。我们在这里使用它,以便Objective-C可以了解Swift的字符串是如何存储的;对于我们来说,这是一种很好的桥接格式。

第三,我们可以要求文本检查器报告在单词中发现任何拼写错误的地方,传递要检查的范围,在该范围内开始的位置(因此我们可以执行“查找下一个”之类的操作),是否应该换行一旦到达末尾,以及字典使用哪种语言:

1
let misspelledRange = checker.rangeOfMisspelledWord(in: word, range: range, startingAt: 0, wrap: false, language: "en")

这会返回另一个Objective-C字符串范围,告诉我们在哪里发现了拼写错误。即使那样,这里仍然存在一个复杂性:Objective-C没有任何可选概念,因此依赖于特殊值来表示丢失的数据。

在这种情况下,如果Objective-C范围返回为空(即,因为字符串正确拼写而没有拼写错误),那么我们将返回特殊值NSNotFound。

因此,我们可以检查拼写结果,看是否有这样的错误:

1
let allGood = misspelledRange.location == NSNotFound

参考资料

查看下一天的SwiftUI学习笔记

关于100days英文课程