[SwiftUI练级-1]--修改程序状态
坊间流传着“视图是它们状态的函数”的说法,这个描述一开始对你来说可能没有什么感觉。想象你在玩一个格斗游戏,你有几条命,拿到了一些分数,收集了一些财宝,说不定里面还有非常强大的武器。编程上,我们把这些东西称为 状态 —— 所有描述游戏当前情况的活跃设定。当你退出游戏时,状态将被保存;让你稍后再回到游戏时,你将重新加载游戏,回到上一次的游戏的地方,这就叫做 状态:所有的整数,字符串,布尔值等, 所有被存储在 RAM 中用以描述你刚才正在做的事情的数据。当我们说 SwiftUI 的视图是它们状态的函数时,我们指的是UI的样子,即你的程序中用户看到和可交互的部分。它们是由你的程序状态决定的。举个例子,如果没有在文本框里输入文字,就不能点击“继续”按钮。虽然听起来显而易见,但这个跟之前的方式其实区别挺大的:UI由一系列事件决定。“事件序列”这种方式意味着存储app的状态会很困难,因为完美的拿回同样的状态需要精确执行用户曾经触发过的所有事件序列。这也是某些app甚至就干脆不尝试存储你的任何状态。因此,你的新闻app不会尝试返回你上一次读的文章。让我们用一个按钮来实践,SwiftUI可用用一个标题字符串加上一个动作闭包,闭包将在按钮被点击时执行:
struct ContentView: View {
var tapCount = 0
var body: some View {
Button("Tap Count: \(tapCount)") {
self.tapCount += 1
}
}
}
上面的代码看起来已经足够合理了:创建一个按钮:显示它自己被点击的次数。每点击一次,tapCount加1。但是,这份代码是无法编译通过的——因为 ContentView 是一个结构体,是以常量方式创建。它是 immutable 的,不能改变值。因此,当创建了结构体属性,如果你想要在方法中改变这些属性,你需要在方面前面添加 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 专门为简单属性而设计,并且只服务于单个视图。 因此,Apple 建议我们在这些属性前面添加 private 访问控制,就像这样:
@State private var tapCount = 0。
网友评论