不显示调用super_让不懂编程的人爱上iPhone开发(2017秋iOS11+Swift4+Xcode9版)-第7篇
歡迎回來繼續我們的學習。
現在我們的to-do 清單上還有不少事情要做,接下來先處理一個比較簡單的:產生一個隨機數,然后讓它顯示在屏幕上。
在游戲開發的時候,我們經常會用到隨機數。當然,大家最感興趣的隨機數可能就是體彩雙色球了,可惜的是彩票里面的所謂隨機數未必隨機啊,主任才是決定一切的力量~
自買彩票十幾年以來,哥中的最大獎是210元。
當時就開始各種幻想,以為千萬頭獎近在咫尺,從此走上人生巔峰,可誰知道從此好運遠離,連中個5元都得一等再等。
算了還是別做夢了,來說說游戲里面的隨機數吧。
在此之前提一句,如果哪位哥中了頭獎,別忘了留個聯系方式,土豪我們做個朋友吧!
實際上,我們不可能讓一個計算機真正產生完全隨機不可預測的數字,而使用所謂的偽隨機數(pseudo-random)生成器來產生貌似是隨機的效果。這里我們將用到一個比較常用的,arc4random_uniform()函數。
你應該還記得函數和方法的區別吧?不怕麻煩再重復一遍,函數不屬于某個對象,而方法則必須寄生于某個對象。
在產生隨機數之前,首先我們需要定義一個變量來保存該隨機數。
在Xcode中打開ViewController.swift,在currentValue這個變量的定義語句之后添加一行代碼:
如果我們沒有告訴編譯器targetValue是怎樣的一種變量,那么它就不知道該為targetValue分配多少存儲空間,更不會知道我們是否正確的使用了該變量。
此外,Swift中的變量必須有一個數值,因此我們賦予其一個初始值0.當然,0這個數值在實際的游戲中并不會用到,我們會使用所產生的隨機數來替代它。
還記得之前提過的變量類型嗎?這里我們所定義的targetValue就是一個實例變量。之所以將其定義為一個實例變量,是因為我們需要在多處用到它,比如在viewDidLoad()中,同時在用戶觸碰按鈕之前記住該隨機數,以及在showAlert()方法將數值和用戶所選擇的數值進行比較預算。
接下來我們就需要生成這個隨機數了。
在我們這個游戲里,最適合生成隨機數的地方當然就是游戲開始處。
在Xcode中打開ViewController.swift,然后在viewDidLoad()方法中添加下面的這行代碼:
targetValue = 1+ Int(arc4random_uniform(100))此時viewDidLoad()方法的完整代碼如下:
override func viewDidLoad() { super.viewDidLoad() currentValue = lroundf(slider.value) targetValue = 1 + Int(arc4random_uniform(100)) }這里我們調用了arc4random_uniform函數來產生一個隨機整數,并將targetValue的最終數值范圍限定在1到100之間。
實際上我們通過arc4random_uniform函數能得到的最大數字是99,因為arc4random_uniform()會排除超過上界的數值。這里我們設置的上界是100,因此我們得到的隨機數在0-99之間(包含99但不包含100)。為了得到1-100之間的數字,我們需要在arc4random_uniform()的結果上加上1。
希望你別被上面的數學運算嚇倒。
雖然很多人說只有學好數學才能成為一個好的程序猿,但實際上絕大多數應用中所用到的數學都不會比上面的算術復雜。當然,在游戲和科學計算中用到的數學要稍微復雜一些。不過你的目的并不是成為一個算法高手或者數學家,所以也不必太擔心。
接下來,讓我們更改showAlert()的方法如下:
@IBAction func showAlert(){ let mesage = "滑動條的當前數值是:(currentValue)" + "n目標數值是: (targetValue)" let alert = UIAlertController(title:"Hello Messi", message:mesage, preferredStyle: .alert) let action = UIAlertAction(title:"ok",style: .default,handler: nil) alert.addAction(action) present(alert, animated: true, completion: nil) }后面就不重復提醒大家了,黃色高亮部分代表修改或新增的代碼。
在上面的代碼中,我們向字符串中添加了存儲在targetValue中的隨機數,這個大家應該不再陌生了。(targetValue)將被真實的隨機數替代。
n這個符號之前沒接觸過。實際上它在字符串中出現的時候,就意味著我們要換行了。
好了,點擊Run運行游戲,試試看有什么變化。
注意:之前我們曾經使用+這個符號來將兩個數字相加(和大家在小學數學里面學到的那樣),但這里我們則使用+來將文本粘合到一起形成一個長的字符串。
在編程語言中,相同的符號有時代表著不同的作用,而具體的含義由上下文決定(跟人類語言一樣)。
添加新的游戲回合
如果你多試幾次,會發現對話框所提示的隨機數目標數值幾乎就沒改變過。這樣的話這游戲也就沒啥意思了。
還好意思說自己是隨機數,一次都不變。這就好比雙色球和大樂透的每期中獎號碼都是重復的,還有誰愿意買?(我!!!)
這是因為我們在viewDidLoad()方法里面生成了隨機數,而之后就沒有再次生成。而這個方法只會在應用第一次打開創建視圖控制器的時候才會調用,之后就沒它的事了。
我們的to-do清單上寫的是,”在游戲的每個回合開始的時候生成一個隨機數“。好吧,這里的回合指的是什么呢?
當游戲開始的時候,玩家的初始分數是0,游戲回合數是1。我們把滑動條設置在中間的位置(50),然后計算一個隨機數。接著我們等玩家觸碰按鈕。當玩家觸碰按鈕之后,一輪游戲就結束了。
這個時候我們會計算這一輪的得分,然后把它添加到總得分里面去。接著我們讓游戲回合數加1,然后開啟新的一輪游戲。我們再次把滑動條重置到中間的位置,然后計算一個新的隨機數。就這樣,只要你愿意,直到地老天荒,直到宇宙的終結(實際上直到你的手機沒電為止~)。
每當你發現自己在想,”在新一輪的游戲開始后我們要做這個,做那個。。。“,這個時候就該創建一個新的方法了。這個方法會創建自己的獨有功能。
記住這一點,讓我們繼續前進吧 。
回到Xcode,切換到ViewController.swift,添加一個新的方法。
具體放的位置不是很重要,只要它是在class ViewController的花括號里面,這樣編譯器就知道它屬于ViewController對象了。
func startNewRound() { targetValue = 1 + Int (arc4random_uniform(100)) currentValue = 50 slider.value = Float(currentValue) }這里和我們之前所做的事情沒有太大區別,只不過我們把開始新一輪游戲的邏輯機制放到了一個獨立的方法startNewRound()里面去。這樣做的好處是,我們可以在后面重復使用這部分代碼的邏輯。
然后更改viewDidLoad()方法的代碼如下:
override func viewDidLoad() { super.viewDidLoad() startNewRound() }注意我們已經把viewDidLoad()方法中原有的相關代碼刪除,并使用對startNewRound()方法的調用來代替。
當玩家觸碰按鈕顯示提示對話框后,可以在showAlert()方法里面調用這個方法來開啟新的一輪游戲。
讓我們更改showAlert()方法里面的代碼:
@IBAction func showAlert(){ let mesage = "滑動條的當前數值是:(currentValue)" + "n目標數值是: (targetValue)" let alert = UIAlertController(title:"Hello Messi", message:mesage, preferredStyle: .alert) let action = UIAlertAction(title:"ok",style: .default,handler: nil) alert.addAction(action) present(alert, animated: true, completion: nil)startNewRound() }這里添加的唯一一行新代碼就是最后的startNewRound()
在此之前,我們所接觸到的視圖控制器里面的方法都是在某個事件發生的時候調用:比如當應用加載視圖控制器的時候調用viewDidLoad()方法,當玩家觸碰按鈕的時候調用showAlert()方法,當玩家拖動滑動條的時候調用sliderMoved()方法。這就是我們之前談到的事件驅動模型。
以上的方法調用都是自動的,而這里開啟新的一輪游戲則是手動調用方法的例子。我們從對象中的某個方法給相同對象的另一個對象發送了一條消息,聽起來似乎有點拗口。
秘密:在iOS開發里面,發送消息和調用方法其實是一個意思~
比如在這里,視圖控制器給自己發送一條startNewRound()的消息以開啟新的一輪游戲。
然后iPhone就會切換到新的方法,并從頭到尾執行里面的語句。當方法里面的語句執行完畢后,就會返回之前的方法,繼續其中的代碼。可能是第一次調用時的viewDidLoad()方法,也可能是此后每一個回合的showAlert()方法。
有時你可能看到類似下面的方法調用方式:
self.startNewRound()這個是之前不帶self.的方法調用作用相同。還記得剛才我提到視圖控制器給自己發送消息嗎?其實這就是self的意思。
在Swift中,當我們需要對某個對象調用方法時,通常這樣來寫:
receiver.methodName(parameters)這里的receiver就是我們要發送消息的接收對象。如果我們要給自己發送消息,receiver就是self。不過因為給self發送消息是非常常見的事情,所以大部分時候這個關鍵詞可以省略掉。
不過實際上這并非我們首次調用方法。之前的addAction()方法是UIAlertController,而present()方法則是所有視圖控制器都會有的,包括你自己的視圖控制器。
當我們使用Swift編程的時候,大部分時候都是讓對象調用方法,因為這就是我們應用中對象的通訊方式。
這里要說明一點,把開啟新一輪游戲的邏輯機制放到一個獨立的方法里面是很好的編碼習慣。如果我們不這樣做,你就會看到下面的代碼:
override func viewDidLoad() { super.viewDidLoad() targetValue = 1 + Int((arc4random()_uniform(100)) currentValue = 50 slider.value = Float(currentValue) }@IBAction func showAlert() { . . .presentViewController(alert,animated:true,completion:nil) targetValue = 1 + Int(arc4random_uniform(100)) currentValue = 50 slider.value = Float(currentValue) }這樣做有什么不好呢?首先,我們在兩個不同的地方重新寫了相同的代碼。好吧,這里只是3行代碼,如果是300行怎么辦?如果我們打算更改這部分邏輯又該怎么辦?如果我們要在10個不同的地方用這部分代碼怎么辦?
還有,你剛開始寫這段代碼的時候非常清楚它是干嗎用的。但1周以后呢?1個月以后呢?半年以后呢?你還記得這樣的代碼究竟是干嘛的嗎?假如幾個月后你發現要修改其中的一兩行,但又忘了其它地方的類似代碼,你就完蛋了。代碼重復是產品中出現bug的主要來源之一。
只要你的代碼需要在兩個不同的地方完成相同的事情,你就需要考慮創建一個新的方法。相信我,別偷懶。偷懶一時爽,代碼死光光~
還有,方法的名稱也有助于我們了解它的確切功能。如果你沒看過前面的教程,我直接給你下面的三行代碼:
targetValue = 1 + Int((arc4random()_uniform(100)) currentValue = 50 slider.value = Float(currentValue)你能知道這幾行代碼究竟是干嗎用的嗎?當然,你可能了解這里用隨機數創建了一個數值,然后設置了一個初始值,再把初始值賦予了滑動條。
僅此而已,你能知道這其實是在開啟新一輪的游戲嗎?不管你能不能,我反正是不知道。有些程序猿會加上一些注釋代碼,但最好的方法還是學學哥:
startNewRound()清清楚楚明明白白真真切切,比注釋行還要清晰明了。雖然注釋行也很有用,但讓代碼具有自解釋性更重要。
而且在后面你可以隨時查看和修改startNewRound()方法,更不用說在Xcode里面搜索它也變得簡單了。
點擊Run運行游戲,確保每次觸碰都會產生新的1到100之間的隨機數。
你或許注意到了,每個新的回合開啟時,滑動條都會重置到中間的位置。這是因為在startNewRound方法中我們重置currentValue的數值到50,然后讓滑動條的結點位置更改為這一點。
這個和我們最初的做法不同,之前我們是讀取滑動條的位置,然后把它的數值賦予currentValue。哥這樣做的目的是讓每一輪新的游戲都從相同的位置開始,感覺要靠譜點。
小練習:
如果你比較無聊,可以嘗試下修改代碼,讓滑動條不必每次開始的時候都放在中間的位置。
類型轉換
對了,你可能對以下代碼中Float(…)和Int(…)的作用有點迷糊:
targetValue = 1 + Int(arc4random_uniform(100)) slider.value = Float(currentValue)Swift是所謂的強類型語言,也就是說它對可以放進盒子里的形狀非常挑剔(也就是說Think Different行不通?!)。比如,一個變量是Int類型的,如果你把Float類型的數值交給它保存,它會直接拒絕的,反過來也一樣。
我們從UISlider所獲得的數值正好是Float類型的,也就是說帶小數點,之前我們曾經見識過。不過currentValue是Int類型的,因此如果你直接這樣寫代碼是不行的:
slider.value = currentValue編譯器會直接報錯。有些編程語言可能會自動幫你把Int類型轉換成Float類型,但Swift是不會樂意幫你做這種事情的~
當我們使用Float(currentValue)的時候,編譯器會把currentValue中保存的整數值轉換成一個新的Float浮點數,從而可以賜給UISlider。
之前在使用arc4random_uniform()的時候我們用過類似的方法,也就是在將隨機數保存到targetValue之前,首先需要把它轉換成一個Int數值。
Swift雖然是一門全新的語言,但是相比大多數的流行編程語言來說,對于變量類型的使用要求非常嚴格。這一點對于編程入門的新手來說會造成混亂和困惑,當然更加不幸的時Swift的錯誤提示信息還不能準確的指出代碼錯在哪里。
作為入門新手只需要記住,當你看到類似這樣的錯誤信息時,cannot assign value of type 'something' to type 'something else'
,就意味著你在數據的變量類型上使用出錯了。而解決方案只能是強制的把某個類型轉換成另一個。
繼續前進!
把我們的目標數值放到標簽里
好吧,我們已經知道如何生成隨機數,然后把它保存在一個名為targetValue的變量里。現在我們需要把這個數值放到屏幕上,不然玩家就是瞎貓去抓活老鼠,要多難有多難。
之前我們設計storyboard界面的時候,已經添加了一個用來放置目標值的標簽。現在要做的事情就是讀取targetValue變量的數值,然后把它放到標簽里。為了做到這一點,我們需要完成兩件事情:
1.創建一個到標簽對象的outlet,這樣我們才好向它發送消息
2.讓標簽顯示新的文本內容
其實之前對滑動條已經做過類似的事情了。還記得我們之前曾添加過@IBOutlet這個變量嗎?通過這種方式,我們可以從視圖控制器的任何一個地方引用該滑動條。通過這個outlet變量,我們可以向滑動條請求獲取它的數值,具體來說就是通過slider.value。對于這個標簽我們將用到相同的方式來完成。
在Xcode中切換到ViewController.swift,然后在其它outlet變量定義的下面添加下面的這行代碼:
@IBOutlet weak var targetLabel: UILabel!在Main.storyboard中,點擊選擇代表目標值的標簽
在Xcode右側切換到Connections inspector,然后從New Referencing Outlet拖一條線到View Controller,如下圖所示:
從彈出菜單中選擇targetLabel,這樣就建立了從界面視覺元素到視圖控制器中相關變量的關聯。
好了,又到了代碼時間。讓我們在Xcode中切換回代碼界面,打開ViewController.swift
在startNewRound()方法的下面添加一個新的方法:
func updateLabels(){ targetLabel.text = String(targetValue) }我們把這個邏輯放到一個單獨的方法里,這是因為在其它地方我們還會用到。
方法的名稱已經很清晰的說明了它的作用:使用它來更新標簽的文本內容。
當然,現在我們只是用它來更新單個標簽的文本內容,后面還會添加代碼來更新其它標簽的文本內容(總得分,游戲回合數)。
updateLabels這個方法里面的代碼現在應該可以看懂了,我們把一個字符串的內容賦予目標標簽的文本屬性。
不過你可能要問,為什么我們不能更簡單一點,用下面的代碼呢:
targetLabel.text = targetValue;原因很簡單,我們不能把整數類型的數值直接賦予字符串類型的變量。方井蓋沒法放進圓孔。
targetLabel這個outlet變量代表對UILabel對象的引用。UILabel標簽對象有自己的text屬性,它是一個字符串對象。我們只能把字符串類型的數據放到text里面。
但是剛才的代碼試圖把targetValue,一個Int類型的整數直接放到字符串類型的變量里。在Swift中這樣的事情顯然不可能發生。
所以我們需要先把整數類型的變量轉換為一個字符串,這就是String(targetValue)的作用。。其實之前我們也做過類似的事情,現在你應該知道為什么要這么樣做了吧。
但是用下面的方式是可行的:
targetLabel.text = “(targetValue)”需要注意的是,updateLabels()是個常規的方法,它沒有作為一個動作方法關聯到任何UI界面控件上,因此除非我們手動調用它,它就難有出頭之日。要區分某個方法是否是動作方法很簡單,只需要看前面是否有@IBAction即可。
調用updateLabels()方法比較合適的地方是在每次調用startNewRound()方法后,因為在那里我們將要獲取新的目標值。我們無需在兩個不同的地方調用updateLabels(),只需將其包含在startNewRound()方法中即可。
因此讓我們修改startNewRound()的方法如下:
func startNewRound() { targetValue = 1 + Int (arc4random_uniform(100)) currentValue = 50 slider.value = Float(currentValue) updateLabels() }黃色高亮部分就是新增的代碼。
Xcode的自動完成功能會幫你不小的幫,作為一個懶人,你只需要敲下方法名稱的頭幾個字母,比如upd,Xcode會自動幫你完成其它的。你只需要回車確認建議即可:
點擊Xcode上的Run按鈕來運行游戲,現在我們可以在屏幕上看到隨機生成的目標值了。
在結束之前,我們再來點理論知識惡補一下吧。
科普:動作方法vs一般方法
好吧,我們現在已經用到了兩種方法,分別是和界面中控件關聯起來的動作方法,以及有著獨立意識的一般方法。那么它們之間有什么區別呢?
答案是:沒什么區別。
唯一不同的就是@IBAction這個標示。當我們在方法前面加了這個東西后,Interface Builder就會知道我們可以把它和界面中的按鈕,滑動條或者其它控件關聯起來。
至于那些沒有@IBAction標識的方法就不能和界面中的控件建立關聯了。
// 基本動作方法 @IBAction func showAlert()// 動作方法,不過添加了到觸發這個動作的對象的引用
@IBAction func sliderMoved(_ slider: UISlider)@IBAction func buttonTapped(_ button: UIButton)// 不能和Interface Builder中的控件建立關聯
func updateLabels()好了,今天的學習到此結束,上福利了。
遙想公瑾當年。。。
未完待續
總結
以上是生活随笔為你收集整理的不显示调用super_让不懂编程的人爱上iPhone开发(2017秋iOS11+Swift4+Xcode9版)-第7篇的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: vins中imu融合_双目版 VINS
- 下一篇: 画面逐渐放大_日本80后画“人体妖女”,