iExpense 介绍
我们接下来的两个项目会把你的 SwiftUI 技术带向更高的水平,超越基础。因为我们会探索有多屏界面的 app ,能够加载和保存用户数据,并且 UI 更加灵活。
这头一个项目叫 iExpense ,它的功能是追踪花销,把个人花费和商务花费分隔开。实现这个 app 的过程中我们会学习到:
- 呈现和消除新的界面
- 从列表中删除行
- 保持和加载用户数据
等等。
有不少要做的事情,让我们开始吧:用 Single View App template 创建新 iOS app ,取名“iExpense” 。这是我们的主工程,但开始这个工程之前我们需要先学习一些会用到的新技术。
为什么 @State 只能在结构体中工作?
用 @ObservedObject 共享 SwiftUI 的状态
请移步阅读
https://juejin.cn/post/6844904086823763982
译自 Showing and hiding views
显示和隐藏视图
在 SwiftUI 中,显示视图有几种方式,其中最常见的一种是 sheet :它是一种在已有视图之上呈现的新视图。在 iOS 上,这种视图给到我们的是一个像卡片一样的呈现:当前视图向后滑出一段距离,新视图以动画方式出现在当前视图的上面。
Sheets 工作方式和 alerts 很像,相似之处在于我们不是通过像 mySheet.present() 这样的代码呈现它。相反,我们定义 sheet 应该显示的条件。当条件变为 true 或者 false 时,sheet 会相应地呈现或者消失。
让我们举例说明。这个例子使用 sheet 展示一个新视图。首先,我们需要创建一个希望在 sheet 中展示的视图,像这样:
struct SecondView: View {
var body: some View {
Text("Second View")
}
}上面这个视图没有什么特别之处 —— 它并不知道自己会被用作 sheet 显示,也不需要知道。
接下来我们创建初始视图,由它来展示第二个视图。我们尽量简单实现:
struct ContentView: View {
var body: some View {
Button("Show Sheet") {
// 显示 sheet
}
}
}填充注释部分需要四个步骤,我们逐一拆解。
首先,我们需要某个状态来追踪 sheet 是否应该显示。跟 alerts 一样,这个状态可以是一个最简单的布尔型,把以下属性添加到 ContentView :
@State private var showingSheet = false其次,我们需要在按钮点击时触发那个状态,把 // show the sheet注释替换成下面的代码:
self.showingSheet.toggle()第三,我们需要把 sheet 附着在视图层级的某个地方。如果你还记得,我们显示 alerts 是用 alert(isPresented:) ,用到一个对于状态的双向绑定。而我们用在 sheet 上的东西是一样的 sheet(isPresented:).
sheet() 是一个跟 alert()一样的 modifier ,所以我们可以把它添加到按钮:
.sheet(isPresented: $showingSheet) {
// contents of the sheet
}第四,我们需要确定 sheet 里要展示的东西。在例子中,我们已经知道要做什么:我们要创建并显示 SecondView 的实例。代码上就是 SecondView(),就这样。
所以,完成后的 ContentView 结构体应该是这样:
struct ContentView: View {
@State private var showingSheet = false
var body: some View {
Button("Show Sheet") {
self.showingSheet.toggle()
}
.sheet(isPresented: $showingSheet) {
SecondView()
}
}
}运行程序,点击按钮,你会看到我们的第二个视图从底部滑入上,而你可以通过往下拖拽把它关掉。
sheet 可以呈现任何的视图,所以我们可以调整 SecondView :
struct SecondView: View {
var name: String
var body: some View {
Text("Hello, \(name)!")
}
}相应调整实例化的地方:
.sheet(isPresented: $showingSheet) {
SecondView(name: "@twostraws")
}现在 sheet 会呈现 “Hello, @twostraws” 。
Swift 在背后做了大量工作:在我们给SecondView 添加了一个名字属性,Swift 会确保代码中所有 SecondView()的实例都变成 SecondView(name: "some name"),然后才能编译通过。
在我们继续之前,我还有一个东西要演示,那就是如何关闭视图。是的,你发现通过通过向下扫的操作可以关掉 sheet ,不过有的时候我们会希望通过编程的方式关闭视图 —— 比如在点击某个按钮之后关闭某个视图。
对于这种需求,SwiftUI 给了我们两个选项。其中简单一些的选项是引入另一个属性包装器 —— 是的,SwiftUI 解决问题的方式经常就是引入一个新的属性包装器。
言归正传,这个包装器叫 @Environment,它让我们可以创建存储值的属性,这种属性可以提供给外部使用。用户是处于 light mode 还是 dark mode ?用户是设置的更小的字号还是更大的字号?用户当前处于什么时区?所有这些值都来自环境,在我们的案例中,我们将从环境中读取视图的 presentation mode 。
一个视图的 presentation mode 包含两部分数据,两部分都有用:其一是存储视图当前是否呈现在屏幕上的属性,其二是一个可以给我们关闭视图的方法。
把这个属性添加到 SecondView,下面的代码会创建一个叫presentationMode的属性,并且绑定到 app 的环境中的 presentation mode 变量:
@Environment(\.presentationMode) var presentationMode现在把 SecondView中的文本视图替换成下面的按钮:
Button("Dismiss") {
self.presentationMode.wrappedValue.dismiss()
}这里面的 wrappedValue 是必须的,因为 presentationMode 实际上是一个由系统自动更新的绑定 —— 我们需要扒开它的外衣,从中查找实际的 presentation mode 的状态值,作为关闭视图的条件。
有了这个按钮,你会发现现在可以通过按钮点击来显示和隐藏 sheet 了。
我的公众号 这里有Swift及计算机编程的相关文章,以及优秀国外文章翻译,欢迎关注~