继续上一话中的计算器Demo。上一话讲到类必须被初始化。类中的属性也必须被初始化,所以你不能仅仅声明而不给它一个处置,那么问题来了,我们从storyboard中拖拽的@IBOutlet为什么仅仅有声明而不须要初始化呢,这是由于它的类型依然是一个optional,在你初始化之前已经被赋值为nil了,这也就是为什么你不须要再初始化它的原因。

@IBOutlet weak var display: UILabel!

然而既然它是一个optional类型的,那为什么UILabel后面是“!”而不是“?”呢。对于实际语言而言。它们没有差别,都是可能为一个UILabel的意思。可是实际的使用方法不同,这都是编译器帮你做的,假设我们把后面的感叹号改成问号会怎么样?以下的部分会报错,提示它没有text这个成员:

我们当然能够给display加入一个。来解包

display!.text = digit

由于拖拽生成的原因。左边的xib界面初始化之后和右边的viewcontroller关联,那么这些@IBOutlet就已经被初始化了而且是永久初始化,假设我们每次用的时候都要加一个“!

”。那实在太耽误事了。所以拖拽生成的变量类型后面是自己主动添加的“。”表达了这个变量尽管是一个optional,可是它已经被解包了,我们在使用这个变量的时候就能够不加。

如今我们须要一个returnbutton,用来表示输入完了一个待操作的数,我们复制一个button,改变它的值。

凡是Unicode的字符都能够被我们使用,包含汉字和表情。

把这个button与vc相关联,它不须要传參数,所以參数类型能够是AnyObject的,方法取名enter

 @IBAction func enter() {
}

enter的作用是把我们label现实的数字存入栈中。以待兴许的操作。

代码中该怎样写呢?点击了回车键,那么我们之后的输入将是一个新的数,所以须要把标志位改动:

    @IBAction func enter() {
userIsInTheMiddleOfTypingANumber = false
}

执行看看:

它清除显示屏的功能实现了可是为什么回车会出如今label中呢。相信你已经猜到了由于我们之前复制button的关系。回车键依然关联在appendDigit方法中,我们仅仅须要右键点击button在弹出的菜单中点“X”取消这个关联就OK了:

如今我们创建一个数组来存储运算的值。你能够看到怎样定义以及初始化一个数组

var appendStack:Array<Double> = Array<Double>()

由于Swift语言是强语言类型。所以我们能够不声明类型:

var appendStack = Array<Double>()

你想要在点击回车的时候把当前label的值增加栈中。假设你这样写:

appendStack.append(display.text!)

你会发现报错。由于appendStack是Double类型的,而display.text即便拆封之后依然是个String类型的,那么怎么办呢。

这里要引入一个新的东西,我们叫它计算属性:

  var displayValue:Double {
get{
}
set{
}
}

在定义的时候后面加一对花括号,然后在当中在get和set方法,这个属性的作用是计算display.text的值转成Double,完整的displayValue代码:

var displayValue:Double {
get{
return NSNumberFormatter().numberFromString(display.text!)!.doubleValue
}
set{
display.text = "\(newValue)"
userIsInTheMiddleOfTypingANumber = false
}
}

如今我们在enter方法中做改动:

 @IBAction func enter() {
userIsInTheMiddleOfTypingANumber = false
appendStack.append(displayValue)
println("appendStack = \(appendStack)")
}

执行来试试:

按回车已经不再显示了,中控台信息:

如今我们来添加运算符button。它们依然公用一个action方法:

和回车一样。我们依然能够在特殊符号中找到数学运算符:

拖拽定义一个新方法operate,记得sender类型写UIButton,获取一下button的值:

@IBAction func operate(sender: UIButton) {
let operation = sender.currentTitle! }

接下来展示swift中的操作流,老头说swift的操作流是很强大的,比其它语言强大太多,这一点我深有体会,我敲swift代码已经上万行了。控制流的确很好用和高效。并且很直观。

首先展示一下乘法运算:

 @IBAction func operate(sender: UIButton) {
let operation = sender.currentTitle!
switch operation{
case "×":
if appendStack.count >= 2 {
displayValue = appendStack.removeLast() * appendStack.removeLast()
enter()
}
// case "÷":
// case "+":
// case "−":
default: break
}
}

removeLast()方法会删除数组的最后一个元素。同一时候返回它的值,我们能够执行一下。输入顺序每输入一个操作数,点击一下回车,然后这个数就会被加到栈里,输入两个数之后点击乘号button进行运算。结果例如以下:

