21.4 表示层开发

从客观上讲,表示层开发的工作量是很大的,工作要做得很细致。

21.4.1 添加图片资源

首先需要把应用中要用的图片等资源添加到工程中。具体步骤如图21-13所示,右键选择Supporting Files组,选择菜单中的Add Files to “Calculator”…弹出选择文件对话框。如图21-14所示,选择我们提供的图片文件夹(images),并在Destination中选中Copy items if needed,这样可以将文件或文件夹复制到我们的工程目录中。在Add to folders中选择Create groups,可以在Xcode中创建images组。然后在Add to targets中选择Calculator。

{%}

图 21-13 添加图片资源文件

{%}

图 21-14 添加文件或文件夹对话框

由于图片资源文件很多,下面我们通过表21-3来解释一下它们的含义。

表21-3 图片资源文件

文件名 说明
0.png~9.png 0~9数字按钮默认状态显示的图片
0-down.png~9-down.png 0~9数字按钮高亮状态显示的图片
c.png 清除按钮默认状态显示的图片
c-down.png 清除按钮高亮状态显示的图片
Decimal.png 小数点按钮默认状态显示的图片
Decimal-down.png 小数点按钮高亮状态显示的图片
Plus.png 加按钮默认状态显示的图片
Plus-down.png 加按钮高亮状态显示的图片
Minus.png 减按钮默认状态显示的图片
Minus-down.png 减按钮高亮状态显示的图片
Multiply.png 乘按钮默认状态显示的图片
Multiply -down.png 乘按钮高亮状态显示的图片
Divide.png 除按钮默认状态显示的图片
Divide-down.png 除按钮高亮状态显示的图片
Equals.png 等号按钮默认状态显示的图片
Equals-down.png 等号按钮高亮状态显示的图片
icon.png 计算器应用图标
Cal.png 主界面背景

21.4.2 改变设计界面大小

在开始界面设计之前,需要改变设计界面的大小。我们预计应用会在iPhone 5及之后的设备上发布,而且是竖屏,因此参考20.2节,通过图21-15所示的步骤改变设计界面大小。

{%}

图 21-15 改变设计界面大小

21.4.3 添加计算器背景

主界面中的计算器背景是一张图片,把图片放到界面中,需要将UIImageView控件添加到设计界面,具体操作可以参考20.2节。拖曳控件UIImageView到设计界面,然后选择属性检查器{%},选择Image View→Image为Cal.png。

为了保证UIImageView控件大小及高宽比与背景图片Cal.png保持一致,需要修改UIImage View尺寸等属性。选中右边的尺寸检查器{%},如图21-16所示的数值,设置坐标X和Y的属性,设置宽和高属性Width和Height。

{%}

图 21-16 修改UIImageView尺寸等属性

提示 原始背景图片大小是640×1136(像素),放到Retina显示屏幕中UIImageView控件大小就是320×568(像素),即高和宽减少了一半。这样设置后我们会发现设计界面下边还有空白(见图21-16),这是因为我们选择的设计界面高度是640点(1280像素),这没有关系,在iPhone 5设备上,下边多出的空白不会显示在屏幕上。

21.4.4 在设计界面中添加主标签

主界面中的主标签是用来显示输入数字和计算结果的控件,我们可以使用UILable控件。我们需要从控件库中拖曳一个Label到设计界面,并拖动它摆放到图21-17所示的位置和大小。如果你觉得拖曳不准确,可以设置它的尺寸属性,尺寸属性值X=0、Y=0、Width=320和Height=119。

{%}

图 21-17 添加Label

摆放好主标签控件的位置后,就需要设置它的文字相关属性。首先是颜色,选择主标签控件,打开属性检查器{%},设置Label→Color属性为白色,如图21-18所示。再设置Label→Alignment属性为右对齐,如图21-19所示。

{%}

图 21-18 设置主标签文字颜色

{%}

图 21-19 设置主标签文字对齐

