iOS Apprentice中文版-从0开始学iOS开发-第五课

我们已经在编程的世界转了一大圈了,开始着手完成这个游戏吧

你已经建造了用户界面,并且你知道如何读取滑条位置上的值。已经搞定了工作清单上的不少项目了。

剩下最大的项目就是生成一个随机值作为目标并且计算玩家的得分。但是首先,需要先对滑条做出一些改进。

输出(outlet)

你设法存储了滑条的值到一个变量里并且在提醒窗口里展示它。这非常不错,但是还需要进行一点改进。

如果你想要给storyboard里的滑条(slider)设置一个新的初始值,不是50的,1-100之间的另一个值会怎么样?接下来currentValue又会出问题因为app认为它开始时总是50。你得记得同时给currentValue一个同样的新的初始值才行。

对我而言,这些小事情根本就记不住,特别是当工程逐渐扩大并且你有十几个视图控制器(view controller)要照顾的时候,或者你几周没有来看这些代码的时候,这种小事特别容易忘掉。

因此,为了一次性彻底解决问题,你要在ViewController.swift里的viewDidLoad()方法里做些工作。这个方法眼下看起来是这个样子的:

override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
    }

当你基于Xcode模版建立这个工程的时候,Xcode早已经在源代码里放置了一个viewDidLoad()方法。你现在需要添加些东西进去。

当view controller一从storyboard文件中读取用户界面时(就是界面已经加载好了,但是还没有展现到屏幕上的前一刻),就会由UIKit向viewDidLoad()发送消息。在这一时刻,用户界面还不可见,所以这是一个理想的地方用于设置变量的初始值。

把viewDidLoad()改成下面这个样子:

override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
        currentValue = lroundf(slider.value)
    }

这个意图是无论你在storyboard里设置滑条的初始值为任何值,都同使用这个值去初始化变量currentValue的值。

回忆一下你需要对这个数取整,因为currentValue是一个Int(整数)型的,整数不能在小数点后有值。

不幸的是,当你做完这一变更运行app时,Xcode开始大发牢骚。自己试一试。

试着运行app

Xcode会在类似于“Error:Use of unresolved identifier ‘slider’”这样一句话后面告诉你“Bulid Failed”

会这样是因为viewDidLoad()并不知道slider这个名字。

但是为什么sliderMoved()就轻松的知道了?让我们再来看一看这个方法:

@IBAction func sliderMoved(_ slider: UISlider) {
        currentValue = lroundf(slider.value)
    }

在这里你做了同样的事情,对slider.value进行取整并且放入currentValue里。所以为什么同样的办法在viewDidLoad()里就不行了呢?

区别在于slider是一个sliderMoved()方法中所谓的参数。参数就是方法名称后括号里面的东西。目前这里仅有一个参数叫做slider,用于指代UISlider对象发送消息的动作方法(Action Method)。

动作方法可以有一个参数用于指代控件(UI Control)触发的方法。这对你在方法中使用这个对象是非常便利的,就像你在sliderMoved()里做的那样(这里谈论的对象就是UISlider)。

当用户移动滑条时,UISlider对象基本上就是在说:“嗨,view controller,我是一个滑条对象,我刚才移动了,顺便说下,这是我的电话号码,你可以用它联系我(这个电话号码就是参数slider)”

这个电话号码(slider)仅仅在sliderMoved()这个特定方法中有效。

换句话说,slider是局部的,你无法在任何其他地方使用它。

局部变量

当我初次介绍变量时,我提到了每个变量都有明确的寿命,也叫作用范围。变量的作用范围依赖于你是在程序的什么位置定义它的。

以下是Swift中的三种作用范围级别:

1、全局作用范围(Global scope),这些对象只要app运行它们就存在,直到用户关闭app,并且可以在任何地方访问。

2、实例作用范围(Instance scope),像currentValue这种变量就是。这些对象只要它们的属主还存在,它们就一直存在。

3、局部作用范围(local scope),具有局部作用的对象,比如sliderMoved()的参数slider,仅在方法方法运行时存在。当程序执行完方法后,它们就不可访问了。

让我们来看看久违的showAlert():