switch operation{
case "×":
if appendStack.count >= 2 {
displayValue = appendStack.removeLast() * appendStack.removeLast()
enter()
}
case "÷":
if appendStack.count >= 2 {
displayValue = appendStack.removeLast() * appendStack.removeLast()
enter()
}
case "+":
if appendStack.count >= 2 {
displayValue = appendStack.removeLast() * appendStack.removeLast()
enter()
}
case "−":
if appendStack.count >= 2 {
displayValue = appendStack.removeLast() * appendStack.removeLast()
enter()
}
default: break
}

能够看到数组中保存了最后的运算结果,之后完好后面几个button的代码,你会发现复制过去的时候代码结构是相似的。假设这样写那么显然很不美观,我们须要新建一个方法把这些反复的方法写进去:

    func performOperation(operation:(Double,Double)-> Double)
{
if appendStack.count >= 2 {
displayValue = operation(appendStack.removeLast() , appendStack.removeLast())
enter()
} }

能够看到有些有意思的东西,我们这种方法的參数operation的类型是一个函数,没错,在swift中函数常常作为參数类型被传递。那么在case中我们该怎样写呢,可能会让你大吃一惊:

 case "×":
performOperation({
$0 * $1
})

这里应用了闭包的简化形式,我们无须指定參数类型。由于Swift是强类型的,它能够自己主动识别类型,而假设你没有取參数名的话,那么$0和$1代表前两个參数,闭包中的结构是一个方法,有两个參数。它的返回值是两个參数的乘积,这也符合performOperation的定义。

还有更酷的!假设你有一个类似于performOperation的方法,那么在调用时方法中的最后一个參数能够写到括号外面,比方:

 case "×":
performOperation( ){$0 * $1}

假设前面有其它參数,能够继续写到括号里,假设仅仅有这一个參数,那么圆括号也能够省略了:

performOperation {$0 * $1}

完整的方法代码:

    @IBAction func operate(sender: UIButton) {
let operation = sender.currentTitle!
switch operation{
case "×":
performOperation {$0 * $1} case "÷": performOperation {$1 / $0}
case "+":
performOperation {$0 + $1} case "−":
performOperation {$1 - $0}
default: break
}
}

能够看到在-和/这样的不满足交换律的运算中我们调换了位置,这是希望栈中的最后一个元素总是能够被运算的。你能够回想最早的代码。如今的写法相当的简洁!

如今我们新增一排button,首先是平方根:

我们仅仅须要在控制流中添加一个case就好了,例如以下:

case "√":
performOperation {sqrt($0)}

sqrt是swift自己的求根方法,你会发现报错了。原因是我们之前的performOperation中的闭包都是有两个參数的,而这个case中仅仅有一个參数。那么该怎样改动呢,我们须要复制performOperation的代码然后做例如以下改动:

  func performOperation(operation:Double-> Double)
{
if appendStack.count >= 1 {
displayValue = operation(appendStack.removeLast() )
enter()
} }

你会发现这两个方法的名字全然一样。可是我们不用操心,由于swift会依据參数的不同自己主动调用应该调用的方法,如今我们来试验一下,输入36,点击开方button:

实现了。就是这么简单。

如今还对界面做一些优化,你会发如今竖屏状态屏幕浪费了太多的空间。而你不可能拖动每个button去设置相互之间的间距,这样太浪费时间。

首先左下角有点空。我们新增一个空button来占位。而且保证它不触发不论什么action

我们希望这些button能依据屏幕的大小自己主动稀疏布局,保持button群到各边框的距离相等。我们应用布局button。这排button在屏幕下方:

点开第一个button我们能够看到一些选项:

我们能够选择左对齐、上对齐、居中等等,可是这不是我们想要的选项,我们点开第二个button:

我们选中这两项标示全部button都有同样的宽和高:

如今来设置间距:

还记得我们对齐时候看到的蓝线么,这个8像素点得间距就是蓝线对齐的默认像素点间距,另外要保证红线是亮的状态而不是虚线状态,这样约束才干被加上。加上约束的界面:

哇偶。是不是眼花了。点开视图大纲,能够看到这里的小黄圈,假设它是红色的证明我们加入的约束有冲突的地方。

还记得我们上一话中介绍的么?这里的黄色标示有些约束和我们预想的不同,那么点开黄色小圆圈。随便点击一个条目。作例如以下设置:

记得勾选以下的Apply to all views in container。这样全部的警告都会被施以同样的操作。

如今警告没有了。看起来非常不错呦。执行一下看看:

在Iphone6上显得非常修长。

切换到横屏:

非常赞对不正确?

假设要清除约束使用第三个button:

上面部分是清除所选项的约束。以下部分是清除全部的约束。

