原文链接不知道在哪,
接着上一话来讲,上一话中讲到了MVC,那么MVC在IOS8开发中是如何应用的呢?Paul Hegarty老师给我们展示了一个计算器的Demo,首先新建一个工程,老师把AppDelegate.swift、LaunchScreen.xib和Images.xcassests文件放到了supporting Files文件夹中,那么剩下的两个文件ViewController.swift就是MVC中的C(控制器),Main.storyboard就是MVC中的V(视图)。
在Main.storyboard中我们可以进行页面的布局,完全使用拖拽的方法,而不用通过代码设定控件的位置。
而ViewController的作用就是我们使用它来控制程序,比如点击按钮那么显示的内容会更新,点击运算法会输出结果等等。
当我们的工程变得复杂之后,会有很多的大白块,我们称之为场景(scenes)。一个场景代表一个完整显示的手机屏幕,现在我想要在界面上加一些东西,storyboard的右侧叫Utilities(工具)区域
最下面这部分展示的叫做Object Library(对象库)。我们的计算器显示计数当然不能允许我们进行输入,所以我们需要的是Label,拖拽的时候屏幕上会出现很多蓝字,这些蓝字会帮助我们摆放控件到我们想要的位置。而对象周围的小方块则表示它被选中了:
通过双击可以修改它的初始属性,计算器的显示界面通常初始显示的是0,我们修改为0:
当你无法直接编辑你拖到页面上的对象的内容时,就可以选择用右侧的Utilities Windows(工具窗),我们来仔细看一下窗口中的选项卡:
1.
代表Size Inspector(尺寸检查器),用于设置尺寸
2.
代表Attributies Inspector(属性检查器),非常重要,这是一个面向对象的检查器,当你选中不同的东西时,用户看到的界面也会改变。比如计算器中显示的0,其实应该在Label的右边,数字是从右边出来的,我们可以操作Alignment(校准)
或者是通过font修改它的尺寸:
这就是我们搭建用户界面的方式,非常直观、非常面向对象。
你可以在左上角选择你想要运行的设备,然后运行你的程序:
第一个是真机调试,当然现在还是没有连接真机的状态,下面的设备都是虚拟的,选中设备然后点击Play按钮(就是那个小三角)
我平时喜欢用快捷键commend+R
运行之后发现一个很有意思的问题,你看不到你在storyboard中的那个0,我们把虚拟的iphone6和storyboard中的屏幕左对齐看一下:
0实际上是存在的,但是它跑到了屏幕外面去了!现在我们来解决这个问题,为什么0会跑到屏幕外面,这是因为我们在storyboard中的屏幕是正方形的,没有任何手机的屏幕是正方形的。这个时候我们需要靠约束来解决这个问题,约束的作用是当我们的屏幕被压扁时我们的控件如何处理,不管是水平方向还是垂直方向的压扁。
对于我们的Label,我们希望无论何种尺寸的设备,都能横向填满它的屏幕,所以我们需要的是label的左右边距,按住control键选中label然后拖拽到边界上,会弹出下面的选项:
Center项意思是控件居中,我们当然不希望这样,后面的选项分别是等同于页面的宽度、高度等等,我们希望自己设置一个小边距,所以选择第一项
这个时候出现的黄线和橙线标示我们已经开始遵守这个控件的约束规则了,只不过现在你还没有告诉我足够多的规则,现在提示当页面被挤压的时候我应该怎么做,请告诉我。
我们继续选中左边和上面的约束之后你会发现框的颜色变成蓝色,标示已经设定好了,现在只剩下边距了,但是我们现在不想设定下边距,我们希望下边距根据我下面的内容来调整:
任何时候当你看到黄色线不知道该怎么办的时候,就点击左下方的按钮:
你会看到左侧弹出了一个新的工具栏,这个工具栏叫做Document Outline(文档大纲),它所指示的内容是和页面中的控件互相关联的。值得注意的是它顶部右侧的小黄圈:
任何时候当你在自动布局的时候界面中有黄色或者橙色线的时候你都可以看到这个小黄圈,点击它会触发一个滑动的效果,列出所有的问题:
光标移上去之后会触发右侧的Label被选中,它提示我们选择的字体的高度是38,但是我们刚才拖动了Label得尺寸之后它的高度现在是45,虚线指示我们的label下边框可能想要出现在这里,但是我们手动拖动时会有一两个像素差,这时候点击左侧的黄色小三角,会弹出三个处理方案:
Update Constraints 会强制约束它的高度,我们并不希望这样,所以这个方法很少用。
Reset to Suggested Constraints 在这里也许会起到作用,它会把约束与蓝线重合。
但是这里需要的是Update Frame,把它放到它该放到的位置,它能够让你预览我的约束是否合适,我们选中它,点击Fix Misplacement,果然成功了,左侧的问题列表已经被清空:
返回文档大纲的时候可以看到黄色小圆圈已经不在了。再次运行看看!
哈,0已经出现在了屏幕的右侧。
你可以通过commend+方向键的方式旋转屏幕,可以看到0的位置是自动至于右上角的,证明我们的约束起作用了。那么如何关联代码和试图呢,最简单的方式是使用按钮Assistant Editor:
点击,看起来有点挤,不过好在我们的Naviation和Uitilites都是可以隐藏的,使用右上角的:
viewcontroller中的这部分代码现在是用不到的,可以删掉(一下子清爽很多啊,不愧是老师,说删就删,我自己从来没删过- -b):
我们可以通过拖拽控件到代码中的方法来创建一种类似于指针的功能,让我们代码中获取页面中元素的实例变量:
点击connect,得到一行代码:
@IBOutlet weak var display: UILabel!
这个变量display就是页面上的那个label了,是不是很神奇?我们选择的链接类型是Outlet,这是一个显示的类型,如果要触发动作的话,可以选择Action,很显然我们的数字显示屏只需要一个显示的功能。
@IBOutlet并不是真正的Swift语法,这个是Xcode生成的,代码的左边也会有一个小点:
你把鼠标移上去xcode会选中左侧的label,也就是它所关联的部分。这里你可能不懂weak的意思,如果你是从其他语言过来的,那么你可能接触过垃圾回收的一些知识。在swift中所有的对象都在堆中,所有的类、类的实例都在堆中,swift已经替你进行了内存管理,没有指针指向它们的话你可以自己管理它们,它们会自动清理,但不是垃圾回收,而是引用计数,计算它们被引用的次数,这些都是自动的。首先先忽略这个weak的作用,它是连线自动生成的,你需要知道内存管理是如何发生的,并且实例变量在这里,没有任何的符号去标示这是一个指针,它总是一个指针,因为对象都在堆中存着,我们并不需要多余的*或者&,这些其他语言中用来指示指针的东西。
回到这条语句本身,var定义了一个属性,它使variable的缩写,display是变量名,UILabel是它的类型。
现在我们继续拖出一个按钮到页面上来,它的值改为7,当做计算器键盘上的一个按钮。
调整一下它的字体和尺寸,尺寸可以通过尺寸编辑器来修改,字号定位24号,尺寸64*64。当7被按下的时候我们希望label展示它的值,也就是7,这就是我们的控制器需要来做的。选中按钮按住control拖拽到控制器中然后选择action,outlet对应于实例变量,而action则对应于一个方法,这个方法用来做什么呢?点击7就会在label原有的数字后面附带一个7,所以这是一个append方法,并且我们想要复用这个方法,因为点击任何一个按钮的作用都是一样的,我们取名为appendDigit.在消息发送前我们需要知道点击的是哪一个按钮,好在swift在方法中可以选择是否有参数:
你可以选择没有参数或者sender,sender的意思就是把这个按钮当做参数,参数的默认类型是AnyObject:
这里我们肯定不要AnyObject选择UIButton。如果你忘记选择UIButton,你就悲剧了(说的真对,我悲剧过好几次,错误不好查,运行会直接弹到AppDelegate中),触发事件选择Touch inside up:
意思是你手指点击按钮,手指抬起来的时候还在按钮范围内。当我点击connect时我就得到了这个方法:
@IBAction func appendDigit(sender: UIButton) {
}
和IBOutlet很相似,func是function的缩写,sender是方法的参数,它的类型是UIButton。这个方法没有返回值,如果它有的话语法这样写:
@IBAction func appendDigit(sender: UIButton) -> String{
}
那么触发这个方法有什么用呢,现在我们通过复制粘贴的方法多创建几个按钮,用蓝线将它们互相对齐,你会发现拷贝的时候它们跟方法的关联同样被拷贝了,你可以拷贝够三个,然后选中一排,同时拷贝,这样可以快速构件一个键盘
需要删除按钮,选中直接点击键盘上的Delete就行了。我们拖动键盘想要把它放到label下面,会有蓝线标示放到下面了:
在代码里添加一个方法中的局部变量digit指示被点击的按钮数值:
let digit =
这次我们用的是let而不是var,let定义的是常量,var定义的是变量,let的特性使得代码的可读性得到了很大提高,方法中最好不要塞太多代码,有过多的功能就重新写一个方法(老师教育的是,我发现我经常犯这个错误导致我自己的代码只有自己能看懂)为什么我们不像java中那样通过取名DIGIT的方法来指示这是个常量呢,因为在swift中我们可以快速定义一个变量的位置,不需要通过名字故弄玄虚。
let digit = sender
现在我们需要用我们方法的参数来实现我们的功能了,那么我们如何知道sender中有哪些东西是我们需要的呢,这就需要查看开发文档,xcode中已经完美地嵌入了开发文档,一个好的IOS程序员一定要经常查看开发文档,按住option键然后移动光标到我们想要知道的内容上点击会弹出摘要:
点击最下面的Reference就可以链接到它的说明文档中。跟大多数语言一样,访问一个实体的方法和变量都用“.”来进行。在sender后面加一个.会出现所有它的成员,为什么有这么多,这是因为它继承于一个父类,而它的父类也有继承,这样成员变量就变得很多,好在随着学习深入你会知道自己需要的是哪个,通过输入前几个字母可选的成员会越来越少。我输入cu的时候会剩下一些可选项,可以看到它们的前几个字母都是current,输入tab键会定位到第一个歧义字母(这个非常好用)。现在我们想知道取到的值是什么样子的,在中控台输出它:
let digit = sender.currentTitle
println("digit = \(digit)")
在字符串中加入“\()”swift会对括号中的内容求值然后转换成swift输出。运行一下点击一下按钮5,看看中控台的输出:
等等,Optional(“5”)是什么鬼?原因是我们用let定义digit的时候没有指定它的类型,这是不是说明它是无类型变量?错!Swift是非常非常强类型的语言,所有对象都要有类型,swift有一个强大的功能叫类型推导,它可以通过上下文来推导出类型,现在按住option选中digit看看它的类型,显示它的类型是“String?”
问号的意思就是Optional(可选型),可选型只有两个值,一个叫“not set”(未设),而用nil来表示未设,它就是变量在未设状态下的值,而对应的另一种值就是“有值”,有值的值类型就是?前面的。所以你可以这样想,这个值如果被赋值了,那它就是个String类型的。它是一个Optional类型的,只不过可以被赋值成nil。同样查看currentTitle的简介:
它的值就是String?类型的,后面的get表示这是一个只读的属性,不能写。我们有其他的方法来set这个变量。那么我们如何得到optional中的String呢,我们需要解包这个Optional,这时候我们使用的是“!”。但是如果这些optional的值是nil的时候会发生什么?程序会崩溃掉!
这时候再运行看看:
怎么样?得到了我们想要的String。现在我们想要输出到label中。
display.text = nil
display.text的值也是可选型,所以我们给它赋值nil不会报错。改为
display.text = display.text! + digit
注意display.text后面的!,它本身是可选型,只有String才能用+,所以用!强制拆封。重新运行:
这个0不太好看,我要加一个属性来判断它
swift给我们提示了一个陌生的错误:我们的viewcontroller没有初始化。这是因为在swift中一个类初始化了那么它的所有属性必须是初始化,你不能用一个没有初始化的属性,两种方法,一种使用初始化方法,一种直接赋值,我们首先使用直接赋值来做:
var userIsInTheMiddleOfTypingANumber:Bool = false
全部代码:
import UIKit class ViewController: UIViewController { @IBOutlet weak var display: UILabel!
var userIsInTheMiddleOfTypingANumber:Bool = false
@IBAction func appendDigit(sender: UIButton) { let digit = sender.currentTitle!
println("digit = \(digit)")
if userIsInTheMiddleOfTypingANumber {
display.text = display.text! + digit
} else {
display.text = digit
userIsInTheMiddleOfTypingANumber = true
}
}
}
运行效果:
好的,这一话就到这里,我们下一话继续讲,这一话内容比较丰富,大家好好理解一下