SwiftUI:修改程序状态
在SwiftUI开发人员中有一种说法,我们的“视图是其状态的函数(views are a function of their state)”,但是当这仅仅只是几个单词,一开始可能对您来说没有什么意义。
如果你在玩格斗游戏,你可能会失去一些生命,得到一些分数,收集一些宝藏,也许会拿起一些强大的武器。在编程中,我们称这些东西为状态——描述游戏当前状态的设置集合。
当你退出游戏时,该状态将被保存,当你稍后回到游戏时,你可以重新加载你的游戏以回到原来的位置。但当你在玩的时候,这些都被称为状态:所有的整数、字符串、布尔值等等,都存储在RAM中来描述你现在正在做的事情。
当我们说SwiftUI的视图是其状态的函数时,我们的意思是用户界面的外观(人们可以看到的东西和他们可以交互的东西)取决于程序的状态。例如,只有在文本字段中输入姓名后,才能单击“继续”。
这本身可能听起来很明显,但这实际上与以前使用的替代方法大不相同:用户界面是由一系列事件决定的。所以,用户现在看到的是,他们使用你的应用已经有一段时间了,已经点击了各种东西,可能已经登录或刷新了他们的数据,等等。
“事件序列”方法意味着很难存储应用程序的状态,因为恢复完美状态的唯一方法是回放用户执行的事件的确切序列。这就是为什么这么多的应用程序甚至都不试图保存你的状态,哪怕是一点点——你的新闻应用程序不会回到你读过的最后一篇文章,Twitter也不会记得你是不是在给别人打回复,而Photoshop会忘记你堆积的任何撤销状态。
让我们用一个按钮来实现这一点,在SwiftUI中,可以用一个标题字符串和一个在点击按钮时运行的操作闭包来创建这个按钮:
struct ContentView: View {
var tapCount = 0
var body: some View {
Button("Tap Count: \(tapCount)") {
self.tapCount += 1
}
}
}
这段代码看起来很合理:创建一个按钮,上面写着“tapCount”加上按钮被点击的次数,然后在每次点击按钮时向tapCount
添加1。
但是,它不会被构建,这不是有效的Swift代码。你看,ContentView
是一个结构体,它可以被创建为一个常量。如果你回想一下你学习结构体的时候,那意味着它是不可变的——我们不能自由地改变它的值。
当创建想要更改属性的结构体方法时,我们需要添加mutating
关键字:mutating func doSomeWork()
,例如。然而,Swift不允许我们创建可变计算属性,这意味着我们不能编写mutating var body: some View
——这是不允许的。
这看起来好像我们陷入了僵局:我们希望在程序运行时能够更改值,但Swift不允许我们这样做,因为我们的视图是结构体。
幸运的是,Swift为我们提供了一个称为属性包装器的特殊解决方案:我们可以在属性之前放置一个特殊的属性,有效地赋予它们超能力。在存储简单的程序状态(如按钮被点击的次数)的情况下,我们可以使用SwiftUI中名为@state
的属性包装器,如下所示:
struct ContentView: View {
@State var tapCount = 0
var body: some View {
Button("Tap Count: \(tapCount)") {
self.tapCount += 1
}
}
}
这个小小的改变足以使我们的程序工作,所以现在您可以构建它并尝试它。
@State
允许我们绕过结构体的限制:我们知道不能更改它们的属性,因为结构是固定的,但是@State
允许SwiftUI将该值单独存储在可以修改的地方。
是的,这感觉有点像作弊,你可能想知道为什么我们不使用类-它们可以自由修改。但是相信我,这是值得的:随着你的进步,你会了解到SwiftUI经常破坏和重新创建你的结构体,所以保持它们的小而简单的结构对性能很重要。
提示:在SwiftUI中存储程序状态有几种方法,您将学习所有这些方法。@State
是专门为存储在一个视图中的简单属性而设计的。因此,苹果建议我们向这些属性添加私有访问控制,比如:@State private var tapCount = 0
。