可是实际我们的计算器是有问题的。由于我们的处理引擎这部分是独立的,这就牵涉到了MVC设计模式的问题,之后我们全部的代码都要满足MVC设计模式,如今来了解一下MVC:

MVC是一个基本机制,用于分类。左边的Model(模型层)包含我们的模型,在计算当中,计算是模型。

控制层控制视图怎样显示,而视图是控制要用到的模型,在view中用到的时相当常规的界面元素。我们用道路上的指示线来作比:

控制器和模型之间是虚线说明同意暂时跨越。可是过去之前你须要观察一下。控制器(C)必须全然掌握模型(M),由于控制器的职责就是展示模型给用户。所以控制器拥有全然的訪问权限,这是个单向的箭头

相同的控制器(C)也能够到达视图(V),由于控制器要向视图发送命令,由于控制器设计视图,我们在单向箭头上加了一行绿色小字,outlet。由于当我们在控制器中有一个属性指向视图,这个属性的名字就是Outlet。

那么模型(M)和视图(V)之间呢?答案是永远不能!

由于模型是全然独立于UI的。这也是为什么他们之间採用了双黄线隔开。

那么视图(V)向控制器(C)发送信息么?它们之间是一种盲通信。并非随意的通信,他们之间的建立通信的方式是控制器生成一个Target。然后视图使用action向控制器反馈信息。比方我们点击了一个button或者其它页面上的操作。页面并不知道控制器是一个什么样的控制器,它仅仅知道页面上产生了动作。并反馈给控制器,这是一种盲目的、简单的、结构化的通信方式。

那么有一些复杂的操作怎么办呢。比方should、will和did。我拖动屏幕这是一个did的动作,我按住了屏幕准备拖动它的时候这是一个will的动作,遇到这样复杂的动作时怎么办呢。视图的做法是它把这些问题抛给了它的代理,代理依然是控制器中的东西,这些代理来回答will、did、should怎么做这种问题。

另外一个非常重要的点是:视图不应该持有它所展示的数据。数据不能作为它的属性。比方你的iphone中有一万首歌。你不能期望它持有一万首歌展示给你看。第一,这样做非常低效,第二,这一万首歌应该在模型层。一些控制器来负责选择展示哪些歌曲。然后从模型中取到并在视图层中展示。

那么当我们滑动屏幕期望能获取很多其它歌曲的时候我们须要怎么做呢?这是第二种代理,我们叫它数据源DataSource。数据源并不去处理诸如will、should这种处理,他回答有多少歌曲并把数量返回给视图这种工作。此时视图为这一万首歌开辟空间。所以控制器(C)的作用就是给视图(V)解释并格式化这些模型(M)提供的数据。

那么问题又来了,模型能够和控制器通信么?显然不行。可是假设数据改变了怎样通知我们的控制器呢?它依然使用了这样的盲通信的方法。我们把模型想象成一个电台,它通过广播的方式告知别人自己的变化,IOS把这样的技术叫做Notification和Key Value Observing(KVO)。一旦控制器接收到了模型变化的消息,它会通过那个绿箭头向模型索取它的变化信息。

那么视图能接受模型的广播么?或许能够但不要这么做,这违背了MVC模式。

我们能够利用这些知识做些小应用,假设project非常复杂呢?我们须要多个MVC。多个MVC的叠加能够实现复杂功能。

