我們已經(jīng)在編程的世界轉(zhuǎn)了一大圈了曲初,開始著手完成這個游戲吧
你已經(jīng)建造了用戶界面赦邻,并且你知道如何讀取滑條位置上的值。已經(jīng)搞定了工作清單上的不少項目了喧锦。
剩下最大的項目就是生成一個隨機(jī)值作為目標(biāo)并且計算玩家的得分。但是首先抓督,需要先對滑條做出一些改進(jìn)燃少。
輸出(outlet)
你設(shè)法存儲了滑條的值到一個變量里并且在提醒窗口里展示它。這非常不錯铃在,但是還需要進(jìn)行一點(diǎn)改進(jìn)阵具。
如果你想要給storyboard里的滑條(slider)設(shè)置一個新的初始值碍遍,不是50的,1-100之間的另一個值會怎么樣阳液?接下來currentValue又會出問題因為app認(rèn)為它開始時總是50怕敬。你得記得同時給currentValue一個同樣的新的初始值才行。
對我而言帘皿,這些小事情根本就記不住赖捌,特別是當(dāng)工程逐漸擴(kuò)大并且你有十幾個視圖控制器(view controller)要照顧的時候,或者你幾周沒有來看這些代碼的時候矮烹,這種小事特別容易忘掉。
因此罩锐,為了一次性徹底解決問題奉狈,你要在ViewController.swift里的viewDidLoad()方法里做些工作。這個方法眼下看起來是這個樣子的:
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
當(dāng)你基于Xcode模版建立這個工程的時候涩惑,Xcode早已經(jīng)在源代碼里放置了一個viewDidLoad()方法仁期。你現(xiàn)在需要添加些東西進(jìn)去。
當(dāng)view controller一從storyboard文件中讀取用戶界面時(就是界面已經(jīng)加載好了竭恬,但是還沒有展現(xiàn)到屏幕上的前一刻)跛蛋,就會由UIKit向viewDidLoad()發(fā)送消息。在這一時刻痊硕,用戶界面還不可見赊级,所以這是一個理想的地方用于設(shè)置變量的初始值。
把viewDidLoad()改成下面這個樣子:
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
currentValue = lroundf(slider.value)
}
這個意圖是無論你在storyboard里設(shè)置滑條的初始值為任何值岔绸,都同使用這個值去初始化變量currentValue的值理逊。
回憶一下你需要對這個數(shù)取整,因為currentValue是一個Int(整數(shù))型的盒揉,整數(shù)不能在小數(shù)點(diǎn)后有值晋被。
不幸的是,當(dāng)你做完這一變更運(yùn)行app時刚盈,Xcode開始大發(fā)牢騷羡洛。自己試一試。
試著運(yùn)行app
Xcode會在類似于“Error:Use of unresolved identifier ‘slider’”這樣一句話后面告訴你“Bulid Failed”
會這樣是因為viewDidLoad()并不知道slider這個名字藕漱。
但是為什么sliderMoved()就輕松的知道了欲侮?讓我們再來看一看這個方法:
@IBAction func sliderMoved(_ slider: UISlider) {
currentValue = lroundf(slider.value)
}
在這里你做了同樣的事情,對slider.value進(jìn)行取整并且放入currentValue里谴分。所以為什么同樣的辦法在viewDidLoad()里就不行了呢锈麸?
區(qū)別在于slider是一個sliderMoved()方法中所謂的參數(shù)。參數(shù)就是方法名稱后括號里面的東西牺蹄。目前這里僅有一個參數(shù)叫做slider忘伞,用于指代UISlider對象發(fā)送消息的動作方法(Action Method)。
動作方法可以有一個參數(shù)用于指代控件(UI Control)觸發(fā)的方法。這對你在方法中使用這個對象是非常便利的氓奈,就像你在sliderMoved()里做的那樣(這里談?wù)摰膶ο缶褪荱ISlider)翘魄。
當(dāng)用戶移動滑條時,UISlider對象基本上就是在說:“嗨舀奶,view controller暑竟,我是一個滑條對象,我剛才移動了育勺,順便說下但荤,這是我的電話號碼,你可以用它聯(lián)系我(這個電話號碼就是參數(shù)slider)”
這個電話號碼(slider)僅僅在sliderMoved()這個特定方法中有效涧至。
換句話說腹躁,slider是局部的,你無法在任何其他地方使用它南蓬。
局部變量
當(dāng)我初次介紹變量時纺非,我提到了每個變量都有明確的壽命,也叫作用范圍赘方。變量的作用范圍依賴于你是在程序的什么位置定義它的烧颖。
以下是Swift中的三種作用范圍級別:
1、全局作用范圍(Global scope)窄陡,這些對象只要app運(yùn)行它們就存在炕淮,直到用戶關(guān)閉app,并且可以在任何地方訪問泳梆。
2鳖悠、實例作用范圍(Instance scope),像currentValue這種變量就是优妙。這些對象只要它們的屬主還存在乘综,它們就一直存在。
3套硼、局部作用范圍(local scope)卡辰,具有局部作用的對象,比如sliderMoved()的參數(shù)slider邪意,僅在方法方法運(yùn)行時存在九妈。當(dāng)程序執(zhí)行完方法后,它們就不可訪問了雾鬼。
讓我們來看看久違的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都是在方法內(nèi)部創(chuàng)建的策菜,所以它們是局部的晶疼。僅當(dāng)showAlert()方法被執(zhí)行時它們才存在并且當(dāng)方法執(zhí)行結(jié)束后它們就不存在了酒贬。
一旦showAlert()執(zhí)行完畢,其中再沒有語句需要執(zhí)行的時候翠霍,電腦就摧毀了message锭吨、alert、action寒匙。它們的存儲空間也不復(fù)存在零如。
而變量currentValue則永遠(yuǎn)會存在,或者說至少ViewController存在它就存在锄弱,直到用戶關(guān)閉app(因為到目前為止我們的app也只有一個ViewController考蕾,所以只要app還在運(yùn)行,currentValue就存在)会宪。這種類型的變量就叫做實例變量辕翰,因為它們的作用范圍和其所屬對象的實例是一樣的。
換句話說狈谊,如果你想從一個動作事件到另一個的時候保存某個值,你就要使用實例變量沟沙。
解決我們剛才那個報錯的方法就是用一個新的實例變量存儲slider河劝,就像currentValue那種,除了此時數(shù)據(jù)類型不是Int(整數(shù))矛紫。并且你不能使用標(biāo)準(zhǔn)實例變量赎瞎,而是用一種比較特殊的,名為輸出(outlet)那種颊咬。
把下面這行添加到ViewController.swift:
@IBOutlet weak var slider: UISlider!
這一行添加在哪里并不重要务甥,只要是在class ViewController后面的花括號里面就行。我經(jīng)常把outlets和其他實例變量放在一起喳篇。
這一行告訴界面建造器現(xiàn)在你擁有了一個叫做slider的變量敞临,它可以和UISlider對象鏈接起來。就像界面建造器調(diào)用動作方法一樣,可以調(diào)用這些outlets。除了這些以@IBOutlet開頭的東西以外葡缰,界面建造器看不到任何其他變量寄啼。
目前先不要操心關(guān)鍵字weak和那個感嘆號,為什么必須要它們我們會在下一個課程中講到庐椒。目前僅僅記住用于outlet的變量必須用@IBOutlet weak聲明并且末尾要有一個感嘆號就可以了(有時候會是一個問號,這些把戲都會在合適的時候給大家講到)。
打開storyboard窄俏。按住ctrl并且點(diǎn)擊一下滑條,不要拖碘菜,點(diǎn)擊一下就好(或者可以用鼠標(biāo)右鍵點(diǎn)擊一下滑條)凹蜈,你會看到彈出一個菜單限寞,上面有所有關(guān)于滑條的鏈接情況。
這個彈出菜單和鏈接檢查器的作用是一樣的踪区。我只是想給你展示一下多種方法昆烁。
點(diǎn)擊New Referencing Outlet傍邊的空心圓圈把它拖拽到View Controller(黃色圖標(biāo)的那個)上:
拖到View Controller后會彈出一個小菜單,上面有slider和view缎岗,選擇slider静尼。
這就是你剛添加到對象上的鏈接。你已經(jīng)成功的完成了從storyboard到view controller的滑條輸出之間的滑條對象的鏈接传泊。
現(xiàn)在你已經(jīng)完成了全部的設(shè)置工作鼠渺,你可以使用slider變量在view controller的內(nèi)部任何地方引用滑條對象。
有了這些改進(jìn)眷细,你就可以在界面建造器中任意的設(shè)置滑條的初始值了拦盹。當(dāng)app開始運(yùn)行,currentValue會被設(shè)置為相應(yīng)的值溪椎。
運(yùn)行app并且直接點(diǎn)擊Hit Me普舆。它會顯示出:“The value of slider is:50”。然后停止app校读,在界面建造器中將滑條的初始值設(shè)定為25沼侣,然后再運(yùn)行app,還是不要動滑條歉秫,直接點(diǎn)擊Hit Me蛾洛。彈出窗口上的值現(xiàn)在就會顯示為25.
測試結(jié)束后,將滑條的初始值還原為50.
練習(xí):把currentValue的值重新設(shè)置為0.再運(yùn)行app雁芙,看看會發(fā)生什么轧膘。你會發(fā)現(xiàn)currentValue的初始值已經(jīng)不重要了,因為viewDidLoad()會把currentValue的值覆蓋一遍兔甘。但是你不能把這一行刪掉谎碍,因為swift要求所有變量都必須有一個值,所以就讓他初始化為0吧洞焙。
注釋(Comments)
你應(yīng)該見過幾次那種綠色的以//開頭的句子了椿浓。這些就是注釋語句。你可以在//符號后面寫上你想寫的任何文字闽晦,編譯器是完全忽視它們的扳碍。
// I am a comment! You can type anything here.
任何位于/*與*/之間的語句也被視為注釋。//和/* */的區(qū)別是//后面必須是單行仙蛉,而/* */之間可以有多行笋敞。
/*
I am a comment as well!
I can span multiple lines
*/
多行注釋經(jīng)常用于失效大段的代碼,你在尋找一個bug的時候荠瘪,經(jīng)常會把大段的代碼先失效掉夯巷。
注釋最大的用處就是用于解釋你的代碼工作原理赛惩。書寫良好的代碼是無須加以解釋的,但是添加一些注釋說明的幫助會非常大趁餐。
給誰解釋喷兼?基本上就是給你自己呀!
除非你的腦容量和大象的體積一樣后雷,否則當(dāng)你六個月以后回來看自己的代碼的時候你基本不會記得這是什么季惯。使用注釋可以有效節(jié)約你的腦容量。
如你所見臀突,Xcode會自動在代碼文件的頂部添加版權(quán)相關(guān)的信息勉抓。就問個人而言,我不太關(guān)心這些版權(quán)說明候学。如果你不喜歡它們藕筋,就趕緊刪掉吧。
生成隨機(jī)數(shù)
在這個游戲可以玩之前梳码,你還有一段路要走隐圾,所以,讓我們開始清單上的下一個項目吧:生成一個隨機(jī)數(shù)掰茶,并且把它顯示到屏幕上翎承。
在制作游戲時隨機(jī)數(shù)經(jīng)常出現(xiàn)因為游戲經(jīng)常需要一些無法預(yù)測的場景。你不可能有一臺電腦可以生成真實的無法預(yù)測的隨機(jī)數(shù)符匾,但是你可以采用一種叫做偽隨機(jī)數(shù)的方法生成一系列數(shù)字。你會使用到我最喜歡的這個函數(shù)arc4random_uniform()瘩例。
最佳的生成隨機(jī)數(shù)的時機(jī)是在游戲一開始的時候啊胶。
將下面這行代碼添加到ViewController.swift的viewDidLoad()中:
tragetValue = 1 + Int(acr4random_uniform(100))
完成后viewDidLoad()應(yīng)該是這個樣子的:
override func viewDidLoad() {
super.viewDidLoad()
currentValue = lroundf(slider.value)
tragetValue = 1 + Int(acr4random_uniform(100))
}
這里是做了什么?首先垛贤,你使用了一個新的變量焰坪,tragetValue。你目前還沒有實際的定義這個變量聘惦,你會在幾分鐘后去做這件事某饰。
你同時也調(diào)用了acr4random_uniform()這個函數(shù)用于交付一個1到100之間的隨機(jī)整數(shù)。
實際上善绎,你得到的最大的整數(shù)是99黔漂,因為acr4random_uniform(100)生成的范圍是0到99,為了得到1到100之間的整數(shù)禀酱,我們需要加上一個1炬守。
你還需要在view controller中聲明tragetValue這個變量,否則的話Xcode會向你抱怨它從沒見過這個變量剂跟。
如果你沒有告訴編譯器變量targetValue的類型减途,那么編譯器將不知道應(yīng)該分配多少存儲空間給它酣藻,也不能核實你是否可以在其他地方使用這個變量。
在ViewController.swift的頂部添加這個變量鳍置,和其他變量放在一起:
var tragetValue: Int = 0
Swift中的變量必須有一個值辽剧,所以我們給這個變量了一個初始值0。這個0永遠(yuǎn)不會在這個游戲中被實際使用税产;它總是會被viewDidLoad()中的隨機(jī)數(shù)覆蓋掉怕轿。
??:直到你添加上面那條語句為止,Xcode應(yīng)該會指出它不認(rèn)識tragetValue這個變量砖第。而你添加了剛才那條語句后撤卢,這個報錯消息就應(yīng)該消失了。
Xcode會努力的在分析程序錯誤這方面提供幫助梧兼。有時放吩,在你沒有完全敲完代碼時,你會看到不斷出現(xiàn)和消失的報錯信息羽杰。
不要被這些報錯嚇到渡紫;它們只是在你尚未徹底輸入完畢時短暫的出現(xiàn)一下。
我希望你清楚為什么要把tragetValue做成一個實例變量考赛。
你需要僅在viewDidLoad()這一個地方生成隨機(jī)數(shù)惕澎,然后存儲下來,直到用戶點(diǎn)擊Hit Me按鈕后颜骤,在showAlert()中讀取它唧喉。
將showAlert()改變成下面這個樣子(代碼框展現(xiàn)不全可以左右滑動):
@IBAction func showAlert() {
let message = "The value of the slider is: \(currentValue)" + "\nThe traget value is: \(tragetValue)"
let alert = ...
小貼士:你在文中任何地方的代碼里看到 ... 這種省略號的時候,我是要告訴你從這里開始不要做任何更改忍抽。(千萬注意不要真的把原來的代碼用省略號替換掉了0诵ⅰ)
你輕易的添加了一個隨機(jī)數(shù),并且將它存儲在targetValue里鸠项,并且在彈出窗口的消息中展現(xiàn)它干跛。\(tragetValue)對你來說應(yīng)該比較熟悉了,實際的隨機(jī)數(shù)會替換掉它祟绊。
\n這個符號是新出現(xiàn)的楼入。它代表換行,就和word里敲一下回車鍵效果是一樣的牧抽,把文本弄成兩行這樣看起來會易讀一些嘉熊。
運(yùn)行app試試效果吧!
??:早先你用 + 操作符對兩個數(shù)字進(jìn)行運(yùn)算(就和數(shù)學(xué)里的一樣)扬舒,但是這里的 + 用于將兩個字符串合并為一個字符串记舆。
Swift允許同一個操作符進(jìn)行不同的任務(wù),具體依賴于參加運(yùn)算的值的類型呼巴。如果 + 號兩邊是兩個數(shù)值泽腮,那么 + 號就是求和運(yùn)算御蒲。但是如果是兩個字符串,那么 + 的作用就是把它們鏈接為一個字符串诊赊。
為這個游戲添加上回合機(jī)制
如果你點(diǎn)擊幾次Hit Me按鈕厚满,你會發(fā)現(xiàn)這個隨機(jī)數(shù)從來沒變過。我恐怕這樣會嚴(yán)重影響游戲性碧磅。
這是因為你在viewDidLoad()中生成了這個隨機(jī)數(shù)后就再也沒管過它碘箍。viewDidLoad()僅僅是app啟動時創(chuàng)建視圖的時候被調(diào)用一次。
讓我們回頭看看工作清單鲸郊,這里明確的寫著“在每一回合開始的時候生成一個隨機(jī)數(shù)”丰榴。讓我們來講講對于這個游戲,回合是什么概念秆撮。
當(dāng)游戲剛開始時四濒,玩家的分?jǐn)?shù)為0,并且回合數(shù)為1职辨。你把滑條置于中間位置(值為50處)并且生成一個隨機(jī)數(shù)盗蟆。之后等待玩家點(diǎn)擊Hit Me按鈕,當(dāng)玩家這樣做了以后舒裤,這一回合就結(jié)束了喳资。
之后你需要計算玩家的分?jǐn)?shù),并且把這個分?jǐn)?shù)加到總分里腾供。然后將回合數(shù)加1仆邓,并且開始新的一個回合。在新的回合里伴鳖,你需要將滑條的位置復(fù)位到中間位置并且生成一個新的隨機(jī)數(shù)节值,周而復(fù)始。
無論何時黎侈,當(dāng)你思考“此時我們應(yīng)該對這個app添加些什么功能”這類問題時,都應(yīng)該新建一個方法闷游。這個方法作為一個獨(dú)立的單元去實現(xiàn)你想要的功能(不要在一個方法里去做兩個功能)峻汉。
順著這一思路,在ViewController.swift里添加下面這個新的方法脐往。
func startNewRound() {
tragetValue = 1 + Int(arc4random_uniform(100))
currentValue = 50
slider.value = Float(currentValue)
}
這段代碼放在文件中的任何位置都可以休吠,但是必須在class ViewController的花括號內(nèi)部,這樣編譯器就知道了它是屬于ViewController這個對象的业簿。
這和你之前做的事情并沒有太大的不同瘤礁,除了將設(shè)置新的一個回合的相關(guān)邏輯移到了屬于它自己的方法(startNewRound())里。這樣做的好處是你可以在其他任何地方隨時調(diào)用這套邏輯梅尤。
首先你可以在ViewDidLoad()調(diào)用這個新方法來為最初的一回合進(jìn)行設(shè)置柜思⊙业鳎回憶一下,ViewDidLoad()僅在app啟動時被調(diào)用一次赡盘,所以這里是進(jìn)行第一回合設(shè)置的好地方号枕。
把ViewDidLoad()改成下面這個樣子:
override func viewDidLoad() {
super.viewDidLoad()
startNewRound()
}
注意一下,你需要把之前存在語句刪除掉陨享,替換為新的這個語句葱淳,僅僅調(diào)用一下startNewRound()方法。
當(dāng)用戶點(diǎn)擊Hit Me按鈕后抛姑,你也需要調(diào)用這個方法赞厕,為下一局的開始做好設(shè)置,可以通過在showAlert()方法中調(diào)用實現(xiàn)這一目的定硝。
把showAlert()改成下面這個樣子:
@IBAction func showAlert() {
. . .
startNewRound()
}
目前皿桑,view controller中的方法會在某些不同條件下分別被調(diào)用起來:viewDidLoad()會在app啟動時被執(zhí)行,showAlert()會在玩家點(diǎn)擊Hit Me按鈕時被執(zhí)行喷斋,sliderMoved()會在你拖動滑條時被執(zhí)行唁毒,等等。這就是我們之前討論的所謂的事件驅(qū)動模型星爪。
同時你也可以手動調(diào)用方法浆西,就是你剛才做的事情。你讓對象中的一個方法向同一對象中的另一個方法傳遞消息顽腾。
在這個例子里近零,為了給新的一回合做配置,view controller自己發(fā)送消息給startNewRound()抄肖。iPhone會走到這個方法前并且逐條執(zhí)行其中的語句久信。當(dāng)startNewRound()這個方法中的語句全部執(zhí)行完后,會回到調(diào)用它的方法(第一回合是viewDidLoad()漓摩,其他回合是showAlert())裙士,繼續(xù)逐條執(zhí)行其中剩余的語句。
有時你會看到這種調(diào)用方法的語句:
self.startNewRound()
如果把前面的self去掉管毙,其實效果是一樣腿椎。回憶一下之前我說過的view controller是如何向自己發(fā)送消息的夭咬。沒錯啃炸,這就是self的意義所在(方法給自己所屬的對象發(fā)送消息)。
在一個對象上調(diào)用一個方法卓舵,常規(guī)寫法是這樣的:
receiver.methodName(parameters)
receiver就是接受消息的對象南用。如果你自己給自己發(fā)送消息,那么receiver就是self。但是因為向自己發(fā)送消息是非常頻繁的一個動作裹虫,不管在任何app中肿嘲,所以可以省略這個關(guān)鍵字。
實話講恒界,這并不是你第一次調(diào)用方法睦刃。addAction()是一個屬于UIAlertController的方法,present()是所有view controller共有的方法十酣,包括你的涩拙。
當(dāng)你用Swift編程,大部分工作就是在對對象上調(diào)用方法耸采,因為這是你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)
}
看出什么來了嗎搓彻?實現(xiàn)同樣功能的語句被復(fù)制了兩遍。確實嘱朽,它們只有三行旭贬,并且目前之后兩個地方調(diào)用,不怎么費(fèi)事搪泳。但是你需要想一下如果是上千行并且在數(shù)十個地方調(diào)用的時候你怎么辦稀轨?
并且當(dāng)你想改變一下這個邏輯的時候,你也不得不在多個地方進(jìn)行修改岸军,漏掉一個都是災(zāi)難奋刽。也許在剛開始的時候你還記得每一個地方,但是假如是幾周后來改呢艰赞?把任何新的功能都建為一個獨(dú)立的方法佣谐,這樣不管多少地方調(diào)用,你都只修改一個地方方妖,永遠(yuǎn)不會忘掉狭魂。而大量的在不同的地方復(fù)制同樣的代碼,是一個重要的bug來源党觅。
并且方法的名稱也可以很清晰的表明這段代碼的意圖雌澄。當(dāng)你看一眼下面的代碼的時候,你能告訴我這是要干什么嗎仔役?
targetValue = 1 + Int(arc4random_uniform(100))
currentValue = 50
slider.value = Float(currentValue)
你可能會有自己的思路:“這段代碼生成了一個隨機(jī)數(shù)并且重置了滑條的位置掷伙,所以它大概應(yīng)該是新的一回合開始時對數(shù)據(jù)的設(shè)置吧是己?”
也許你會為這段代碼添加注釋又兵,以便以后查看,但是有什么能比新建一個方法更清晰明了的呢?
startNewRound()
這一行非常清楚的拼寫出了它想干什么沛厨。并且假如你想知道開始新的一回合時到底做了什么宙地,你就可以去看看這個方法里具體的內(nèi)容。
書寫良好的代碼會自己說話逆皮。我希望我已經(jīng)讓你明白了對不同功能獨(dú)立建一個新的方法的價值宅粥。
運(yùn)行app,確認(rèn)你每次點(diǎn)擊Hit Me按鈕時电谣,都生成了一個新的1到100之間的隨機(jī)數(shù)秽梅。
你同時也應(yīng)該注意到,每一回合結(jié)束后滑條也會回到中間的位置剿牺。這是因為我們在startNewRound()中設(shè)置了currentValue等于50并且把告訴滑條回到這個位置企垦。這和我們之前做的相反(之前你是讀取滑條的值,并且把這個值存儲到currentValue中)晒来,因為在每一回合開始時钞诡,讓滑條回到同樣的位置用戶體驗會好很多。
練習(xí):僅僅是為了好玩湃崩,修改下代碼荧降,讓滑條在每一回合開始不要恢復(fù)位置。
順便說一下攒读,你也許想知道Float(...)和Int(...)這種東西是干嗎的:
targetValue = 1 + Int(arc4random_uniform(100))
slider.value = Float(currentValue)
Swift是一種被稱為‘強(qiáng)類型(strongly typed)’的語言朵诫,這就是說它對能放進(jìn)容器里的積木形狀非常挑剔。例如:如果一個變量是Int(整數(shù))那么你就不能把一個Float(單精度浮點(diǎn)數(shù))的值放進(jìn)去整陌,反之亦然拗窃。
UISlider的值是Float型的,這是一種可以帶小數(shù)點(diǎn)的數(shù)泌辫,你之前應(yīng)該在打印slider的值時見過随夸,但是currentValue是Int(整數(shù))型的,所以下面的語句不會執(zhí)行:
slider.value = currentValue
編譯器會在這里報錯震放。有些編程語言會自動將Int轉(zhuǎn)換為Float宾毒,但是Swift要求你明確的確認(rèn)這種轉(zhuǎn)換。
當(dāng)你說“Float(currentValue)”時殿遂,編譯器拿出currentValue中存貯的整數(shù)并且將它轉(zhuǎn)換為一個新的Float型的值诈铛,這樣才能把這個值傳遞給UISlider。
類似的事情在用arc4random_uniform()時發(fā)生過墨礁,生產(chǎn)的隨機(jī)數(shù)先被轉(zhuǎn)換為Int(整數(shù))幢竹,然后才放到變量targetValue里。
因為Swift在值類型上比任何其他語言都嚴(yán)謹(jǐn)恩静,所以很多初學(xué)者在這里會有些轉(zhuǎn)不過彎來焕毫。不幸的是蹲坷,Swift的報錯信息并不能總是準(zhǔn)確的反映這一情況,所以嚴(yán)格的判斷每個變量的值類型邑飒,就主要靠你自己了循签。
你可以記住,每當(dāng)你看到“cannot assign value of type ‘something to type ’ to something else”這種報錯信息時疙咸,那么就是你試圖將不同類型的數(shù)據(jù)混在一起县匠。解決的辦法就是,精確的制定某些值轉(zhuǎn)換為其他類型的撒轮,就像我們剛才做的那樣乞旦。
把targetValue的值放到標(biāo)簽里去
你已經(jīng)成功的生成了一個隨機(jī)數(shù),并且將它存儲到一個實例變量里去了题山,這樣杆查,之后你就可以讀取這個數(shù)了,非常好臀蛛。
現(xiàn)在你將要把這個隨機(jī)數(shù)(也就是游戲中的目標(biāo)值)顯示在屏幕上(游戲界面上亲桦,不是提醒窗口中)。如果不這樣做浊仆,那么玩家就不知道到底目標(biāo)是多少客峭,游戲也就無法進(jìn)行下去了。
當(dāng)你在storyboard中布置界面時抡柿,你已經(jīng)添加了一個標(biāo)簽(Label)用于展示目標(biāo)值(targetValue)舔琅,就是右上角的那個。我們要做的就是把targetValue這個變量中的值放到這個標(biāo)簽上去洲劣。為了到達(dá)這一目的备蚓,你需要完成以下兩件事:
1、創(chuàng)建屬于這個標(biāo)簽(label)的輸出(outlet)囱稽,這樣你才可以給它傳遞消息郊尝。
2、把targetValue的值傳遞給標(biāo)簽(label)的文本战惊,這樣標(biāo)簽就可以展示這個值了流昏。
這和我們之前對滑條(slider)做的事情非常相似⊥袒瘢回憶一下况凉,你通過添加了一個@IBOutlet變量,用于在view controller中的任何地引用滑條(slider)各拷。使用@IBOutlet變量你可以請求訪問滑條的值刁绒,通過slider.value語句。你接下來要對標(biāo)簽做的事情烤黍,是一樣的知市。
在ViewController.swift中粮坞,添加下面的語句,就添加在滑條的@IBOutlet變量下面就好:
@IBOutlet weak var targetLabel: UILabel!
打開Main.storyboard初狰,單擊選中右上角的那個標(biāo)簽(就是預(yù)先設(shè)置為100的那個)。
打開鏈接檢查器互例,并且將New Referencing Outlet拖拽到View Controller上(下圖中黃色標(biāo)簽的那個)
在彈出的小窗口中選擇targetLabel奢入,之后鏈接就好了。
之后媳叨,在startNewRound()方法的下面添加一個新的方法:
func updateLabels() {
targetLabel.text = String(targetValue)
}
我們在這里需要新建一個方法是因為你也許會在不同的地方調(diào)用它腥光。
方法的名字清楚的表明了它的作用:這個方法是用戶更新標(biāo)簽的文本。目前僅僅是更新一個標(biāo)簽的文本糊秆,但是之后我們會把其他的標(biāo)簽更新也放到這個方法中來武福,比如總分(total score),回合數(shù)(round number)痘番。
updateLabels()方法中的語句捉片,對你來說應(yīng)該一點(diǎn)都不陌生,也許你會有一點(diǎn)點(diǎn)疑問汞舱,為什么不能簡單的寫成下面這個樣子呢伍纫?
targetLabel.text = targetValue
答案是:你不能把一個數(shù)據(jù)類型放到與之不同的數(shù)據(jù)類型的變量中去。方形的積木并不適合圓形的孔昂芜。
targetLabel的outlet引用了一個UILabel的對象莹规。而UILabel的屬性是文本,這種一種String(字符串型)的對象泌神。你僅可以將String型的變量放進(jìn)文本良漱,但是上面那個例子中,你放進(jìn)文本的目標(biāo)targetValue是Int(整數(shù))型的欢际。這樣做不會有好結(jié)果母市,因為Int和String是兩個不同類型的數(shù)據(jù)。
所以你必須將Int轉(zhuǎn)換為String损趋,這就是String(targetVlaue)的作用窒篱。它和你之前見過的Float()和Int()是一樣的。
僅僅是滿足你的好奇舶沿,你也可以用之前我們用的字符串插值的方法把targetValue傳遞給targetLabel墙杯,像這樣:
targetLabel.text = "\(targetValue)"
你更喜歡用那種方法就因人而異了。這兩種方法的效果是一樣的括荡。
注意一下高镐,updateLabels()是一個標(biāo)準(zhǔn)方法,它不像動作方法(Action Method)那樣依附于某個用戶控件(UI controls)畸冲,所以你調(diào)用它之前嫉髓,就這樣把它放著就行观腊。(updateLabels() 的前面沒有@IBAction,所以它是一個標(biāo)準(zhǔn)方法)算行。
從游戲的邏輯上講梧油,我們應(yīng)該在每一回合開始的時候調(diào)用updateLabels(),也就是說updateLabels()應(yīng)該緊接著startNewRound()方法執(zhí)行州邢,因為我們必定是在新的一回合開始時儡陨,才計算一個新的隨機(jī)數(shù)作為新的目標(biāo)。
目前量淌,我們在viewDidLoad()和showAlert()這兩個地方調(diào)用了startNewRound()方法骗村,所以我們也同時需要在這兩個地方調(diào)用updateLabels()方法:
將viewDidLoad()和showAlert()方法修改為下面這個樣子:
override func viewDidLoad() {
super.viewDidLoad()
startNewRound()
updateLabels()
}
@IBAction func showAlert() {
. . .
startNewRound()
updateLabels()
}
你可以僅僅敲updateLabels()的前幾個字母,比如upd呀枢,然后Xcode會自動檢測出這個方法胚股,之后你只需要敲一下回車就可以,對于其他的任何方法裙秋,變量也都是如此琅拌。
運(yùn)行app并且確認(rèn)隨機(jī)數(shù)已經(jīng)作為目標(biāo)顯示在屏幕上了。這應(yīng)該使游戲的目標(biāo)變得清晰些了摘刑。
你可以在03-Outlet中找到相關(guān)代碼财忽。(請支持正版_)
動作方法(action method)和標(biāo)準(zhǔn)方法(normal method)
動作方法和標(biāo)準(zhǔn)方法有什么區(qū)別呢?
答案是:沒有區(qū)別泣侮。
動作方法和其他任何方法都是一樣的即彪。唯一特殊的就是它們有一個特殊的說明符@IBAction。這一標(biāo)識符允許界面建造器(Interface Bulider)能看到這個方法活尊,這樣你才可以把它們和你的buttons(按鈕)隶校,slider(滑條)、等控件鏈接起來蛹锰。
其他方法深胳,比如viewDidLoad(),就沒有@IBAtion說明符铜犬。這是一個好事舞终,因為所有的故意傷害罪都是發(fā)生在企圖把這類方法和buttons等控件鏈接起來,沒有@IBAtion說明符就不能鏈接癣猾,沒有鏈接就沒有傷害敛劝。
下面這些是動作方法(action method)的一般形式:
@IBAction fund showAlert()
你也可以通過一個參數(shù)請求相關(guān)的對象的動作方法
@IBAction func sliderMoved(_ slider: UISlider)
@IBAction func buttonTapped(_ button: UIButton)
第一個在滑條被拖動時觸發(fā),第二個在按鈕被點(diǎn)擊時觸發(fā)纷宇。
但是下面的方法不能被界面建造器(Interface Bulider)當(dāng)作動作方法處理:
func updateLabels()
它沒有@IBAction的標(biāo)記夸盟,這樣界面建造器(Interface Bulider)就看不到它。如果要使用func updateLabels()像捶,你就只能自己調(diào)用了上陕。
如果你一直堅持讀到了這里桩砰,那么我猜,你也許已經(jīng)喜歡上編程了释簿。
這僅僅是我們4本自學(xué)教程中的第一課的一部分亚隅。整個巨大的教程還有三本書要學(xué),并且在每節(jié)課你都會從設(shè)計草圖到最終代碼完整的做完一個app庶溶。
課程的設(shè)計是從簡單到中級水平安排的煮纵。每一個app都要比前面一個更難一些。它們加起來幾乎涵蓋了全部關(guān)于iOS開發(fā)的內(nèi)容渐尿,你可以使用這些知識來做屬于自己的app。
在本系列的課程結(jié)束時矾瑰,你會學(xué)到Swift和iOS開發(fā)工具的全部精要內(nèi)容砖茸。更重要的是,你會掌握如何將各種不同的東西組合起來以到達(dá)想要的目的殴穴,以及像一個專業(yè)開發(fā)者那樣去分析解決問題凉夯。
我有充分的信心,當(dāng)你堅持學(xué)完本系列的課程后采幌,你一定有能力將自己的想法轉(zhuǎn)換為實際的app劲够。
到目前為止,也許很多事情你都搞不清楚休傍,但是你已經(jīng)可以像一個程序員那樣獨(dú)立思考了-并且你要做好進(jìn)入這個令人激動的iPhone和iPad開發(fā)者的世界征绎。
以下是一些我們將要教會你的精華內(nèi)容:
如何用Swift編程:即使你之前沒有任何編程方面的經(jīng)驗,或者說編程對你來說是令人恐懼的磨取。
如何像一個開發(fā)者那樣思考:你將超過那些僅僅會把代碼塞進(jìn)編輯器的程序猿(原文是code monkey人柿,很形象)。作為一個開發(fā)者忙厌,你將通過思考凫岖,對那些困難的計算問題給出創(chuàng)造性的解決方法。一旦你掌握了這個技術(shù)逢净,就沒有你不能實現(xiàn)的想法了哥放。
體驗SDK:iOS的SDK非常龐大,我們沒有任何可能做到面面俱到爹土,幸好我們也不需要這樣做甥雕。你只需要掌握基本的功能模塊,比如navigation controller(導(dǎo)航控制器)胀茵,table views(表視圖)犀农。你同時也會學(xué)習(xí)如何使用網(wǎng)絡(luò)服務(wù)以及如何開發(fā)iPad的app。一旦你理解了這些基本原理宰掉,你可以輕易的自己搞定其他所有SDK的原理呵哨。
如何把a(bǔ)pp打扮的更好看:美化app比編程還要重要一些赁濒,畢竟這是一個看臉的世界。我們會通過圖形和動畫技術(shù)來更好的設(shè)計用戶界面孟害。在本節(jié)課中拒炎,你會對這個游戲app來個大整容,并且使它支持所有類型的iPhone挨务。
最后也是最棒的:我們所有的知識點(diǎn)都基于Swift和iOS的最新版本击你,比如Auto Layout(自動布局)和Universal Storyboard(通用故事模版)。不會像很多市面上的其他書那樣谎柄,還在將過時的東西(這一點(diǎn)很重要丁侄,凡事學(xué)過Swift的同學(xué)都應(yīng)該體會過,舊的版本不是簡單的版本過低而已朝巫,是根本無法被編譯運(yùn)行了都)鸿摇。每次新的iOS版本發(fā)布,都會對開發(fā)工具進(jìn)行相應(yīng)的改進(jìn)劈猿,這些將成為你的優(yōu)勢拙吉。
我們也不會進(jìn)行枯燥的理論課程,而是手把手的練習(xí)和糾正揪荣!當(dāng)你在實際做app的過程當(dāng)中筷黔,我會解釋你做的一切是如何工作的,并且用大量的配圖來清晰的展示應(yīng)該怎么做仗颈。