让不懂编程的人爱上iPhone开发( iOS13+Swift5.1+Xcode11版)-08

欢迎继续我们的学习。

在上一课的内容中,我们通过print()函数在Console视图中添加了一个只有开发者可以看到,用户完全没感觉的按钮交互,略有点坑。

接下来我们将学习如何让用户也可以跟我们的按钮交互。

State和SwiftUI

作为iOS开发的新概念,SwiftUI的核心之一就是state。

那么到底什么是state呢?我们先不要查英汉词典,先来看看很多人比较熟悉的东西,也就是汽车的仪表盘。

在汽车的仪表盘中,车速里程表、和燃油表等算是最重要的组成部分了,

通过这些仪表,我们可以轻松查看汽车的当前驾驶速度、燃油油位、引擎温度和驾驶历程等信息,每种信息都可以用一个数字来表示。

除此之外,仪表盘还提供了各种警示灯,比如“引擎检查”灯,低油量警示灯,安全带未正确佩戴警示灯,等等。以上各种警示灯要吗开启,以提示司机汽车可能存在某方面需要注意的问题,要吗处于关闭状态,表示一切安好(至少在电子仪器测量的眼中)。这些开关状态信息可以用binary二进制来表示。

以上的所有信息,包括仪表盘上显示的各种数字信息,以及警示灯的开关状态,通通都属于汽车的state(状态)。

司机可以通过自己的操作来改变汽车的state,从而在仪表盘上实时显示全新的state。例如,当司机踩下油门时,汽车的驾驶速度会提高,从而让车速表上的数字增加。当司机踩下刹车时,汽车的驾驶速度会降低,从而让车速表上的数字减少。

内部环境因素的变化也会改变汽车的state,并立即显示在仪表盘上。比如当汽车的燃油快耗尽时,燃油表的状态会从F(Full)变到E(Empty)。但是当司机给汽车加满油后,燃油表又会回到F。

另外一个例子则是”维护提示“灯。当汽车达到预设的驾驶历程后,汽车的状态从不需要维护转为需要维护保养,从而让这个灯开启。而一旦汽车完成维护保养,汽车的state再次转为不需要维护保养,从而让这个灯关闭。

以上关于汽车的所有state共同组成了一个更复杂的概念,state space(状态空间)。

单一按钮示例项目的state space

好了,回到我们当前的示例。

和刚才举的汽车例子相比,只有一个按钮的示例项目的state space显然要简单多了。

在此前的待办事务清单中,我们曾指出需要实现的第一个里程碑,也就是触碰按钮时出现一个警告提示。

当前的应用包含了两个状态:

1.欢迎:

只需要在界面上显示”Welcome to my first app!“

当用户打开应用后,即便什么也不做,也可以看到这行信息。

2.提示:

当用户触碰按钮后,就会看到这个提示信息。

如果用state diagram(状态图)的方式来表示,那么当前应用的状态空间可以这样来表示:

其中不同颜色的矩形代表了应用的两种不同状态。

箭头代表从一种状态切换到另一种状态,而箭头上的文字则是状态切换的原因。

1.从”欢迎“到”提示“:

当用户触碰”Hit me”按钮时,就会触发状态切换。

2.从”提示“到”欢迎“:

当用户dismiss了提示信息后,就会触发该状态切换。

SwiftUI和state space

在SwiftUI中编写用户界面的代码跟绘制刚才的状态图非常类似。

如果说应用的状态图中显示了所有可能的状态,以及状态之间的所有可能切换方式。

那么在SwiftUI应用的代码则包含了所有可能的界面布局,以及布局之间的过渡切换。

让我们回过头看看当前的用户界面相关代码:

struct ContentView: View {
    var body: some View {
        VStack {
           Text("2020年面对困难不要害怕,武汉加油!")
               .fontWeight(.black)
               .foregroundColor(.red)
           Button(action:
              {
                    print("这辈子都没见过这样的要求~")
           }) {
                    Text("过来打我啊")
           }
      }
   }
}

注意,ContentView的body属性定义了界面的整体布局。

目前body中包含了一个VStack,通过它将Text文本对象和Button按钮对象进行垂直排列。

当前的代码定义了Welcome”欢迎“状态下的界面布局,接下来我们需要编写关于Alert(提示)状态的相关代码。

不过在此之前,我们还需要明确应用的state状态。

使用变量代表应用的state

我们已经看到了body这个变量是如何定义”欢迎“状态下的界面布局,接下来需要为应用的状态创建一个单独的变量。

该变量需要保存两种信息:

1.其中一个信息代表应用没有显示提示信息的状态

2.另外一个信息代表应用显示了提示信息的状态。

对于此类信息的保存,我们用某种叫布尔变量的东西比较合适。

布尔变量只有两个值,true和false,也就是说是,不是。

关于布尔数学和布尔这个人,感兴趣的童鞋可以去搜索一下:

乔治·布尔_百度百科baike.baidu.com图标


这里还有一个八卦,深度学习三巨头之一杰弗里.辛顿教授的曾祖母的父亲就是大名鼎鼎的乔治.布尔。

深度学习巨擘—杰弗里辛顿,他背后不可思议的家族www.sohu.com图标

所以这个怎么喷呢?这恐怕就是传说中的科学贵族吧。

好了,不再八卦,回到我们的代码。