@IBAction func showAlert() {
        let message = "The value of the slider is: \(currentValue)"
        let alert = UIAlertController(title: "Hello World", message: message, preferredStyle: .alert)
        let action = UIAlertAction(title: "OK", style: .default, handler: nil)
......

因为messgae,alert,action都是在方法内部创建的,所以它们是局部的。仅当showAlert()方法被执行时它们才存在并且当方法执行结束后它们就不存在了。

一旦showAlert()执行完毕,其中再没有语句需要执行的时候,电脑就摧毁了message、alert、action。它们的存储空间也不复存在。

而变量currentValue则永远会存在,或者说至少ViewController存在它就存在,直到用户关闭app(因为到目前为止我们的app也只有一个ViewController,所以只要app还在运行,currentValue就存在)。这种类型的变量就叫做实例变量,因为它们的作用范围和其所属对象的实例是一样的。

换句话说,如果你想从一个动作事件到另一个的时候保存某个值,你就要使用实例变量。

解决我们刚才那个报错的方法就是用一个新的实例变量存储slider,就像currentValue那种,除了此时数据类型不是Int(整数)。并且你不能使用标准实例变量,而是用一种比较特殊的,名为输出(outlet)那种。

把下面这行添加到ViewController.swift:

@IBOutlet weak var slider: UISlider!

这一行添加在哪里并不重要,只要是在class ViewController后面的花括号里面就行。我经常把outlets和其他实例变量放在一起。

这一行告诉界面建造器现在你拥有了一个叫做slider的变量,它可以和UISlider对象链接起来。就像界面建造器调用动作方法一样,可以调用这些outlets。除了这些以@IBOutlet开头的东西以外,界面建造器看不到任何其他变量。

目前先不要操心关键字weak和那个感叹号,为什么必须要它们我们会在下一个课程中讲到。目前仅仅记住用于outlet的变量必须用@IBOutlet weak声明并且末尾要有一个感叹号就可以了(有时候会是一个问号,这些把戏都会在合适的时候给大家讲到)。

打开storyboard。按住ctrl并且点击一下滑条,不要拖,点击一下就好(或者可以用鼠标右键点击一下滑条),你会看到弹出一个菜单,上面有所有关于滑条的链接情况。

这个弹出菜单和链接检查器的作用是一样的。我只是想给你展示一下多种方法。

点击New Referencing Outlet傍边的空心圆圈把它拖拽到View Controller(黄色图标的那个)上:

将outlet和slider链接起来

拖到View Controller后会弹出一个小菜单,上面有slider和view,选择slider。

这就是你刚添加到对象上的链接。你已经成功的完成了从storyboard到view controller的滑条输出之间的滑条对象的链接。

现在你已经完成了全部的设置工作,你可以使用slider变量在view controller的内部任何地方引用滑条对象。

有了这些改进,你就可以在界面建造器中任意的设置滑条的初始值了。当app开始运行,currentValue会被设置为相应的值。

运行app并且直接点击Hit Me。它会显示出:“The value of slider is:50”。然后停止app,在界面建造器中将滑条的初始值设定为25,然后再运行app,还是不要动滑条,直接点击Hit Me。弹出窗口上的值现在就会显示为25.

测试结束后,将滑条的初始值还原为50.

练习:把currentValue的值重新设置为0.再运行app,看看会发生什么。你会发现currentValue的初始值已经不重要了,因为viewDidLoad()会把currentValue的值覆盖一遍。但是你不能把这一行删掉,因为swift要求所有变量都必须有一个值,所以就让他初始化为0吧。

注释(Comments)

你应该见过几次那种绿色的以//开头的句子了。这些就是注释语句。你可以在//符号后面写上你想写的任何文字,编译器是完全忽视它们的。

// I am a comment! You can type anything here.
任何位于/*与*/之间的语句也被视为注释。//和/* */的区别是//后面必须是单行,而/* */之间可以有多行。
/*
 I am a comment as well!
 I can span multiple lines
 */

多行注释经常用于失效大段的代码,你在寻找一个bug的时候,经常会把大段的代码先失效掉。

注释最大的用处就是用于解释你的代码工作原理。书写良好的代码是无须加以解释的,但是添加一些注释说明的帮助会非常大。

给谁解释?基本上就是给你自己呀!

除非你的脑容量和大象的体积一样,否则当你六个月以后回来看自己的代码的时候你基本不会记得这是什么。使用注释可以有效节约你的脑容量。

如你所见,Xcode会自动在代码文件的顶部添加版权相关的信息。就问个人而言,我不太关心这些版权说明。如果你不喜欢它们,就赶紧删掉吧。

生成随机数

在这个游戏可以玩之前,你还有一段路要走,所以,让我们开始清单上的下一个项目吧:生成一个随机数,并且把它显示到屏幕上。

在制作游戏时随机数经常出现因为游戏经常需要一些无法预测的场景。你不可能有一台电脑可以生成真实的无法预测的随机数,但是你可以采用一种叫做伪随机数的方法生成一系列数字。你会使用到我最喜欢的这个函数arc4random_uniform()。

最佳的生成随机数的时机是在游戏一开始的时候。

将下面这行代码添加到ViewController.swift的viewDidLoad()中:

tragetValue = 1 + Int(acr4random_uniform(100))

完成后viewDidLoad()应该是这个样子的:

override func viewDidLoad() {
        super.viewDidLoad()
        currentValue = lroundf(slider.value)
        tragetValue = 1 + Int(acr4random_uniform(100))
    }

这里是做了什么?首先,你使用了一个新的变量,tragetValue。你目前还没有实际的定义这个变量,你会在几分钟后去做这件事。

你同时也调用了acr4random_uniform()这个函数用于交付一个1到100之间的随机整数。

实际上,你得到的最大的整数是99,因为acr4random_uniform(100)生成的范围是0到99,为了得到1到100之间的整数,我们需要加上一个1。

你还需要在view controller中声明tragetValue这个变量,否则的话Xcode会向你抱怨它从没见过这个变量。

如果你没有告诉编译器变量targetValue的类型,那么编译器将不知道应该分配多少存储空间给它,也不能核实你是否可以在其他地方使用这个变量。

在ViewController.swift的顶部添加这个变量,和其他变量放在一起:

var tragetValue: Int = 0

Swift中的变量必须有一个值,所以我们给这个变量了一个初始值0。这个0永远不会在这个游戏中被实际使用;它总是会被viewDidLoad()中的随机数覆盖掉。

⚠️:直到你添加上面那条语句为止,Xcode应该会指出它不认识tragetValue这个变量。而你添加了刚才那条语句后,这个报错消息就应该消失了。
Xcode会努力的在分析程序错误这方面提供帮助。有时,在你没有完全敲完代码时,你会看到不断出现和消失的报错信息。
不要被这些报错吓到;它们只是在你尚未彻底输入完毕时短暂的出现一下。

我希望你清楚为什么要把tragetValue做成一个实例变量。

你需要仅在viewDidLoad()这一个地方生成随机数,然后存储下来,直到用户点击Hit Me按钮后,在showAlert()中读取它。

将showAlert()改变成下面这个样子(代码框展现不全可以左右滑动):

@IBAction func showAlert() {
        let message = "The value of the slider is: \(currentValue)" + "\nThe traget value is: \(tragetValue)"
        let alert = ...

小贴士:你在文中任何地方的代码里看到 ... 这种省略号的时候,我是要告诉你从这里开始不要做任何更改。(千万注意不要真的把原来的代码用省略号替换掉了!)

你轻易的添加了一个随机数,并且将它存储在targetValue里,并且在弹出窗口的消息中展现它。\(tragetValue)对你来说应该比较熟悉了,实际的随机数会替换掉它。

\n这个符号是新出现的。它代表换行,就和word里敲一下回车键效果是一样的,把文本弄成两行这样看起来会易读一些。

运行app试试效果吧!

提醒窗口在新的一行显示里targetValue的值

⚠️:早先你用 + 操作符对两个数字进行运算(就和数学里的一样),但是这里的 + 用于将两个字符串合并为一个字符串。
Swift允许同一个操作符进行不同的任务,具体依赖于参加运算的值的类型。如果 + 号两边是两个数值,那么 + 号就是求和运算。但是如果是两个字符串,那么 + 的作用就是把它们链接为一个字符串。

为这个游戏添加上回合机制

如果你点击几次Hit Me按钮,你会发现这个随机数从来没变过。我恐怕这样会严重影响游戏性。

这是因为你在viewDidLoad()中生成了这个随机数后就再也没管过它。viewDidLoad()仅仅是app启动时创建视图的时候被调用一次。

让我们回头看看工作清单,这里明确的写着“在每一回合开始的时候生成一个随机数”。让我们来讲讲对于这个游戏,回合是什么概念。

当游戏刚开始时,玩家的分数为0,并且回合数为1。你把滑条置于中间位置(值为50处)并且生成一个随机数。之后等待玩家点击Hit Me按钮,当玩家这样做了以后,这一回合就结束了。

之后你需要计算玩家的分数,并且把这个分数加到总分里。然后将回合数加1,并且开始新的一个回合。在新的回合里,你需要将滑条的位置复位到中间位置并且生成一个新的随机数,周而复始。

无论何时,当你思考“此时我们应该对这个app添加些什么功能”这类问题时,都应该新建一个方法。这个方法作为一个独立的单元去实现你想要的功能(不要在一个方法里去做两个功能)。

顺着这一思路,在ViewController.swift里添加下面这个新的方法。

   func startNewRound() {
        tragetValue = 1 + Int(arc4random_uniform(100))
        currentValue = 50
        slider.value = Float(currentValue)
    }

这段代码放在文件中的任何位置都可以,但是必须在class ViewController的花括号内部,这样编译器就知道了它是属于ViewController这个对象的。

这和你之前做的事情并没有太大的不同,除了将设置新的一个回合的相关逻辑移到了属于它自己的方法(startNewRound())里。这样做的好处是你可以在其他任何地方随时调用这套逻辑。

首先你可以在ViewDidLoad()调用这个新方法来为最初的一回合进行设置。回忆一下,ViewDidLoad()仅在app启动时被调用一次,所以这里是进行第一回合设置的好地方。

把ViewDidLoad()改成下面这个样子:

override func viewDidLoad() {
        super.viewDidLoad()
        startNewRound()
    }

注意一下,你需要把之前存在语句删除掉,替换为新的这个语句,仅仅调用一下startNewRound()方法。

当用户点击Hit Me按钮后,你也需要调用这个方法,为下一局的开始做好设置,可以通过在showAlert()方法中调用实现这一目的。

把showAlert()改成下面这个样子:

@IBAction func showAlert() {
        . . .
        startNewRound()
    }

目前,view controller中的方法会在某些不同条件下分别被调用起来:viewDidLoad()会在app启动时被执行,showAlert()会在玩家点击Hit Me按钮时被执行,sliderMoved()会在你拖动滑条时被执行,等等。这就是我们之前讨论的所谓的事件驱动模型。

同时你也可以手动调用方法,就是你刚才做的事情。你让对象中的一个方法向同一对象中的另一个方法传递消息。

在这个例子里,为了给新的一回合做配置,view controller自己发送消息给startNewRound()。iPhone会走到这个方法前并且逐条执行其中的语句。当startNewRound()这个方法中的语句全部执行完后,会回到调用它的方法(第一回合是viewDidLoad(),其他回合是showAlert()),继续逐条执行其中剩余的语句。

有时你会看到这种调用方法的语句:

self.startNewRound()

如果把前面的self去掉,其实效果是一样。回忆一下之前我说过的view controller是如何向自己发送消息的。没错,这就是self的意义所在(方法给自己所属的对象发送消息)。

在一个对象上调用一个方法,常规写法是这样的:

receiver.methodName(parameters)

receiver就是接受消息的对象。如果你自己给自己发送消息,那么receiver就是self。但是因为向自己发送消息是非常频繁的一个动作,不管在任何app中,所以可以省略这个关键字。

实话讲,这并不是你第一次调用方法。addAction()是一个属于UIAlertController的方法,present()是所有view controller共有的方法,包括你的。

当你用Swift编程,大部分工作就是在对对象上调用方法,因为这是你app中对象之间的通信方法。

我希望你能明白为什么要把这些逻辑塞到属于它自己的一个方法里去。如果你不这样做,那么viewDidLoad()和showAlert()就会是这个样子的:

override func viewDidLoad() {
  super.viewDidLoad()
  targetValue = 1 + Int(arc4random_uniform(100))
  currentValue = 50
  slider.value = Float(currentValue)
}
@IBAction func showAlert() { ...
  targetValue = 1 + Int(arc4random_uniform(100))
  currentValue = 50
  slider.value = Float(currentValue)
}

看出什么来了吗?实现同样功能的语句被复制了两遍。确实,它们只有三行,并且目前之后两个地方调用,不怎么费事。但是你需要想一下如果是上千行并且在数十个地方调用的时候你怎么办?

并且当你想改变一下这个逻辑的时候,你也不得不在多个地方进行修改,漏掉一个都是灾难。也许在刚开始的时候你还记得每一个地方,但是假如是几周后来改呢?把任何新的功能都建为一个独立的方法,这样不管多少地方调用,你都只修改一个地方,永远不会忘掉。而大量的在不同的地方复制同样的代码,是一个重要的bug来源。

并且方法的名称也可以很清晰的表明这段代码的意图。当你看一眼下面的代码的时候,你能告诉我这是要干什么吗?

targetValue = 1 + Int(arc4random_uniform(100))
currentValue = 50
slider.value = Float(currentValue)

你可能会有自己的思路:“这段代码生成了一个随机数并且重置了滑条的位置,所以它大概应该是新的一回合开始时对数据的设置吧?”
也许你会为这段代码添加注释,以便以后查看,但是有什么能比新建一个方法更清晰明了的呢?

startNewRound()

这一行非常清楚的拼写出了它想干什么。并且假如你想知道开始新的一回合时到底做了什么,你就可以去看看这个方法里具体的内容。

书写良好的代码会自己说话。我希望我已经让你明白了对不同功能独立建一个新的方法的价值。

运行app,确认你每次点击Hit Me按钮时,都生成了一个新的1到100之间的随机数。

你同时也应该注意到,每一回合结束后滑条也会回到中间的位置。这是因为我们在startNewRound()中设置了currentValue等于50并且把告诉滑条回到这个位置。这和我们之前做的相反(之前你是读取滑条的值,并且把这个值存储到currentValue中),因为在每一回合开始时,让滑条回到同样的位置用户体验会好很多。

练习:仅仅是为了好玩,修改下代码,让滑条在每一回合开始不要恢复位置。

顺便说一下,你也许想知道Float(...)和Int(...)这种东西是干吗的:

targetValue = 1 + Int(arc4random_uniform(100))
slider.value = Float(currentValue)

Swift是一种被称为‘强类型(strongly typed)’的语言,这就是说它对能放进容器里的积木形状非常挑剔。例如:如果一个变量是Int(整数)那么你就不能把一个Float(单精度浮点数)的值放进去,反之亦然。

UISlider的值是Float型的,这是一种可以带小数点的数,你之前应该在打印slider的值时见过,但是currentValue是Int(整数)型的,所以下面的语句不会执行:

slider.value = currentValue

编译器会在这里报错。有些编程语言会自动将Int转换为Float,但是Swift要求你明确的确认这种转换。

当你说“Float(currentValue)”时,编译器拿出currentValue中存贮的整数并且将它转换为一个新的Float型的值,这样才能把这个值传递给UISlider。

类似的事情在用arc4random_uniform()时发生过,生产的随机数先被转换为Int(整数),然后才放到变量targetValue里。

因为Swift在值类型上比任何其他语言都严谨,所以很多初学者在这里会有些转不过弯来。不幸的是,Swift的报错信息并不能总是准确的反映这一情况,所以严格的判断每个变量的值类型,就主要靠你自己了。

你可以记住,每当你看到“cannot assign value of type ‘something to type ’ to something else”这种报错信息时,那么就是你试图将不同类型的数据混在一起。解决的办法就是,精确的制定某些值转换为其他类型的,就像我们刚才做的那样。

把targetValue的值放到标签里去

你已经成功的生成了一个随机数,并且将它存储到一个实例变量里去了,这样,之后你就可以读取这个数了,非常好。

现在你将要把这个随机数(也就是游戏中的目标值)显示在屏幕上(游戏界面上,不是提醒窗口中)。如果不这样做,那么玩家就不知道到底目标是多少,游戏也就无法进行下去了。

当你在storyboard中布置界面时,你已经添加了一个标签(Label)用于展示目标值(targetValue),就是右上角的那个。我们要做的就是把targetValue这个变量中的值放到这个标签上去。为了到达这一目的,你需要完成以下两件事:

1、创建属于这个标签(label)的输出(outlet),这样你才可以给它传递消息。

2、把targetValue的值传递给标签(label)的文本,这样标签就可以展示这个值了。

这和我们之前对滑条(slider)做的事情非常相似。回忆一下,你通过添加了一个@IBOutlet变量,用于在view controller中的任何地引用滑条(slider)。使用@IBOutlet变量你可以请求访问滑条的值,通过slider.value语句。你接下来要对标签做的事情,是一样的。

在ViewController.swift中,添加下面的语句,就添加在滑条的@IBOutlet变量下面就好:

@IBOutlet weak var targetLabel: UILabel!

打开Main.storyboard,单击选中右上角的那个标签(就是预先设置为100的那个)。

打开链接检查器,并且将New Referencing Outlet拖拽到View Controller上(下图中黄色标签的那个)

把显示目标值的标签和它的输出链接起来

在弹出的小窗口中选择targetLabel,之后链接就好了。

之后,在startNewRound()方法的下面添加一个新的方法:

func updateLabels() {
        targetLabel.text = String(targetValue)
    }

我们在这里需要新建一个方法是因为你也许会在不同的地方调用它。

方法的名字清楚的表明了它的作用:这个方法是用户更新标签的文本。目前仅仅是更新一个标签的文本,但是之后我们会把其他的标签更新也放到这个方法中来,比如总分(total score),回合数(round number)。

updateLabels()方法中的语句,对你来说应该一点都不陌生,也许你会有一点点疑问,为什么不能简单的写成下面这个样子呢?

targetLabel.text = targetValue

答案是:你不能把一个数据类型放到与之不同的数据类型的变量中去。方形的积木并不适合圆形的孔。

targetLabel的outlet引用了一个UILabel的对象。而UILabel的属性是文本,这种一种String(字符串型)的对象。你仅可以将String型的变量放进文本,但是上面那个例子中,你放进文本的目标targetValue是Int(整数)型的。这样做不会有好结果,因为Int和String是两个不同类型的数据。

所以你必须将Int转换为String,这就是String(targetVlaue)的作用。它和你之前见过的Float()和Int()是一样的。

仅仅是满足你的好奇,你也可以用之前我们用的字符串插值的方法把targetValue传递给targetLabel,像这样:

targetLabel.text = "\(targetValue)"

你更喜欢用那种方法就因人而异了。这两种方法的效果是一样的。

注意一下,updateLabels()是一个标准方法,它不像动作方法(Action Method)那样依附于某个用户控件(UI controls),所以你调用它之前,就这样把它放着就行。(updateLabels() 的前面没有@IBAction,所以它是一个标准方法)。

从游戏的逻辑上讲,我们应该在每一回合开始的时候调用updateLabels(),也就是说updateLabels()应该紧接着startNewRound()方法执行,因为我们必定是在新的一回合开始时,才计算一个新的随机数作为新的目标。

目前,我们在viewDidLoad()和showAlert()这两个地方调用了startNewRound()方法,所以我们也同时需要在这两个地方调用updateLabels()方法:

将viewDidLoad()和showAlert()方法修改为下面这个样子:

override func viewDidLoad() {
        super.viewDidLoad()
        startNewRound()
        updateLabels()
    }
@IBAction func showAlert() {
        . . .
        startNewRound()
        updateLabels()
    }

你可以仅仅敲updateLabels()的前几个字母,比如upd,然后Xcode会自动检测出这个方法,之后你只需要敲一下回车就可以,对于其他的任何方法,变量也都是如此。

Xcode会自动根据输入的首字符匹配相关的项目

运行app并且确认随机数已经作为目标显示在屏幕上了。这应该使游戏的目标变得清晰些了。

右上角的标签已经可以展现我们生成的随机数了

你可以在03-Outlet中找到相关代码。(请支持正版_

动作方法(action method)和标准方法(normal method)

动作方法和标准方法有什么区别呢?

答案是:没有区别。

动作方法和其他任何方法都是一样的。唯一特殊的就是它们有一个特殊的说明符@IBAction。这一标识符允许界面建造器(Interface Bulider)能看到这个方法,这样你才可以把它们和你的buttons(按钮),slider(滑条)、等控件链接起来。

其他方法,比如viewDidLoad(),就没有@IBAtion说明符。这是一个好事,因为所有的故意伤害罪都是发生在企图把这类方法和buttons等控件链接起来,没有@IBAtion说明符就不能链接,没有链接就没有伤害。

下面这些是动作方法(action method)的一般形式:

@IBAction fund showAlert()

你也可以通过一个参数请求相关的对象的动作方法

@IBAction func sliderMoved(_ slider: UISlider)
@IBAction func buttonTapped(_ button: UIButton)

第一个在滑条被拖动时触发,第二个在按钮被点击时触发。

但是下面的方法不能被界面建造器(Interface Bulider)当作动作方法处理:

func updateLabels()

它没有@IBAction的标记,这样界面建造器(Interface Bulider)就看不到它。如果要使用func updateLabels(),你就只能自己调用了。

如果你一直坚持读到了这里,那么我猜,你也许已经喜欢上编程了。

这仅仅是我们4本自学教程中的第一课的一部分。整个巨大的教程还有三本书要学,并且在每节课你都会从设计草图到最终代码完整的做完一个app。

课程的设计是从简单到中级水平安排的。每一个app都要比前面一个更难一些。它们加起来几乎涵盖了全部关于iOS开发的内容,你可以使用这些知识来做属于自己的app。

在本系列的课程结束时,你会学到Swift和iOS开发工具的全部精要内容。更重要的是,你会掌握如何将各种不同的东西组合起来以到达想要的目的,以及像一个专业开发者那样去分析解决问题。

我有充分的信心,当你坚持学完本系列的课程后,你一定有能力将自己的想法转换为实际的app。

到目前为止,也许很多事情你都搞不清楚,但是你已经可以像一个程序员那样独立思考了-并且你要做好进入这个令人激动的iPhone和iPad开发者的世界。

以下是一些我们将要教会你的精华内容:

如何用Swift编程:即使你之前没有任何编程方面的经验,或者说编程对你来说是令人恐惧的。

如何像一个开发者那样思考:你将超过那些仅仅会把代码塞进编辑器的程序猿(原文是code monkey,很形象)。作为一个开发者,你将通过思考,对那些困难的计算问题给出创造性的解决方法。一旦你掌握了这个技术,就没有你不能实现的想法了。

体验SDK:iOS的SDK非常庞大,我们没有任何可能做到面面俱到,幸好我们也不需要这样做。你只需要掌握基本的功能模块,比如navigation controller(导航控制器),table views(表视图)。你同时也会学习如何使用网络服务以及如何开发iPad的app。一旦你理解了这些基本原理,你可以轻易的自己搞定其他所有SDK的原理。

如何把app打扮的更好看:美化app比编程还要重要一些,毕竟这是一个看脸的世界。我们会通过图形和动画技术来更好的设计用户界面。在本节课中,你会对这个游戏app来个大整容,并且使它支持所有类型的iPhone。

最后也是最棒的:我们所有的知识点都基于Swift和iOS的最新版本,比如Auto Layout(自动布局)和Universal Storyboard(通用故事模版)。不会像很多市面上的其他书那样,还在将过时的东西(这一点很重要,凡事学过Swift的同学都应该体会过,旧的版本不是简单的版本过低而已,是根本无法被编译运行了都)。每次新的iOS版本发布,都会对开发工具进行相应的改进,这些将成为你的优势。

我们也不会进行枯燥的理论课程,而是手把手的练习和纠正!当你在实际做app的过程当中,我会解释你做的一切是如何工作的,并且用大量的配图来清晰的展示应该怎么做。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,847评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,208评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,587评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,942评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,332评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,587评论 1 218
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,853评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,568评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,273评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,542评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,033评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,373评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,031评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,073评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,830评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,628评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,537评论 2 269

推荐阅读更多精彩内容