然后设置文字字体,选择Label→Font属性,如图21-20所示,在弹出的对话框中设置Font为Custom(自定义),Family为Helvetica Neue,Style为Thin,Size为50。

{%}

图 21-20 设置主标签字体

当设置完成这些属性之后,我们需要选择Label标签,拖曳Label标签轮廓填充屏幕的宽度,效果如图21-21所示。

{%}

图 21-21 效果比较

21.4.5 在设计界面中添加按钮

从原型上看,主界面有17个按钮,我们先详细介绍其中一个按钮的设计过程,其他的以此类推。左上角的按钮是7,我们先来设计按钮7。从控件库中拖曳Button控件到设计界面,如图21-22所示。

{%}

图 21-22 添加Button控件

然后我们需要设置按钮的图片,由于按钮有4种状态,分别是Default(默认)状态、Highlighted(高亮)状态、Selected(选择)状态和Disabled(不可用)状态。每种状态都对应一套按钮风格,包括颜色、背景颜色、图片、背景图片以及字体等。状态切换如图21-23所示。

{%}

图 21-23 切换Button状态

在本例中,我们只设置默认状态下Image属性为7-down.png,高亮状态下Image属性为7-down.png。如图21-24所示,选择高亮状态,然后在下面的Image属性下拉列表中选择7-down.png。

{%}

图 21-24 选择特定状态下图片

接下来还需要设置按钮的大小,打开尺寸检查器{%},如图21-25所示,宽和高属性Width和Height设置为80。X和Y属性不需要在此设置。我们需要手动拖曳到左上角,具体位置参考原型设计图21-2。

{%}

图 21-25 设置按钮大小

依次按照按钮7的设计过程,设计其他16个按钮。全部设计完成后,界面如图21-26所示。

{%}

图 21-26 设计完成

为方便程序的计算,我们为主界面中的每一个按钮添加Tag属性。具体过程是选中控件,打开右边的属性检测器,选择View→Tag属性,在这里输入该控件的Tag值,Tag属性默认为0。图21-27所示是设置“0”按钮的Tag属性。按照此方法,参考表21-4,依次设置各个按钮的Tag值。

{%}

图 21-27 设置Tag属性

表21-4 按钮的Tag属性值

按钮 Tag属性值
0~9数字按钮 100~109
小数点按钮 0
清除按钮 0
等于按钮 0
加运算符按钮 200
减运算符按钮 201
乘运算符按钮 202
除运算符按钮 203

21.4.6 控件的输出口和动作

为了将访问控件状态、事件和控件联系到一起,我们引入了输出口和动作的概念。

  1. 输出口

为了能够访问标签等控件,我们需要给标签定义并连接输出口。单击左上角第一组按钮中的“打开辅助编辑器”按钮{%},打开如图21-28所示的界面。

{%}

图 21-28 辅助编辑器

选中标签,同时按住control键,将标签主标签拖曳到图21-29所示的位置。

{%}

图 21-29 拖曳标签Label

释放鼠标,会弹出一个对话框。在Connection栏中选择Outlet,将输出口命名为mainLabel,如图21-30所示。

{%}

图 21-30 设置输出口

单击“Connect”按钮,右边的编辑界面将自动添加如下代码:

  1. @IBOutlet var mainLabel: UILabel

mainLabel被声明为@IBOutlet,这样就为主标签控件定义了输出口变量mainLabel,就可以在程序中访问mainLabel了,mainLabel就是主标签控件的实例。

  1. 动作

为了响应按钮的事件,要把按钮事件与视图控制器中的方法管理起来。为此,需要将响应事件的方法声明为@IBAction。此处列举一个动作的代码:

  1. @IBAction func numberButtonPressed(sender: AnyObject) {
  2. }

参数sender是事件源,也就是发生动作事件的控件,AnyObject是它的类型。

添加动作与输出口类似,打开辅助编辑器界面。选中按钮控件同时按住control键,将按钮控件拖曳到图21-31所示的位置。

{%}

图 21-31 拖曳按钮控件