我们需要创建一个名为alertIsVisible的布尔类型变量,当应用刚启动时,提示框默认是不显示的,也就是alertIsVisible的初始值是false。

只有当用户触碰了按钮后,alertIsVisible的值才会更改为true,同时显示出对应的提示框信息。

更改刚才的代码如下:

struct ContentView: View {
 @State var alertIsVisible: Bool = false

    var body: some View {
        VStack {
           Text("2020年面对困难不要害怕,武汉加油!")
               .fontWeight(.black)
               .foregroundColor(.red)
           Button(action:
              {
                    print("这辈子都没见过这样的要求~")
           }) {
                    Text("过来打我啊")
           }
      }
   }
}

这里我们只添加了一行代码而已,

@State var alertIsVisible: Bool = false

这行代码的意思很简单,我们定义了一个名为alertIsVisible的变量,它属于Bool类型,它的初始值是false。

至于@State,是个新东西。通过这个让开发者知道,alertIsVisible变量用于存储应用的状态。同时,它还告诉Swift注意密切关注该变量的内容变化,从而执行对应的任务。

接下来我们需要添加代码,让alertIsVisible在按钮触碰时更改状态:


struct ContentView: View {
 @State var alertIsVisible: Bool = false

    var body: some View {
        VStack {
           Text("2020年面对困难不要害怕,武汉加油!")
               .fontWeight(.black)
               .foregroundColor(.red)
           Button(action:
              {
                    print("这辈子都没见过这样的要求~")
                    self.alertIsVisible = true //更改状态
           }) {
                    Text("过来打我啊")
           }
      }
   }
}

这里我们新增了的一行代码用于更改alertIsVisible变量的状态:

 self.alertIsVisible = true //更改状态

好吧,这里又冒出个self是什么东西?

alertIsVisible是ContentView对象的一种属性,如果在是对象的外面使用它,我们需要用ContentView.alertIsVisible这种形式。但现在我们就在ContentView对象内部使用它,所以就用self代替ContentView对象了。

显示其它状态下的布局

这里再次重复一下:在SwiftUi中,我们必须定义所有可能状态下的布局。

此前我们已经定义了Welcome状态下的界面布局,接下来需要定义Alert状态下的界面布局。

在这种状态下,我们只需要让提示对话框显示就好。好在Button对象有一个内置的alert()方法,可以让我们轻松完成这一工作。

接下来更改刚才的代码如下:

struct ContentView: View {
 @State var alertIsVisible: Bool = false

    var body: some View {
        VStack {
           Text("2020年面对困难不要害怕,武汉加油!")
               .fontWeight(.black)
               .foregroundColor(.red)
           Button(action:
              {
                    print("这辈子都没见过这样的要求~")
                    self.alertIsVisible = true //更改状态
           }) {
                    Text("过来打我啊")
           }
              .alert(isPresented: self.$alertIsVisible){
                    Alert(title: Text("你好"), 
                          message: Text("这样的要求不算奇怪"), 
                          dismissButton: .default(Text("太棒了!")))
                }
      }
   }
}

先不去管上面新添加代码的含义,先跑起来给自己点小小的成就感再说。

点击Xcode工具栏上的运行按钮,可以在模拟器上触碰按钮,看到类似下面的界面:

现在来看我们刚刚添加的代码:

 .alert(isPresented: self.$alertIsVisible){
 Alert(title: Text("你好"), message: Text("这样的要求不算奇怪"), dismissButton: .default(Text("太棒了!")))
 }

alert()是Button的一个方法,它用于向用户显示一个提示信息。该方法需要两个参数。所谓的参数,就是当我们需要让某个方法或函数执行某些特定任务时,向它提供的基本信息。

举个例子,如果我们让加法函数执行两个数字之和,那就要提供两个参数,也就是要相加的两个数字。

在这里,alert()方法也需要两个参数:

1.到布尔状态变量的绑定(或者说双向关联)

什么叫绑定,说直白点就是alertIsVisible变量的值和alert提示信息是关联在一起的。当我们把alertIsVisible的值更改为true时,就会自动显示alert提示信息。反过来,当用户关闭了提示信息后,alertIsVisible的值会自动更改为false。

所以alertIsVisible前面的$符号其实就是告诉Swift,这是个绑定型的变量。

2.Alert对象

其中定义了alert提示信息要显示的具体内容。

进一步往下分析,Alert对象由三部分组成:

1.title(标题)

也就是提示信息的标题,比如这里的”你好“。

2.message(信息主体)

也就是提示信息的主要内容,比如这里的”这样的要求不算奇怪“。

3.dismissButton(取消按钮)

提示信息不可能永远显示,用户需要通过某种方式让它消失,所以这里我们创建了一个显示内容为”太棒了!!!“的取消按钮。


好了,要完全看懂上面这些信息,对于新手小白来说还是有些困难的。


不管怎样,还是恭喜你!

因为到目前为止,从某种 意义上来说,你算是真正完成了自己的首个iOS应用!

虽然它很简陋,但是麻雀虽小五脏俱全,有显示有交互。

同时,在我们的待办清单上的第一个任务已经完成:

在界面上放置一个按钮,当用户触碰该按钮时显示一条提示信息。

如果你在学习的过程中遇到困难,可以参考我提示的完整示例代码。

艰难的学习之后,让我们享受一点小小的福利,然后准备开启全新的篇章。

编辑于 05-15

文章被以下专栏收录