本节书摘来自华章计算机《Swift iOS应用开发实战》一书中的第2章,第2.5节,作者:刘铭 著, 更多章节内容可以访问云栖社区“华章计算机”公众号查看。
2.5与代码进行关联
通过前面的学习,我们已经知道如何在故事板中创建用户界面,但在搭建好用户界面以后又要做什么呢?接下来就需要将控件对象和程序代码关联起来。我们在创建Calculator项目的时候使用了默认的Single View Controller模板,该模板关联了故事板中的View Controller场景和ViewController.swift文件中的ViewController类。现在我们在故事板中查验一下。
在故事板中选中View Controller场景,使用Command+Option+3快捷键切换出标识检视窗(Identity Inspector),在Custom Class部分中,查看Class是否被设置为ViewController。
Class中所设置的ViewController指的就是项目中的View-Controller类(ViewController.swift文件中),ViewController类是一个控制器类,它包含一个View属性(指针),该属性指向故事板中的ViewController视图,也就是说ViewController类控制着这个视图。但是,这个控制器类还不能操作刚刚在这个视图中所添加的那些可视化控件,接下来就需要建立控制器和视图之间的关联。
2.5.1要完成的效果
本章的实践中,我们需要通过UIButton输入操作数和运算符,最后还要将计算结果显示到UILabel对象上,如图2-17所示。
2.5.2理解Outlet和Actions
在前面的实战练习中,我们使用Interface Builder设计了一个简单的计算器界面,并且还在Identifier检视窗中看到了所对应的ViewController.swift类。那么问题就是,我们通过什么方法能够让ViewController类中的代码与故事板中的用户界面元素进行交互呢?
方法肯定是有的,一个控制器类中的属性(实例变量)可以通过引用的方式与故事板或者nib文件中的视图对象建立联系,只要我们在类中声明属性的时候加上关键字IBOutlet。我们可以把Outlet想象成一个指向用户界面对象的指针。例如在Calculator项目中,我们创建了一个Label对象,当进行计算时,需要这个Label显示用户输入的数据以及计算的结果,所以通过Outlet就可以在代码中更新Label的显示内容。
与Outlet相反,当触发故事板或者nib文件中的界面对象时,需要执行指定类中的方法,这个指定的方法就是Action方法。例如Calculator项目中的数字按钮,当用户点击这些按钮的时候就要执行Action方法。我们甚至可以设置成当用户点击按钮以后执行某个方法,或者是当用户点击按钮后在手指离开的时候执行某个方法。
我们可以先在模拟器中查看用户界面的搭建效果。
步骤1点击Xcode工具栏中左侧的“Run”按钮,当编译成功以后,启动iOS模拟器并自动运行Calculator项目(设备建议选择iPhone 5或iPhone 5s)。此时,点击计算器中任何按钮的时候,只会看到按钮有被点击的效果反应,程序方面并不会执行什么。
当用户点击计算器按钮的时候,视图就会触发一个动作(Action),动作会以指定的方法形式呈现在ViewController类中。通过执行该方法,我们可以将运算结果反馈到故事板的Label中。所谓的反馈,实际上就是通过代码修改故事板中Label对象的属性,因此需要建立Outlet关联。
我们可以通过助手编辑器为ViewController类添加1个Outlet和17个Action。
在项目导航中选择Main.storyboard文件,从菜单中选择“View→Assistant Editor→ Show Assistant Editor”打开助手编辑器。
如果在故事板里面选中了某个视图场景,那么当我们打开助手编辑器后,会自动显示与场景对应的视图控制器类文件。比如,在当前Main.storyboard中选中的是ViewController场景,则切换到助手编辑器模式的时候,会自动打开ViewController.swift文件。如果没有打开该文件,则可以通过编辑器顶部的工具栏选择相应的文件,如图2-18所示。
接下来,我们开始创建ViewController类与故事板中Label的Outlet关联。
步骤2确定ViewController.swift文件显示在助手编辑窗口中。
步骤3故事板中,在Label控件上按住鼠标右键,将其拖曳至右侧窗口中ViewController.swift文件的@interface命令的下方,如图2-19所示。
松开鼠标以后会弹出关联设置面板,如图2-20所示。将Connection设置为Outlet,将Name设置为labelResult,Type和Storage不用修改,设置好后点击“Connect”按钮。
在Outlet关联构建好以后,ViewController类中会新增加一行@IBOutlet声明:
class ViewController: UIViewController {
@IBOutlet weak var labelResult: UILabel!
……
在这个例子中,我们创建了一个叫labelResult的Outlet,这个指针指向故事板中ViewController场景里面的Label对象。
在编译项目代码时,Swift编译器不会对@IBOutlet声明做任何特别的事情。@IBOutlet唯一的目的就是告诉Xcode这个属性会连接故事板或nib文件中的一个视图对象。也就是说,任何想要指向故事板或nib文件的属性必须被声明为@IBOutlet。
Action与Outlet正好相反,它代表一个动作,当用户在控件对象上进行交互操作并需要反馈给ViewController时,就会调用这个动作所关联的程序代码。当用户点击场景中按钮时,就会触发一个事件,这个事件会调用我们在类中定义的Action方法。
建立Action关联的方法与Outlet稍微有些区别,接下来我们为故事板中的UIButton与ViewController类建立Action关联。
步骤4在IBOutlet的下面添加5个实例变量的定义:
class ViewController: UIViewController {
@IBOutlet weak var labelResult: UILabel!
var firstOperand: Double = 0.0 // 第一操作数
var secondOperand: Double = 0.0 // 第二操作数
var decimalPointFlag: Bool = false // 标记是否输入了小数点
var isSecond: Bool = false // 是否输入第二操作数
var operatorFlag: String = "" // 操作符
步骤5故事板中,在按钮为0的Button控件上按住鼠标右键,然后将其拖曳至助手编辑器窗口中ViewController类中的didReceiveMemoryWarning方法的下面,如图2-21所示。
松开鼠标以后,在弹出的关联设置面板中,将Connection设置为Action,将Name设置为buttonTap,将Type设置为UIButton,将Event设置为Touch Up Inside,Arguments设置为Sender,如图2-22所示。设置好以后点击“Connect”按钮。
在默认情况下,关联设置面板中的Connection是Outlet,在选择Action以后面板会发生一些变化。Name代表所定义的Action方法的名称;Type代表该方法会携带一个参数,这个参数的类型是UIButton(传递给buttonTap方法的参数就是故事板中指向UIButton对象的指针变量),所以这里的Type我们设置为UIButton类型。Event代表用户所触发的事件,当用户点击按钮后并离开的时候会触发buttonTap方法;Arguments代表buttonTap方法所携带的参数形式,它分为3种:不带参数、带一个参数(Sender,表示用户在故事板中所交互的那个对象)、带两个参数(Sender和Event,除了所交互的视图对象外还有所触发的事件)。
在关联设置面板中点开Event列表后,会发现很多不同的事件,我们可以根据需要选择。
在创建好Action以后,ViewController.swift文件应该如下面这样:
import UIKit
class ViewController: UIViewController {
@IBOutlet weak var labelResult: UILabel!
……
@IBAction func buttonTap(sender: UIButton) {
}
}
步骤6以此类推,将1~9按钮与buttonTap方法建立IBAction关联。因为用户按0~9这10个按钮调用的都是buttonTap方法,所以我们不用为接下来的9个再单独创建新的方法。在1按钮上按住鼠标右键,拖曳至buttonTap方法上,如图2-23所示,再将2~9按钮照此方法操作。
步骤7在编辑器的右侧窗口中编辑ViewController.swift文件,找到buttonTap方法并在该方法中添加下面的代码:
@IBAction func buttonTap(sender: UIButton) {
// labelResult中默认是0,如果开始输入数字,则先清除0
if labelResult.text == "0" || (isSecond && secondOperand == 0.0) {
labelResult.text = ""
}
// 将用户录入的数添加到labelResult中
//labelResult.text = labelResult.text + sender.titleLabel
if let value = sender.titleLabel?.text {
labelResult.text = labelResult.text! + value
}
if isSecond {
secondOperand = (labelResult.text! as NSString).doubleValue
}else {
// 将labelResult中的字符串转化为单精度数
firstOperand = (labelResult.text! as NSString).doubleValue
}
// 在控制台中输出转换完的数值
println(firstOperand)
构建并运行应用程序,当我们按下数字键以后,数值就会同时显示在Label和控制台中。接下来,我们还要处理有关小数点的问题。
步骤8将故事板中的小数点按钮与ViewController类建立IBAction关联。在小数点按钮上按住鼠标右键,拖曳至buttonTap方法的下面,在弹出的面板中Connection设置为Action,Name设置为decimalPointTap,Event设置为Touch Up Inside,Arguments设置为None。
通过IBAction关联,当我们点击小数点按钮以后,系统就会调用decimalPointTap方法,而且我们也不需要所传递的参数信息,所以将Arguments设置为None。
步骤9在ViewController.swift文件中添加下面关于小数点的程序代码:
class ViewController: UIViewController {
@IBOutlet weak var labelResult: UILabel!
……
if isSecond {
secondOperand = (labelResult.text! as NSString).doubleValue
}else {
firstOperand = (labelResult.text! as NSString).doubleValue
}
decimalPointFlag = !decimalPointFlag
}
}
}
构建并运行应用程序,此时可以正常输入带小数部分的数字。在第3章中我们还会继续完善Calculator项目,将添加更多的IBAction方法。
2.5.3使用快速检查器查看关联
如果我们在建立关联时出现了错误,比如关联了错误的Outlet变量,为错误的事件指定了一个Action方法等。要想查看某场景所有的关联信息,可以在故事板大纲视图的ViewController图标上点击鼠标右键调出快速检查器,如图2-24所示。
在快速检查器中,我们可以点击已关联好的对象前面的“X”移除关联,也可以点击后面的圆圈建立新的关联。点击检查器左上角的“X”可以关闭整个快速检查器。
除了通过快速检查器检查关联以外,在代码文件中也可以查看是否成功关联。
1)在项目导航中选择ViewController.swift文件。
2)在IBOutlet和IBAction的声明语句之前看到实心的圆点,代表已经成功建立关联,如图4-31所示。如果看到的是空心圆点,则代表没有建立关联。
在故事板中成功建立关联以后,如果此时想要在代码中删除Outlet变量或Action方法,还需要在故事板中删除建立的关联,否则会导致程序崩溃退出。