释放鼠标,会弹出一个如图21-32所示的对话框,在Connection栏中选择Action,Name中输入“numberButtonPressed”,其他选项用默认值就可以。

{%}

图 21-32 定义动作

单击“Connect”按钮,右边的编辑界面将自动添加如下一行代码:

  1. @IBAction func numberButtonPressed(sender: AnyObject) {
  2. }

如果希望多个按钮共用一个方法,可以在拖曳第二个控件时候,直接把它拖曳到辅助编辑器的动作方法上。如图21-33所示,选中按钮控件,同时按住control键,将按钮控件拖曳到辅助编辑器的动作方法上,然后释放鼠标键,这样就可以为多个控件的动作定义一个方法了。类似输出口可以有多个控件对应一个输出口属性,设置方法与动作类似。

{%}

图 21-33 拖曳其他控件到动作方法

按照上面的方法依次为其他16个按钮添加动作,事件触发的方法请参考表21-5,其他的按钮设计这里不再赘述。

表21-5 按钮动作方法

按钮 触发的方法
0~9数字按钮 numberButtonPressed
小数点按钮 decimalPressed
清除按钮 clearPressed
等于按钮 equalsPressed
运算符按钮 operandPressed

21.4.7 视图控制器

下面我们看看视图控制器ViewController.swift的代码,代码如下:

  1. class ViewController: UIViewController {
  2. @IBOutlet var mainLabel: UILabel
  3. var logic : CalcLogic!
  4. override func viewDidLoad() {
  5. super.viewDidLoad()
  6. mainLabel.text = "0"
  7. logic = CalcLogic()
  8. }
  9. override func didReceiveMemoryWarning() {
  10. super.didReceiveMemoryWarning()
  11. logic = nil
  12. }
  13. @IBAction func operandPressed(sender: AnyObject) {
  14. var btn : UIButton = sender as UIButton
  15. mainLabel.text = logic.calculateByTag(btn.tag, withMainLabelString: mainLabel.text)
  16. }
  17. @IBAction func equalsPressed(sender: AnyObject) {
  18. var btn : UIButton = sender as UIButton
  19. mainLabel.text = logic.calculateByTag(btn.tag, withMainLabelString: mainLabel.text)
  20. }
  21. @IBAction func clearPressed(sender: AnyObject) {
  22. mainLabel.text = "0"
  23. logic.clear()
  24. }
  25. @IBAction func decimalPressed(sender: AnyObject) {
  26. if logic.doesStringContainDecimal(mainLabel.text) == false {
  27. let string = mainLabel.text + "."
  28. mainLabel.text = string
  29. }
  30. }
  31. @IBAction func numberButtonPressed(sender: AnyObject) {
  32. var btn : UIButton = sender as UIButton
  33. mainLabel.text = logic.updateMainLabelStringByNumberTag(btn.tag,
  34. withMainLabelString: mainLabel.text!)
  35. }
  36. }

上述代码第①行是声明CalcLogic可变类型变量logic。第②行代码重写UIViewController父类的viewDidLoad()方法。该方法在视图控制器加载时调用,我们在该方法中可以进行一些初始化。类似地,第③行的didReceiveMemoryWarning()方法将在视图控制器卸载或内存报警时调用,我们在该方法中应该释放一些资源。

第④行代码operandPressed方法是在用户点击运算符按钮时调用,第⑤行代码var btn: UIButton = sender as UIButton是将参数sender转换为UIButton类型。转换为UIButton类型后,才能通过访问UIButton的属性和方法,例如tag属性。第⑥行代码是调用业务逻辑层的calculateByTag方法进行计算,返回结果用来更新mainLabel内容。

第⑦行代码在用户点击小数点时调用,在该方法中判断mainLabel标签中是否已经有小数点,如果没有则在mainLabel后面拼接一个小数点。

第⑧行代码是在用户点击了数字按钮时调用的语句,通过调用业务逻辑层的updateMainLabelStringByNumberTag方法,将更新之后的字符串重新设置到控件mainLabel内容里。