【我们都爱Paul Hegarty】斯坦福IOS8公开课个人笔记3 Xcode、Auto Layout及MVC的更多相关文章

  1. 【我们都爱Paul Hegarty】斯坦福IOS8公开课个人笔记2 Xcode、Auto Layout及MVC

    原文链接不知道在哪, 接着上一话来讲,上一话中讲到了MVC,那么MVC在IOS8开发中是如何应用的呢?Paul Hegarty老师给我们展示了一个计算器的Demo,首先新建一个工程,老师把AppDel ...

  2. 【我们都爱Paul Hegarty】斯坦福IOS8公开课个人笔记1 IOS8概述

    首先感谢网易公开课和SwiftV课堂的朋友们辛苦翻译,这个系列是我学习斯坦福IOS8公开课的个人心得体会和笔记,希望能给大家带来启发. 首先我们要知道IOS系统中的结构情况,从贴近硬件的底层到贴近用户 ...

  3. 【我们都爱Paul Hegarty】斯坦福IOS8公开课个人笔记19 为Demo添加手势

    在这一话中我们将应用上一话学到的知识来为Demo添加手势识别,首先添加一个缩放的功能,其次添加一个拖动功能,使得小人的表情可以随着我们的手指改变. 首先来添加一个缩放手势的识别器,我们来到FaceVi ...

  4. 【我们都爱Paul Hegarty】斯坦福IOS8公开课个人笔记30 ScrollView Demo实战

    在上一话中我们创建了一个通过URL读取图片的Demo,这个Demo是不能拖动和缩放的.如今给它添加选项让它能够手动切换URL,并把图片加入到ScrollView中. 向Storyboard中拖入一个s ...

  5. 【我们都爱Paul Hegarty】斯坦福IOS8公开课个人笔记38 Unwind Segue反向过渡

    之前我们接触过了segue,这是IOS中最主要的传递方式,今天来解说一下怎样进行反向的segue. 反向过渡的使用前提是发出过渡的MVC必须是由目标MVC直接或者间接正向过渡来的.反向过渡是唯一不会创 ...

  6. 【我们都爱Paul Hegarty】斯坦福IOS8公开课个人笔记35 UITextField文本框

    本话来介绍UIKit框架中的组件UITextField. UItextField(文本框)和Label看起来看像,可是文本框是能够编辑的.在UI中使用文本框要注意.由于在模拟器上面输入文字是能够使用电 ...

  7. 【我们都爱Paul Hegarty】斯坦福大学IOS8公开组个人笔记28 ScrollView 幻灯片视图

    随着移动设备,iphone屏幕尺寸的限制.超过内容的屏幕大小为scrollview于,通过滑动来获得.scrollview滑动方向可以是也可以是横向垂直,scrollview可以嵌套,例如,纵向滑动s ...

  8. 斯坦福iOS7公开课11笔记及演示Demo&访问HTTPS链接下载数据

    这一节主要介绍UITableView以及iPad,Demo为从Flicker下载图片并显示,但是实际过程中发现需要FQ并使用HTTPS连接,所以这次用了两个Demo,一个是课程中的Demo,另一个是简 ...

  9. 斯坦福iOS7公开课10笔记及演示Demo

    这一节主要介绍了多线程中的串行队列以及滚动视图UIScrollView. 1 .多线程 这一节只是简单介绍了多线程的串行队列,即把任务加入线程队列后按顺序逐步执行. (1)目前iOS多线程提供的方法主 ...

随机推荐

  1. 高级聚合函数rollup(),cube(),grouping sets()

       rollup(),cube(),grouping sets()   上面这几个函数,是对group by分组功能做的功能扩展. a.rollup()   功能:在原结果基础上追加一行总合计记录 ...

  2. like

    5.在WHERE中使用like做模糊查询    %符号表示0到多个任意字符    _符号表示1个任意字符     //查询名字中含有O字符的员工信息   select empno,ename   fr ...

  3. 2015 Multi-University Training Contest 6 hdu 5358 First One

    First One Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 131072/131072 K (Java/Others)Total ...

  4. WinServer-PowerShell基础

    命令的参数: [-name] 这个参数必须要有,string表示name参数接受什么样的实参,<>表示参数可以接受的实参类型,通常出现set get add都会伴随着必须参数 [-name ...

  5. 【转载】How to Reset USB Device in Linux

    USB devices are anywhere nowadays, even many embedded devices replace the traditional serial devices ...

  6. C/C++拾遗(一):关于数组的指针和数组元素首地址的一道经典题

    代码例如以下: #include <stdio.h> int main(void) { int a[5] = {1, 2, 3, 4, 5}; int *ptr = (int *)(&am ...

  7. 在Redhat Linux中执行非Redhat的Openstack, Redhat将对其Linux不提供支持

    一声叹息. IBM.HP.Dell, Mirantis, Piston等.请问.你们还把你们的Openstack部署在Redhat中吗? http://blogs.wsj.com/digits/201 ...

  8. 【iOS开发-54】案例学习:通过UIScrollView的缩放图片功能练习代理模式的详细实现

    案例:(在模拟器中按住option键,点击鼠标就会出现缩放的手势) (1)在ViewController.m中: --缩放东西是UIScrollView除了滚动之外的还有一个功能,所以须要缩放的东西应 ...

  9. nyoj--1036--非洲小孩(区间相交问题)

    非洲小孩 时间限制:1000 ms  |  内存限制:65535 KB 难度:2 描述 家住非洲的小孩,都很黑.为什么呢? 第一,他们地处热带,太阳辐射严重. 第二,他们不经常洗澡.(常年缺水,怎么洗 ...

  10. 线上服务CPU100%问题快速定位实战--转

    来自微信公众号 架构师之路 功能问题,通过日志,单步调试相对比较好定位. 性能问题,例如线上服务器CPU100%,如何找到相关服务,如何定位问题代码,更考验技术人的功底. 58到家架构部,运维部,58 ...