The Bull's Eye game
在本節課,你將要創建一個名為Bull's Eye的游戲。這是這個游戲完成品的預覽。
這個游戲的目標就是將刻度為0到100之間的滑條上的bull's eye(就是滑條上的浮標)的位置盡可能的拖放在接近等于給出隨機數的位置。在上面的屏幕截圖上,給出的目標值為84(原文中為22,估計是書的版本更新了,但是截圖木有更新),所以我們要將滑條上的bull's eye拖動到盡可能最好是剛好等于84的位置,因為我們無法看到滑條上的當前數值,所以我們需要目測位置。(我猜這就是這個游戲名字Bull's eye的來由了)
當你確實有自信位置已經接近目標值的時候點擊‘Hit Me’按鈕,然后界面上會彈出一個消息框,上面會顯示你的得分,像這個樣子:
你越是接近目標值,你的得分就越高。在你點擊‘OK’之后,彈窗會被關閉,新的一局就開始了,同時也會給出一個新的隨機數作為新的目標。每次重復游戲都會累加你的得分,直到你點擊‘Start Over’按鈕,就是左下角的那個U字型箭頭,你的得分將會被清零。
這個應用也許不會讓你立刻成為百萬富翁,但是那些App Store中的百萬富翁都是從這一小步開始的。
做一個編程的工作計劃
練習:現在,你已經看到了游戲的完整界面以及游戲的規則,試著列一下,為了完成這個游戲開發,你都需要做哪些工作,腦子里一片空白是正常的,但是盡可能的去例舉下自己應該做些什么。
我舉一個例子:
這個游戲應用需要放置一個名叫‘Hit Me’的按鈕到屏幕上,并且當玩家點擊這個按鈕到時候,需要彈出一個消息框。
試著去想想其他要做的一切工作,至于你是否能完成這些工作目前無關緊要。第一階段是要整理出需要做的事情,至于具體如何去做,眼下一點都不重要。(想象一下自己是個產品經理,只需要提要求就可以了,具體的事情可以交給開發團隊去做)
一旦你知道了自己想要的是什么,你就可以指出如何去做這件事,即使在這個過程中你不得不去問其他人或者查閱一些資料。但是‘想要什么’是最重要的。(你會驚訝于許多人開始寫代碼前根本根本沒有形成一個清晰的目標,難怪他們進行不下去!)
無論何時,我開始著手做一個新的app時,我會首先把我認為這個app需要的各種功能做一個列表。這個列表會成為我的編程清單。用一個列表把構想分解成若干小任務,是處理復雜工程的非常棒的方法。
你也許有一個非常棒的點子,但是當你坐下來開始編程的時候感覺根本無從下手,無法開始。通過把工作分解成若干小的步驟可以使整個事情的難度下降許多。我們總是可以找到簡單并且足夠簡易的一個出發點,并且從這里開始入手。
如果這個練習讓你覺得困難也沒啥大不了的。畢竟你是該領域的新手,對此一無所知。當你熟悉了軟件的運作方式以后,你可以輕易的將一個構想分解為易處理、可管控的若干小模塊。
這是我做的清單,我只是簡單的將游戲描述切成非常小的塊。
1、在屏幕上放置一個按鈕,并且標記為‘Hit Me’
2、當玩家點擊‘Hit Me’按鈕時,app應該彈出一個消息框,并且顯示玩家的成績。計算分值并且將分值顯示在消息框上。
3、在屏幕上放置文本,比如“Score:”、“Round:”這類標簽。其中一些文本在每一局的值會發生變化,比如score,當玩家得分后,該數字會增加。
4、放置一個滑條在屏幕上,并且設置滑條值的范圍為1-100.
5、當玩家點擊‘Hit Me’按鈕后,需要讀取滑條位置的值。
6、在每局的開始生成一個隨機數,并且將它顯示在屏幕上作為目標值。
7、通過比較滑條上的值和目標值為玩家計分,并且將結果顯示在彈出消息框上。
8、在屏幕上放置一個‘Start Over’按鈕。用于重制分值并且使游戲回到第一局。
9、固定app在橫向上。
10、把app界面做的好看些
也許我漏掉了那么一兩項工作,但是看起來像是一個得體的列表了。即使是像一個這樣小的游戲,也有如此多的工作需要做。開發app是非常有趣的,但同時也是工作繁重的。
一個按鈕的APP
讓我們從清單中的第一條開始,做一個關于這個游戲的及其簡單的第一個版本,顯示一個按鈕。當你點擊這個按鈕的時候,app會彈出一條消息。這就是我們此時要做的全部。一旦這一步成功了,我們將在這個基礎上做完這個游戲的其他剩余部分。
這個app會長成下面這個樣子:
到了寫代碼的時候了!我假設你已經下載并且安裝了最新版的軟件開發工具。
在我們的課程中,我們會使用Xcode 8.0或者更高版本。更新的版本也許也適用于本課程,但是任何低于8.0的版本就不適用了。
因為swift是一種非常新的語言,它經常隨著Xcode的版本變化而變化。如果你的Xcode版本過低或者過高!(目前最高就是8.0)那么本書中的代碼也許就不能完全運行了。(基于同樣的原因,你也不要去用任何beta版本,只用MAC App Store里現有的版本就可以了)
運行Xcode。如果你找不到Xcode應用的圖標,你可以從Finder/應用程序/Xcode中找到,或者在觸控板上四指合攏從Launchpad中找到。因為我自己經常使用Xcode,所以我把它固定在dock欄中,非常易于運行。
當Xcode運行時,會顯示一個‘Welcome to Xcode’的歡迎界面:
選擇Create a new Xcode project。就可以看到Xcode的主菜單窗口,并且從這里選擇一個模版。
這里有多種應用樣式的模版。Xcode會根據你選擇的模版做一些預配置,這樣當你新建一個工程的時候就已經生成了許多你需要的源文件。這些模版都非常便利,可以節省你大量的精力。他們是現成的起跑線。
選擇Single View Application(但視圖應用)并且點擊Next按鈕。
之后會打開一個界面,可以在這里配置新app的一些選項:
像這樣填寫以下項目:
Product Name(工程名稱):BullsEye。如果你想使用比較傳統的英語,你可以Bull's Eye代替BullsEye,但是最好工程名稱中還是避免使用空格和特殊字符。
Team(團隊):如果你已經是蘋果開發者成員了,那么這里會顯示你的團隊名稱。目前我們最好不要去管它;在本課程的后面我們會回過頭來討論這個事。
Organization Name(組織名稱):填寫你自己的名字或者公司的名字。
Organization Identifier(組織身份):我的是“com.razeware”。這是我用于自己app的唯一標示碼。習慣上,這里是將我的域名倒過來寫。你應該在這里填寫你自己的標識。選擇一個沒有重復的,或者把你自己的域名像我一樣倒過來寫,也可以僅僅是寫上自己的名字。你可以隨時修改這一項。(國內個人域名并不流行,為了確保唯一性,你可以填郵箱)
Language(語言):Swift
Devices(設備):iPhone
確保底部的三個復選框——Use Core Data,Include Unit Tests,和Include UI Tests不要被選中。在這個工程中你不會用到這些。
點擊Next。現在Xcode會讓你選擇工程文件的存儲路徑:
選擇一個位置存儲,比如桌面或者我的文檔。
Xcode會自動創建一個以工程名命名的新文件夾用于存放工程文件,所以你無需自己創建文件夾。
在底部有一個叫做“Create Git repository on My Mac”的復選框。目前你可以忽視它。你將會在下一個課程中學習有關版本控制的內容。
點擊Create完成新工程的創建。
這時Xcode已經基于單視圖應用的模版在你指定的文件夾中創建好了一個名為BullsEye。
你的屏幕現在看起來應該像這樣:
也許你電腦上的界面會和我的看起來有些不同,不過假如你的Xcode版本是8.0或者更新,那么放心,任何差別都是微不足道的。
注意:如果你看不到一個叫做 ViewController.Swift的文件,在左側列表中,而是可以看到ViewController.h和ViewController.m,那就是你選擇了錯誤的語言,你沒有選擇Swift,而是選擇了Objective-C。刪掉這個工程文件(去存貯目錄中刪,把整個目錄刪掉),并且重新創建一遍,確保語言選擇為Swift。
點擊左上角的Run按鈕
注意:如果這是你第一次運行Xcode,它也許會要求你打開開發者模式。點擊Enable并且輸入你的密碼,允許Xcode作出這些更改。
Xcode會在模擬器中運行你的app。這個app目前看起來一無是處,并且不具備任何功能,但是這是你旅途中的一個重要的里程碑。
當你點擊run按鈕時如果Xcode報錯“Build Failed”或者“Xcode cannot run using the selected device”,確定下圖中的這個選項是BullsEye>iPhone SE(或者其他任何型號),而不是Generic iOS Device
如果你的iPhone手機此時正好通過USB連接在你的MAC電腦上,那么Xcode也許會試圖直接在你的手機上運行這個app,但是沒有一些額外設置,是運行不起來的。在本節課快結束的時候,我會給你們展示如何在你的iPhone上運行你的app,這樣你就可以拿去給朋友們看了,但是目前我們還是在模擬器中進行教學。
在Run旁邊的是Stop按鈕(方塊的那個),點擊它退出app。
在我們的手機中你需要按一下home鍵退出app(在模擬器中需要選擇頂部菜單中的Hardware->Home選項),但是這樣并沒有實際的中斷app。它會在模擬器的屏幕上消失,但是實際上仍然駐留在模擬器的內存中,就和真實的手機上是一致的。
在你點擊Stop之前,Xcode的頂部活動指示器會始終顯示“Running BullsEye on iPhone SE”
你可以在app保持運行時回到Xcode進行代碼的變更,并不是非要把它停止掉。但是這些改變并不會被啟用,直到你停止并且重新啟動app它們才會生效。點擊停止再點擊運行后,會中斷該app任何運行中的版本,之后生成一個新的版本,并且重新在模擬器中運行。
當你點擊Run后發生了什么?
首先Xcode會編譯你的源代碼-就是把Swift語言翻譯為iPhone或者模擬器能夠識別的機器語言。因為即使用Swift或者Objective-C寫成的iPhone app,iPhone也不認識它們。所以翻譯的工作是必須的。
編譯器是Xcode的一部分,它會將你的Swift源代碼轉換為可執行的二進制代碼。它同時也收集所有用于組裝app的組件-比如源文件、圖像、故事模版文件(storyboard files)等等,并且將它們打包為所謂的應用包(application bundle)
這整個進程稱之為創建app。如果這中間有什么錯誤(比如關鍵字拼寫錯誤),創建會失敗。如果所有事情都按部就班的進行,Xcode會復制應用包到模擬器或者手機中,并且運行它。所有這些事情僅僅點一下Run就完成了。
添加按鈕
我敢肯定你和我一樣有點驚訝于一個app僅僅展現一個白色的界面,所以,我們加一個按鈕上去吧。
Xcode窗口的左手邊部分叫做導航器區域(Navigator area)。頂部的一行圖標決定目前顯示的是哪個導航器。目前顯示的是工程導航器,用于展示工程中的文件列表。
這些文件的組織方式和你硬盤中的工程文件夾中的大致一致,但并不是非得這樣。你可以按照你想的把這些文件拖拽到新的分組中去。我們稍后會討論工程中的不同文件。
在工程導航器中,找到名為Main.storyboard并且單擊選擇它:
選中以后,像超人在電話亭里變裝一樣,Xcode的主編輯面板就切換到了界面建造器(Interface Builder)。這個工具使你可以通過拖拽的方式將界面組件放置到app上,比如一個按鈕。(好吧,雖然類比有些不恰當,但是界面建造器對我而言是個超級工具)
如果你看不到界面建造器,點擊Xcode工具欄右上角中的‘隱藏或顯示實用工具(Hide or show utilities)’按鈕
這幾個工具欄按鈕可以改變Xcode的界面。這一個負責打開一個新的面板位于Xcode窗口的右側。
現在你的Xcode應該看起來像是這個樣子了:
這就是屬于你app的故事面板(storyboard),你的app中的全部界面設計都會體現在這個故事面板中,并且以一個大箭頭顯示app如何從一個界面切換到另一個界面。
目前這個故事面板僅包含一個界面(或場景),就是界面建造器畫布(真的是這樣翻譯,屏幕中間的一大塊白色區域稱之為畫布)中的那個長方形。
注意:如果你看不到一個長方形,上面標有“View Controller”,而僅僅是一大片白色畫布,那么用你的鼠標或觸控板向四周滾動一下看看。相信我,它就在某個地方!并且保證你的Xcode窗口足夠大(13寸屏估計會很杯具)。界面建造器會占用很大一塊地方。
這個場景目前的大小應該是iPhone6或者iPhoen7的大小。為了使事情簡單點,剛開始,我們會在iPhone SE的大小上進行設計,這樣占用的屏幕會稍微小一些。之后我們會使這個app同樣適應于iPhone6s,7和plus。
在界面建造器窗口的底部,點擊“View as:iphone 6s”也許你的顯示為“View as:iphone 7”會彈出一個面板(再次點擊就可以關閉它):
選擇iPhone SE,第二小的那個iPhoen圖標。選擇之后代表場景的長方形會變小一些。該場景適用于iPhone 5,5s和SE的大小。
在Xcode的工具欄中,確保Stop按鈕旁邊顯示為BullsEye>iPhone SE。如果不是的話則點擊它并且在彈出列表中選擇iPhone SE:
現在你運行app 的話,就會在iPhone SE的模擬器上運行了(試試看!)
回到故事面板
在實用工具面板的底部你會找到對象庫(Object Library),確保被選中的是第三個看起來像是一個圓形的那個按鈕:
在對象庫中用鼠標滾動,查找到“Button”。
點擊Button并且將它拖拽到工作區域,場景長方形的上面。
添加一個新按鈕就是如此簡單,僅僅是拖拽就可以了。其他的全部用戶界面元素也都是這樣處理。你會大量做這種工作,所以我們來花點時間熟悉下這個過程。
拖拽一些其他的控件,比如labels(標簽),sliders(滑條),switches(開關),找找感覺。
這會給你在如何在iOS中使用UI控件帶來一些靈感。注意一下,界面建造器可以通過吸附這些控件到屏幕邊緣或者其他對象上來幫助你進行布局。這非常便利!
雙擊button來編輯標題。將它命名為“Hit Me!”
也許你的button會被一個邊界包圍著:
這個邊界并不是按鈕的一部分,僅僅是為了方便你查看這個button有多大。你可以在菜單Editor->Canvas->Show Bounds Rectangles中關閉顯示這個邊界。(為了不給自己找麻煩,我建議大家還是選擇顯示邊界)
當你大概熟悉了界面建造器后,在Xcode的工具欄中點擊Run按鈕。這個app就應該在模擬器中運行了,這時你可以看到你的“Hit Me!”
按鈕。然而,當你點擊這個按鈕時,什么都不會發生。為此,你不得不寫點Swift代碼了。
源碼編輯器
一個點擊后什么都不做的按鈕對誰而言都是無用的,所以我們讓它來彈出一個提醒窗口。在游戲結束的時候,這個提醒窗口會顯示玩家的分數,但是現在我們僅僅是給自己展示一條簡短的消息。(根據傳統,這條消息應該是“Hello World”)
在工程導航器里點擊ViewController.swift。
點擊后界面建造器就會消失了,取而代之顯示的是一個顯示許多高亮色字體的編輯區域。這就是你的app的swift源代碼。
在最后一個大括號‘}’前添加如下代碼:
@IBAction func showAlert() {
}
現在ViewController.swift的代碼應該是這樣子的(簡書中的代碼框如果顯示不全,尤其是在手機上看的時候,可以左右拖拽代碼框):
//
// ViewController.swift
// BullsEye
//
// Created by on 2017/1/11.
// Copyright ? 2017年 . All rights reserved.
//
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
@IBAction func showAlert() {
}
}
對你自己的第一行代碼感覺如何?在我告訴你這些代碼是作什么用的之前,我先要介紹一些關于view controller的概念。
Xcode會自動保存
你不需要在每次改動后去手動保存你的代碼文件,因為當你點擊Run按鈕的時候Xcode會自動的保存所有被修改的文件。雖然如此,但是Xcode并不是世界上最穩定的軟件,偶爾它也會崩潰并且沒有來及保存你的修改,所以我仍然喜歡用command+S來自己保存文件。
視圖控制器(view controllers)
你已經通過對Main.storyboard文件編輯建造了這個app的用戶界面。盡管上面只有一個按鈕和一個純白色的背景。你同時也在VIewController.swift文件中添加了源代碼。
這兩個文件——storyboard和swift文件,一起實現了一個視圖控制器(view controller)。做一個iOS app的大量工作就是制作視圖控制器。一個視圖控制器的作用就是管理你app上的一個界面。
以一個簡單的食譜類app為例子。當你運行這個食譜app的時候,它的主界面列出了許多食譜。點擊一下其中一個食譜就會彈出一個新的界面,顯示關于這個食譜的烹飪方法、細節以及照片。每一個界面都被屬于自己的視圖控制器管理著。
這兩個界面的作用是有很大不同的,一個是若干項目的列表;另一個是展示某條項目的詳細細節。
這就是為什么此時同時需要兩個視圖控制器:一個知道如何處理列表,另一個可以處理圖像和烹飪方法介紹的文本。這種視圖控制器(view controller)被簡單的命名為“VIewController”,由storyboard和swift文件共同實現它。
簡而言之,Main.storyboard文件包含視圖控制器的用戶界面設計部分,而ViewController.swift包含它的功能部分——用swift語言寫的邏輯使得用戶界面可以正常工作。
因為你使用的是單視圖應用模版(Single View Application template),所以Xcode會自動為你創建一個視圖控制器。稍后,你將會添加第二個界面到這個游戲中,你將為這個界面手動創建一個視圖控制器。
形成鏈接
你在ViewController.swift文件中添加的幾行源代碼使得界面建造器(Interface Builder)知道了這個控制器有一個叫做“showAlert”的動作,就是會彈出一個提示窗口的動作。你將要把按鈕和這個動作鏈接起來。
點擊Main.storyboard 回到界面建造器。
界面建造器的左邊應該會有一個面板,綱要面板,這里列出了storyboard中的所有項目。如果你看不到這個面板,點擊界面建造器左下角的一個小開關來顯示它。
單擊“Hit Me”按鈕來選中它
在Hit Me按鈕被選中的同時,按住ctrl鍵,在按鈕上點擊一下,并且將它拖向略縮面板中的View Controller項目。你應該可以看到一條藍色的線將按鈕和View Controller鏈接了起來。(除了按住ctrl鍵,你也可以用鼠標右鍵進行這一拖拽)
當完成拖拽,放開鼠標時,你將會看到出現一個小菜單。它包含兩個部分,“Action Segue”和“Sent Events”,在每個部分的下面都有一個或多個選項。你要選擇的是“Sent Events”下面的“showAlert”選項。這是你之前在ViewController.swift文件中寫源代碼時,為這個動作所取的名稱。
從現在開始,不管什么時候點擊按鈕都會觸發showAlert這個動作。這就是你做按鈕或者其他控件時要做的事情:你在ViewController對應的Swift文件中定義動作,然后把它和界面鏈接起來。
你可以在鏈接指示器中查看你創建的鏈接,就是實用工具面板中的最右面一個選項。
點擊這個右箭頭符號的按鈕,切換到鏈接指示器:
選擇ViewController.swift來編輯它。
注意@IBAction fund showAlert的左邊,這里有一個實心圓點。點擊它也可以展示鏈接的內容。
讓按鈕表演一下它的功能
你現在有了一個界面和一個按鈕。這個按鈕鏈接著一個叫showAlert的動作,當你點擊這個按鈕時,將觸發這個動作。
目前,無論你如何點擊這個按鈕,還是什么都不會發生,因為畢竟這個動作是空的,里面什么都沒有,讓我們來給它一點指令。
在ViewController.swift中,添加以下代碼到showAlert中(再次提示代碼框可以左右滑動):
@IBAction func showAlert() {
let alert = UIAlertController(title: "Hello World", message: "This is my first app!", preferredStyle: .alert)
let action = UIAlertAction(title: "Awesome", style: .default, handler: nil)
alert.addAction(action)
present(alert,animated: true,completion: nil)
}
這些新的代碼給這個動作提供了實際的功能。
{}花括號中間的代碼告訴iPhone在點擊按鈕后去做什么。
showAlert后面跟的花括號中的代碼創建了一個提醒窗口,標題為“Hello World”,內容為“This is my first app!”,以及一個新的按鈕,叫做“Awesome”。
如果你分不清楚標題和內容的區別,你就記住,它們都是文本,并且標題的字體會比內容的要大。
點擊位于Xocde工具欄上的Run按鈕。如果你沒有在代碼里有拼寫錯誤,那么你的app將在模擬器中如期運行,并且當你點擊Hit Me按鈕時,你會看到一個提醒窗口。
恭喜,你已經完成了你的第一個iOS app!你剛才做的事情也許讓你混亂不堪,但是沒有關系。我們會穿插的講解每一個小步驟。
你可以輕易的重復之前你做過的兩個步驟:放一個button到界面上并且當點擊按鈕時展現一個提醒窗口。
稍微中斷一下,讓剛才的內容沉淀一下,當你準備好接受更多內容時再回來。你僅僅是踏出了第一個小步。。。
??:為了防止你卡住做不下去,在隨書附帶的源代碼文件夾中我提供了每一小節的完整代碼。(想到得到代碼的同學請支持正版_)。這樣你就可以用自己的代碼和我提供的代碼做對比學習,或者你弄糟了一些事情,也可以用我的版本繼續往下學習。
截止目前為止你做的一切,都可以在01-one button app文件夾中找到完整的project文件。
問題?
如果你點擊運行的時候,你的Xcode給你一個‘Build Failed’的報錯,這時你需要確認你是否存在拼寫錯誤,有沒有敲錯字符。即使最小的錯誤也會完全使Xcode無法理解你的意圖而報錯。一個小的拼寫錯誤,也許會在不同文件中造成多個錯誤。
典型的錯誤是大小寫不分,Swift編程語言有大小寫之分的,這意味這alert和Alert是兩個不同的名字。如果將alert錯寫為Alert的話,Xcode編譯器會給出“<something> undeclared”或者“Use of undeclared identifier”的報錯。
當Xcode告訴你類似于“Parse Issue”或者“Expected<something>”時,你多半是漏掉了某個花括號"{"或者圓括號“)”。沒有相匹配的前后括號(Not matching up opening and closing brackets)是一種常見的錯誤。
(小貼士:如果你將光標移動到某個括號上雙擊一下,Xcode會高亮顯示出對應的括號在哪里)
當你編程時像這樣的小細節非常重要。即使敲錯一個字符都可以使Xcode無法編譯你的app。
幸運的是,像這樣的錯誤是可以輕易發現的。
當Xcode查到有錯誤時,就會切換之前的左半部分的面板,切到到問題導航器上去。這里的列表顯示了所有Xcode發現的的錯誤和警告。(你可以通過點擊頂部的小按鈕(文件夾圖標的那個)回到project files面板中去)
在上圖的報錯中,顯然我時忘掉了某個逗號。
點擊錯誤信息后Xcode會告訴你時在哪一行發生的錯誤。并且會顯示一個建議的解決方案:
有時是很難定位到底那里出了問題,幸運的是,Xcode提供了有力的幫助。
Errors(錯誤) and warnings(警告)
Xcode對errors(紅色的)和warnings(黃色的)做了區分。Errors是致命的。如果存在一個error,app將無法運行。warnings是警告性質的。Xcode僅僅是說:“也許不能這樣做,但是管他呢,繼續往下吧”
但是我認為,最好是將warnings和errors以同等重視程度對待。在運行程序前處理掉每一個warning和error。即使這樣也不能保證app不存在bug,但是至少不會是低級錯誤。