你是一位熟悉另一平臺,希望開始 IPhone 開發(fā)(從而接觸 Objective-C)的軟甲開發(fā)者?不久之前的我就處于如此境地,而且坦率的說,由于日常工作的需求,我遠離開發(fā)很久了。
在兩年之后,我已經(jīng)創(chuàng)造了很多 iPhone 和 iPad 的應用。正如我的學習過程千辛萬苦,我希望你能夠從我所經(jīng)受的磨難中獲得一些益處。
本教程針對有開發(fā)經(jīng)驗的讀者。假定你分得出 while loop 同 fruit loops 的區(qū)別,以及 debug 同 ladybug 的區(qū)別!如果你對編程完全陌生,你可以先看看 給高中學生的 iOS 書 系列。
此教程的目標是通過 Objective-C 的基礎內(nèi)容,給予你一些信心。不同于“讓我們分析一下語法的每個片段”的做法,你將得到實踐和工作實例。這樣,當需要更進一步時,你能夠更容易的查閱參考(類似 這種)。
在此教程中,你將創(chuàng)建一個從儲存的列表中隨機抽取格言的簡單應用。在此過程中,你將能夠熟悉 Objective-C 的某些部分,包括:
- 變量
- 數(shù)組
- 屬性列表
- 字符串
- 斷言
- 隨機選擇
- 簡單的界面對象和事件
我需要先警告你——使用 Objective-C 進行 iPhone 開發(fā)的過程很有趣,你可能會有些上癮。做好放棄一些睡眠,家務堆積的心理準備!:]
在開始前,先確認你擁有一個蘋果開發(fā)者賬號,并配置完畢,同時安裝了最新版的 Xcode (你可以在 Mac App Store)免費下載。
如果都準備好了,I will (Objective) C-you after the jump! :]
準備開始
凡事有先后:創(chuàng)建一個 Xcode 項目。這篇教程以 Xcode 4.5+ 和 iOS 6+ 環(huán)境為基礎——如果你的 Xcode 版本較老,要不然升級,要不然改看本教程 iOS 6 之前的版本。
啟動 Xcode 并通過 iOS\Application\Single View Application (單視圖應用)模板創(chuàng)建一個新項目。
鍵入 QuoteGen 作為項目名稱,將設備家族設為 iPhone,確認 Use Automatic Reference Counting 和 Use Storyboards 被點選(且其它復選框未點選)。然后就可以點擊下一步并選擇項目保存路徑了。
你可以看到,你的項目中自動創(chuàng)建的文件有: AppDelegate.h,AppDelegate.m,ViewController.h 以及 ViewController.m 。同時還有 MainStoryboard.storyboard。
AppDelegate 包含初始化應用的代碼。在此教程中,你只需要知道這些。下面是你需要應對的文件的簡要說明:
- MainStoryboard.storyboard 是一個界面布局文件,通過此文件你可以可視化的創(chuàng)建/設計程序在 iPhone 屏幕上的顯示界面。
- ViewController.m 是界面控制器類。界面布局文件被鏈接到此類。這個過程是自動完成的,因此現(xiàn)階段你你只需要了解你在界面類中設置的對象和事件將會很容易的同你的界面布局鏈接。同時,這個文件也是承載你即將編寫的 Objective-C 代碼.
- ViewController.h是界面控制器類的頭文件,實體變量以及屏幕界面需要訪問的對象和事件都將被定義在這里。
注意:在 Xcode 中創(chuàng)建界面有兩種方法——使用 Storyboards 或 Xcode Interface Builder files (XIBs)。每個方式都很好用,在這篇教程里我們將使用 Storyboards,這是現(xiàn)在最流行的方式。不過兩種方法非常相似——只要了解了一種,即可觸類旁通。
關于 Storyboards 的更多信息,請在之后參閱 此教程。
向前進
首先你要做的事情就是為在此應用中顯示的摘引建立幾個變量——普通摘引和視頻相關摘引。
為此,你需要創(chuàng)建兩個 Objective-C 屬性。關于屬性有一些微妙之處,但現(xiàn)在你只需要將其視為為你的類創(chuàng)建變量的方式。
創(chuàng)建屬性很容易——讓我們通過為摘引數(shù)組創(chuàng)建屬性示例。在 ViewController.h 文件中 @interface 和 @end 行之間加入下面一行:
1 | @property (nonatomic, strong) NSArray *myQuotes; |
讓我們一點點分解它:
- 首先你需要添加 @property 關鍵詞。
- 然后,你將列出 屬性的特性。在此不再深入—— nonatomic 特性將提高線程安全消耗的性能,而 strong 特性則表示只要持有某個指針的對象存在,則此指向具體變量指針將一直保存在內(nèi)存之中。
- 之后,你將列出 屬性的類型。這里選擇 NSArray *,意為“指向 NSArray 類的指針”NSArray 是蘋果公司提供的一個方便保存列表信息的類——我們很快就會談到它。
- 最后,添加屬性名。
通過添加這一行,你有了一個這個類可以讀取和設置的變量!
注意:在過去,在創(chuàng)建屬性之后你還需要 @synthesize 處理,而在更早的過去,你還需要手動聲明你的實體變量。這一切現(xiàn)在都不必再做了——現(xiàn)在你只需要一行代碼,即可添加一個屬性。
同樣在過去,你需要自己處理所有的內(nèi)存管理,但通過新的名為 Automatic Reference Counting (ARC) 的特性,這個過程變得自動化了。閱讀更多 ARC 相關,參閱 此教程.
我是不是因為知道這一切泄露了我的年紀了? :]
這個應用也將保存一些電影中的名言。因此你需要創(chuàng)建第二個數(shù)組:
1 | @property (nonatomic, strong) NSMutableArray *movieQuotes; |
這里使用 NSMutableArray 只是為了說明數(shù)組的不同類型。卻別在于,在創(chuàng)建 NSArray 之后,你不能增加或減少其項目,而 NSMutabelArray 則隨時可以。
體力活
現(xiàn)在,你可以將你最喜歡的名言保存在 myQuotes 數(shù)組中了。你將在 viewDidLoad 中完成這一步,此方法在視圖(屏幕)第一次被創(chuàng)建時被調(diào)用
在 viewDidLoad 的 [super viewDidLoad]; 一行后添加下面的代碼。你可以添加你喜歡的名言。這是一項“體力活”,我們可以只加幾個數(shù)組項。
01 | // 1 - Add array of personal quotes |
04 | @ "Don't cry over spilt milk" , |
05 | @ "Always look on the bright side of life" , |
07 | @ "Can't see the woods for the trees" , |
08 | @ "Better to have loved and lost then not loved at all" , |
09 | @ "The early bird catches the worm" , |
10 | @ "As slow as a wet week" |
這樣,我們就將 myQuates 屬相填充完畢了。這里有一些你可能沒有用過的時髦語法,讓我們破解它。
- self 是一個特殊關鍵詞,意為“當前類”——類似其它語言中的 this。
- 你可以通過點+屬性名的方式訪問類屬性——例如:使用 self.myQuotes 訪問你之前創(chuàng)建的myQuates 屬性。
- 要想創(chuàng)建數(shù)組,Objective-C 中有一個方便的新簡寫——@[ item1, item 2, item 3 ]
- 數(shù)組中的每一項都是一個字符串。要在 Objective-C 中創(chuàng)建字符串,你需要為其添加 @ 符號作為前綴。如果你習慣于其它語言,這點很容易被忘記,并導致你的應用崩潰 :] 所以當你的使用字符串的應用崩潰時,重新檢查一下你是否忘記使用 @ 符號了!
很好——現(xiàn)在你的名言數(shù)組已經(jīng)準備就緒。是時候添加隨機顯示名言的代碼了。
開始Outlets
你還沒創(chuàng)建用戶界面,但要做的話,你需要添加一個文本框來顯示名言,一個按鈕點擊獲取隨機名言。
為了顯示一個隨機名言在屏幕上,你需要兩件事情-一個文本框的引用來設置文本,和按鈕被點擊的通知。
但是你如何把界面上的事情和代碼關聯(lián)起來?通過兩個關鍵字 – IBOutlet 和 IBAction!
讓我們看看它們怎么工作,先看看IBOutlet。在ViewController.h 數(shù)組下面添加下面的代碼:
1 | @property (nonatomic, strong) IBOutlet UITextView *quoteText; |
這里你像之前一樣聲明了一個屬性(UITextView類型的),但你用一個特殊的關鍵字標記它 – IBOutlet.
IBOutlet 意味著 quote_text 是一個可以鏈接到XIB文件的一個界面元素的對象。這樣view controller 就可以通過這個屬性訪問(修改)那個界面元素了。 這樣, 我們將設置這個UITextView顯示的文本,但你也可以輕松的修改它的顏色,字體,大小,等等。
下來,在屬性列表的后面添加下面的代碼:
1 | - (IBAction)quoteButtonTapped:(id)sender; |
它定義了一個方法,你必須在這個類里實現(xiàn)它。這是第一次你看到在Objective-C看到定義方法,所以我們一點點的講解一下:
- 首先你輸入一個破折號 -, 表明你要聲明一個實例方法。
- 下來你輸入這個方法的返回類型。這個方法返回一個 IBAction, 實際上IBAction定義為void - 也就是說這個方法什么也不返回。但 IBAction 有特別的用途 – 它標記這個方法可以鏈接到一個界面元素的動作上。在這個例子,你把按鈕點擊動作,關聯(lián)到這個方法上,也就是當按鈕點擊后,這個方法被調(diào)用。
- 下來你輸入方法的名字 – quoteButtonTapped.
- 下面輸入冒號,然后在圓括號中輸入第一個參數(shù)的類型。id 是一個特殊類型,代表“任何從NSObject繼承的對象”。通常當你設置按鈕或者其他控件的回調(diào)時,它們用這個按鈕/控件作為第一個參數(shù)調(diào)用回調(diào)。因為你不需要知道它是什么類型,所以你在這里輸入id。
- 下來你輸入?yún)?shù)的名字 – sender 。
如果你有超過一個的參數(shù),你應該重復步驟3-5。在Objective-C命名方法的語法有點奇怪,但你習慣了就會喜歡上它。
下來,切換到 ViewController.m 文件去添加quoteButtonTapped:的實現(xiàn)。在文件的結尾處(但要在@end之上)添加下面的代碼 :
01 | -(IBAction)quoteButtonTapped:(id)sender { |
02 | // 1 - Get number of rows in array |
03 | int array_tot = [self.myQuotes count]; |
04 | // 2 - Get random index |
05 | int index = (arc4random() % array_tot); |
06 | // 3 - Get the quote string for the index |
07 | NSString *my_quote = self.myQuotes[index]; |
08 | // 4 - Display the quote in the text view |
09 | self.quoteText.text = [NSString stringWithFormat:@ "Quote:\n\n%@" , my_quote]; |
讓我們一行一行的講解一下:
- 首先你得到一個數(shù)組的數(shù)據(jù)項個數(shù)。這是你第一次看到在Objective-C調(diào)用函數(shù)的例子。這個語法有點奇怪 – 你輸入左方括號([), 然后輸入包含你要調(diào)用的方法的對象的名字(self.myQuotes), 然后輸入你要調(diào)用的方法名(count). 最后,你輸入右方括號結束方法調(diào)用 (]). 注意這個方法不包含任何參數(shù) – 你會在第四步看到包含參數(shù)的例子。
- 下來你使用 arc4random 函數(shù)來產(chǎn)生一個隨機數(shù)。arc4random() 是一個常規(guī)的 C 風格的函數(shù) (而不是方法), 所以你使用你熟悉和熱愛的括號語法。在這個例子,因為你想隨機選擇一個名言,這個最大可能性就是數(shù)組的行數(shù),最小可能性就是0.在Objective-C (就像其他很多的語言), 輸入的第一行是從0開始而不是1.
- 下來你要在myQuotes查找一個數(shù)據(jù)項。 Objective-C’新的字面語法(literal syntax)允許你在訪問一個NSArray數(shù)據(jù)項時使用你這里看到的下標語法。
- 最后,你使用 stringWithFormat 方法格式化最終的輸出字符串,這樣你就可以在顯示名言前顯示一個標簽和換行。它就象C/C++的printf一樣使用變量替換。%f 是浮點型, %d 是整形, %@ 是Objective-C 對象.
現(xiàn)在為了真正的在屏幕上顯示名言,你需要把這個類的文本框的outlet鏈接到你的XIB文件的一個文本框界面元素。
關聯(lián)控件
讓我們看看怎么做, 打開 MainStoryboard.storyboard. 下來,查看Xcode窗口的右邊側邊欄。如果你看不到,你需要點擊上面工具欄中“Views”這一區(qū)中的最右邊的按鈕,來顯示右邊的側邊欄。
右邊的側邊欄的下半部分有四個tabs,你可以點擊相應的圖標來切換tab,我們現(xiàn)在要切換到 Object Library.
從 Object Library 拖拽一個文本框(Text View)和一個圓角矩形按鈕(Round Rect Button)到我們的view上。按照自己的喜好把它們放好位置。在按鈕上加一個標題,例如“名言”。如果喜歡還可以改變一下控件的顏色和字體。你可以通過右邊側邊欄的上半部分改變控件大部分屬性,它也有好幾個可以切換的tab - 我們最常用的是用來定制控件外觀的 Attributes Inspector tab.
因為這個文本框只是用來顯示,所以要取消 Behavior 中的 Editable 復選框.
現(xiàn)在你需要鏈接這個按鈕和文本框到你之前在類中設置好的outlet和action。
如果沒有看到View Controller Scene,點擊storyboard 左下方的第一個按鈕,是一個三角形按鈕,單機此按鈕顯示或隱藏View Controller Scene。
按著control鍵,鼠標左鍵點擊左邊側邊欄的View Controller,然后拖拽到中間的文本框,然后釋放鼠標左鍵,會彈出一個彈出菜單,選擇其中的“quoteText”.
另外的一種方式是,你可以先選中左邊欄的view controller,然后點擊右邊欄上面圖標,切換tab到 Connections Inspector tab. 然后就可以看到你的View Controller的所有可用的鏈接,然后拖拽“quoteText”到中間文本框。
請記住,Storyboard之所以知道你的quoteText屬性,是因為你之前添加的IBOutlet關鍵字!
鏈接動作
鏈接一個控件的動作(例如按鈕的點擊)到一個方法,過程和之前鏈接控件到屬性的第二種過程很類似。
這次,按control鍵,拖拽View Controller中的按鈕, 釋放按鍵,從彈出菜單選擇“quoteButtonTapped:” :
同樣有另外的方法,先選擇按鈕,跳轉(zhuǎn)右邊欄的tab頁到 Connections Inspector,你可以看到按鈕的所有事件,拖拽“Touch Up Inside”事件到View Controller,下來的步驟和上面的過程一樣。

按下Touch Up Inside”事件,其后的按鈕被選中,不要松按鈕,向左拖動,出現(xiàn)連接線。到storyboard上的按鈕上再松手。
全速前進!
猜怎么了? 你準備好開始搖滾吧。只要在Xcode的“Run”按鈕上點擊一下(在屏幕頂端的第一個按鈕)就開始編譯并且在模擬器上運行應用了。
如果期間發(fā)生錯誤,不要緊張。在這個階段,這些錯誤的提示都是明確。常常是聲明變量是打錯字了。記住Xcode是大小寫敏感的。
如果的應用通過了編譯,并且運行起來了。然后點擊"Quote"按鈕獲取一個隨機的名言:
好的 – 你有了個可以工作的應用,而且你已經(jīng)學了很多關于Objective-C的知識 – 你已經(jīng)學會創(chuàng)建屬性,方法,類,等等!
但是等等 – 還有些不完美的地方! 現(xiàn)在你的名言的列表是硬編碼在應用中。如果你可以從外部文件加載他們那該有多好阿?
啊哈,屬性列表的精彩之處開始登場了!
屬性列表規(guī)則
屬性列表是一種特殊的 XML 格式,它由蘋果公司設計,用來儲存基本數(shù)據(jù)類型如字符串、數(shù)字、數(shù)組和字典。它創(chuàng)建容易,并以代碼的形式讀寫,因此是在應用中引入少量數(shù)據(jù)的良好方式。
讓我們嘗試一下!在左側欄你的項目 root 上(項目導航欄)右擊鼠標創(chuàng)建,并選擇 New File 創(chuàng)建新文件。然后選擇 iOS\Resource\Property List 模板并單擊 Next。選擇儲存路徑(通常是項目文件夾中的某處)并將文件命名為 quotes.plist。
在 Xcode 中,你可以通過網(wǎng)格視圖(如同屬性的列表)或文本模式編輯屬性列表。如果想要以文本方式編輯,在項目導航欄中的 quotes 文件上右擊鼠標并選擇 Open As\Source Code。
如果你希望通過復制粘貼快速添加所有名言,以源代碼打開或許是更快的方法。如果你想這樣,你可以嘗試使用網(wǎng)格式圖方式,并嘗試找出如果使用該方法添加與下面的值?,F(xiàn)在,通過復制粘貼下面代碼到 quotes(用源代碼模式) 添加你的電影名言:
001 | <? xml version = "1.0" encoding = "UTF-8" ?> |
002 | <!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> |
009 | < key >category</ key > < string >classic</ string > |
010 | < key >quote</ key > < string >Frankly my dear, I don't give a dam.</ string > |
011 | < key >source</ key > < string >Gone with the wind</ string > |
015 | < key >category</ key > < string >classic</ string > |
016 | < key >quote</ key > < string >Here's looking at you kid.</ string > |
017 | < key >source</ key > < string >Casablanca</ string > |
021 | < key >category</ key > < string >classic</ string > |
022 | < key >quote</ key > < string >There's no place like home.</ string > |
023 | < key >source</ key > < string >Wizard of Oz</ string > |
026 | < key >category</ key > < string >classic</ string > |
027 | < key >quote</ key > < string >Play it again sam.</ string > |
028 | < key >source</ key > < string ></ string > |
031 | < key >category</ key > < string >classic</ string > |
032 | < key >quote</ key > < string >Elementary my dear Watson.</ string > |
033 | < key >source</ key > < string >Sherlock Holmes</ string > |
037 | < key >category</ key > < string >classic</ string > |
038 | < key >quote</ key > < string >Fasten your seatbelts. It's going to be a bumpy night.</ string > |
039 | < key >source</ key > < string >All about Eve</ string > |
043 | < key >category</ key > < string >classic</ string > |
044 | < key >quote</ key > < string >I have not the pleasure of understanding you.</ string > |
045 | < key >source</ key > < string >Pride and Predice</ string > |
049 | < key >category</ key > < string >classic</ string > |
050 | < key >quote</ key > < string >O Romeo, Romeo! wherefore art thou Romeo?</ string > |
051 | < key >source</ key > < string >Romeo and Juliet</ string > |
055 | < key >category</ key > < string >classic</ string > |
056 | < key >quote</ key > < string >To be or not to be</ string > |
057 | < key >source</ key > < string >Hamlet</ string > |
061 | < key >category</ key > < string >classic</ string > |
062 | < key >quote</ key > < string >...Crime is only a left-handed form of human endeavor.</ string > |
063 | < key >source</ key > < string >The Asphalt Jungle</ string > |
067 | < key >category</ key > < string >classic</ string > |
068 | < key >quote</ key > < string >That's, uh, quite a dress you almost have on...What holds it up?</ string > |
069 | < key >source</ key > < string >An American in Paris</ string > |
073 | < key >category</ key > < string >classic</ string > |
074 | < key >quote</ key > < string >Love, desire, ambition, faith - without them life is so simple, believe me</ string > |
075 | < key >source</ key > < string >Invasion of the Body Snatchers</ string > |
079 | < key >category</ key > < string >modern</ string > |
080 | < key >quote</ key > < string >Go ahead make my day.</ string > |
081 | < key >source</ key > < string >Dirty Harry</ string > |
085 | < key >category</ key > < string >modern</ string > |
086 | < key >quote</ key > < string >May the Force be with you.</ string > |
087 | < key >source</ key > < string >Star wars</ string > |
090 | < key >category</ key > < string >modern</ string > |
091 | < key >quote</ key > < string >Hasta la vista, baby</ string > |
092 | < key >source</ key > < string >Terminator</ string > |
095 | < key >category</ key > < string >modern</ string > |
096 | < key >quote</ key > < string >I feel the need for speed.</ string > |
097 | < key >source</ key > < string >Top Gun</ string > |
100 | < key >category</ key > < string >modern</ string > |
101 | < key >quote</ key > < string >She doesn't even go here.</ string > |
102 | < key >source</ key > < string >Mean Girls</ string > |
105 | < key >category</ key > < string >modern</ string > |
106 | < key >quote</ key > < string >It takes a great deal of bravery to stand up to your enemies, but a great deal more to stand up to your friends.</ string > |
107 | < key >source</ key > < string >Harry Potter</ string > |
110 | < key >category</ key > < string >modern</ string > |
111 | < key >quote</ key > < string >I solemnly swear that I am up to no good.</ string > |
112 | < key >source</ key > < string >Harry Potter</ string > |
116 | < key >category</ key > < string >modern</ string > |
117 | < key >quote</ key > < string >You like pain? Try wearing a corset.</ string > |
118 | < key >source</ key > < string >Pirates of the Carribean</ string > |
122 | < key >category</ key > < string >modern</ string > |
123 | < key >quote</ key > < string >Houston, we have a problem.</ string > |
124 | < key >source</ key > < string >Apollo 13</ string > |
128 | < key >category</ key > < string >modern</ string > |
129 | < key >quote</ key > < string >I'll be back.</ string > |
130 | < key >source</ key > < string >The Terminator</ string > |
133 | < key >category</ key > < string >modern</ string > |
134 | < key >quote</ key > < string >E.T. phone home.</ string > |
135 | < key >source</ key > < string >E.T.</ string > |
138 | < key >category</ key > < string >modern</ string > |
139 | < key >quote</ key > < string >Why are you trying so hard to fit in when you were born to stand out?</ string > |
140 | < key >source</ key > < string >What a girl wants</ string > |
144 | < key >category</ key > < string >modern</ string > |
145 | < key >quote</ key > < string >Watch you talkin about Willis?</ string > |
146 | < key >source</ key > < string >Different Strokes</ string > |
150 | < key >category</ key > < string >modern</ string > |
151 | < key >quote</ key > < string >The plane, the plane</ string > |
152 | < key >source</ key > < string >Fantasy Island</ string > |
155 | < key >category</ key > < string >modern</ string > |
156 | < key >quote</ key > < string >D'oh</ string > |
157 | < key >source</ key > < string >The Simpsons</ string > |
160 | < key >category</ key > < string >modern</ string > |
161 | < key >quote</ key > < string >Kids, you tried your best and you failed miserably. The lesson is, never try.</ string > |
162 | < key >source</ key > < string >The Simpsons</ string > |
166 | < key >category</ key > < string >modern</ string > |
167 | < key >quote</ key > < string >You don’t win friends with salad.</ string > |
168 | < key >source</ key > < string >The Simpsons</ string > |
172 | < key >category</ key > < string >modern</ string > |
173 | < key >quote</ key > < string >Whoever said that money doesn't buy happiness didn't know where to shop.</ string > |
174 | < key >source</ key > < string >Gossip Girl</ string > |
178 | < key >category</ key > < string >modern</ string > |
179 | < key >quote</ key > < string >If I were you, I'd accessorize with some gloves. Even a manicure can't mask those peasant hands.</ string > |
180 | < key >source</ key > < string >Gossip Girl</ string > |
184 | < key >category</ key > < string >modern</ string > |
185 | < key >quote</ key > < string >I tried to be diplomatic, but mostly I just lied a lot.</ string > |
186 | < key >source</ key > < string >Twilight</ string > |
191 | < key >category</ key > < string >modern</ string > |
192 | < key >quote</ key > < string >Once people start throwing wet stuff, I go inside.</ string > |
193 | < key >source</ key > < string >Twilight</ string > |
197 | < key >category</ key > < string >modern</ string > |
198 | < key >quote</ key > < string >I don’t like to lie – so there’d better be a good reason why I’m doing it..</ string > |
199 | < key >source</ key > < string >Twilight</ string > |
這里只有少量作為示例的名言。找點樂子,添加你喜歡的那些。如果你比較懶,示例項目中有一個包含很多名言的屬性列表
名言被分類為 classic 或 modern 用來說明你以后會了解到的非常好的功能。
你也可以切換到屬性列表視圖(Property List view) (在Project Navigator上鼠標右鍵點擊quotes文件,選擇Open As\Property List) 看看你添加的值在網(wǎng)格視圖中如何顯示和組織的?,F(xiàn)在你知道不同的編輯模式如何工作的了,你可以任意來回切換。
屬性列表很酷,但是如果你出錯了,就不酷了。新人常犯的錯誤是忘記結束的tag或者偶然刪除掉< 或者 >. 如果你的屬性列表不能加載,那你就需要仔細查閱,找出來原因。Xcode早期的版本可以提示錯誤行號。我想從版本4之后這個有用的特性就去掉了。
如果你被那個錯誤卡住了,你需要點技巧來查閱你的文件。我用了個方法(坦白說,這個方法用的有點多)來簡化查閱:備份我的plist文件,然后依次移除一點內(nèi)容來定位大致的錯誤位置。
創(chuàng)建完你可愛的屬性列表后,你要準備加載它到數(shù)組開始使用。所以,讓我們切換回ViewController.m 然后添加下面的代碼到viewDidLoad:結尾處:
1 | // 2 - Load movie quotes |
2 | NSString *plistCatPath = [[NSBundle mainBundle] pathForResource:@ "quotes" ofType:@ "plist" ]; |
3 | self.movieQuotes= [NSMutableArray arrayWithContentsOfFile:plistCatPath]; |
就這么簡單 – 現(xiàn)在你把你之前輸入到屬性列表的所有電影名言加載到一個數(shù)組里了!
要試用你的新數(shù)組,你可能想你只要把電影名言數(shù)組替換成你的名言數(shù)組就可以了。所以,在quoteButtonTapped: 里你簡單的替換所有movieQuotes引用成myQuotes, 對嗎?
如果你試了你就會發(fā)現(xiàn),但是只這樣做是不行的。因為myQuotes是一個名言字符串數(shù)組,但是movieQuotes不是。它是一個字典數(shù)組,字典是一列數(shù)據(jù),每個數(shù)據(jù)可以通過唯一的鍵值(a unique key)來訪問。
為什么?因為你設置了屬性列表導致的(再仔細看看就會明白了)
注意: 字典(Dictionaries) 是一個 key/value 容器, 類似于其他語言的哈希表(hashtables)。你可以用valueForKey查找字典里每一項。
所以用下列的代碼替換quoteButtonTapped,它替換成movieQuotes數(shù)組,而且使用正確鍵值從每項名言字典里獲取名言:
01 | -(IBAction)quoteButtonTapped:(id)sender { |
02 | // 1 - Get number of rows in array |
03 | int array_tot = [self.movieQuotes count]; |
04 | // 2 - Get random index |
05 | int index = (arc4random() % array_tot); |
06 | // 3 - Get the quote string for the index |
07 | //NSString *my_quote = [self.myQuotes objectAtIndex:index]; |
08 | NSString *my_quote = self.movieQuotes[index][@ "quote" ]; |
09 | // 4 - Display the quote in the text view |
10 | self.quoteText.text = [NSString stringWithFormat:@ "Quote:\n\n%@" , my_quote]; |
為了后面方便,保持段#3的注釋行。編譯,運行,享受你新的電影名言吧!
真棒,現(xiàn)在你有了個文件可以從外部文件讀取名言了。這非常方便,如果你希望其他人為你填寫一些名言。
接下來,你準備玩一些花樣了。允許用戶選擇看 myQuotes 或者 movieQuotes, 和選擇看經(jīng)典或者現(xiàn)代電影名言。
選項,選項,選項
首先你需要回到類的頭文件 ViewController.h 并添加一個新的屬性。
1 | @property (nonatomic, strong) IBOutlet UISegmentedControl *quoteOpt; |
這里你添加了一個連接到 Segmented Control 的屬性,這樣你就可以在選擇列表中選擇一個項目——這是選擇名言類型的最佳方式。
現(xiàn)在到
MainStoryboard.storyboard 頁面,并將一個 Segmented Control 空間拖到界面中。
將 control 的屬性修改如下:
- Style: Bar (my personal preference)
- Segments: 3
- Select Segment: 0 and change the title to: Classic
- Select Segment: 1 and change the title to: Modern
- Select Segment: 2 and change the title to: Mine
這樣就獲得了三個不同名言類型的效果,或者更準確的說,是三選一的能力。
創(chuàng)建好您的 Segmented Control 后, 您需要將它與您的類建立鏈接。 你可以使用之前相同的方式將控件鏈接到視圖控制器的 quoteOpt 屬性。
這個控件不需要使用行為事件。
為什么您不生成并運行一下看看你屏幕上的新控件呢?目前它還不會有任何功能,但是看到它在那里感覺很不錯!
謂詞的使用
謂詞是一個可以用于過濾數(shù)組的對象。它就像我們在 SQL 中 select 查詢的 where 子句。 我發(fā)現(xiàn)將它用于處理分類屬性表非常有用。這樣可以節(jié)省你不必創(chuàng)建單獨的屬性列表.
別生我的氣,你得回到 ViewController.m 并修改 quoteButtonTapped: 使用 myQuotes 代替 movieQuotes,很快你就將為你的電影對白做一些完全不同的東西。您需要為它設置一個條件,所以你將在 Segmented Control 的第三項選中時使用它。
或者如果你愿意,只需使用以下代碼替換 quoteButtonTapped: :
01 | -(IBAction)quoteButtonTapped:(id)sender { |
02 | // 1 - 當?shù)谌芜x中時獲取 quotes |
03 | if (self.quoteOpt.selectedSegmentIndex == 2) { |
05 | int array_tot = [self.myQuotes count]; |
07 | int index = (arc4random() % array_tot); |
08 | // 3 - 從索引處獲取 quote 字符串 |
09 | NSString *my_quote = self.myQuotes[index]; |
10 | // 4 - 在文本視圖上顯示 quote 字符串 |
11 | self.quoteText.text = [NSString stringWithFormat:@ "Quote:\n\n%@" , my_quote]; |
現(xiàn)在當用戶選擇第三項時,它將只看到 myQuotes 。正如你會發(fā)現(xiàn)剩下的代碼是和以前一樣,唯一的區(qū)別是只在段索引2選中時顯示 quote(來源于 personal quote 列表的 quote) 您可能還記得,因為段控件在索引0開始,索引2表示的第三個項目。
生成并測試你的代碼確保它正常工作,只有選中“Mine”時顯示 guotes 。
對謂詞的使用,首先你需要通過段控件當前選擇的段確定要用于數(shù)組過濾的分類,一起來!
這段是 quoteButtonTapped: if 段的后一段內(nèi)容 – 所以在第一節(jié)之后 if 段后如下:
04 | NSString *selectedCategory = @ "classic" ; |
05 | if (self.quoteOpt.selectedSegmentIndex == 1) { |
06 | selectedCategory = @ "modern" ; |
08 | // 2.2 - 使用謂詞通過分類過濾數(shù)組 |
09 | NSPredicate *predicate = [NSPredicate predicateWithFormat:@ "category == %@" , selectedCategory]; |
10 | NSArray *filteredArray = [self.movieQuotes filteredArrayUsingPredicate:predicate]; |
11 | // 2.3 - 獲取過濾后的數(shù)組大小 |
12 | int array_tot = [filteredArray count]; |
13 | // 2.4 - 作為保障只有當數(shù)據(jù)有內(nèi)容時繼續(xù) |
16 | int index = (arc4random() % array_tot); |
17 | // 2.6 - 獲取索引處的 quote 字符串 |
18 | NSString *quote = filteredArray[index][@ "quote" ]; |
19 | self.quoteText.text = [NSString stringWithFormat:@ "Movie Quote:\n\n%@" , quote]; |
21 | self.quoteText.text = [NSString stringWithFormat:@ "No quotes to display." ]; |
好的,生成并運行。檢查,你看到的引述取決于你的選擇正確的類型。如果你總是得到相同的類型,我的猜測是,你可能沒有將 Segmented Control 鏈接到你的類。
字符串 交響樂
到目前位置一切都很好! 現(xiàn)在讓我們探索Objective-C中一些不同的字符串選項和語法吧。
如果這個名言在屬性列表中有一個來源(source), 那么我們的應用也應該顯示它。檢查字符串是否包含值,可以檢查字符串的長度。
所以在quoteButtonTapped:的section #2.6第一行之后添加下面的代碼(第一行不包括注釋行):
1 | // 2.7 - Check if there is a source |
2 | NSString *source = [[filteredArray objectAtIndex:index] valueForKey:@ "source" ]; |
3 | if (![source length] == 0) { |
4 | quote = [NSString stringWithFormat:@ "%@\n\n(%@)" , quote, source]; |
6 | // 2.8 - Set display string |
同樣,注釋掉這一行,你不在需要它了:
1 | //self.quoteText.text = [NSString stringWithFormat:@"Movie Quote:\n\n%@", quote]; |
你從數(shù)組中獲得來源(source)然后檢查它的長度不為0來確認它是否包含有效值。 ! 代表 NOT. 當檢查整數(shù)是否相等時使用 == 。
然后你用stringWithFormat 把名言和來源連接起來,構建一個新的字符串來顯示。
為了更有趣些,為什么你把來自經(jīng)典電影的名言顯示的稍微不一樣些?這需要檢查選擇的名言歸屬那個類別。
用下面的代碼替換quoteButtonTapped:中的段 #2.8:
1 | // 2.8 - Customize quote based on category |
2 | if ([selectedCategory isEqualToString:@ "classic" ]) { |
3 | quote = [NSString stringWithFormat:@ "From Classic Movie\n\n%@" , quote]; |
5 | quote = [NSString stringWithFormat:@ "Movie Quote:\n\n%@" , quote]; |
8 | self.quoteText.text = quote; |
這里檢查是否字符串等于特定的值“classis”,然后特殊顯示這個類別里的label。
如果你想檢查一個電影的標題(或者其他任意的字符串)以特定的值開始。你也可以做到。如果你想特殊顯示一個來自哈利波特電影的的名言 - 添加下面的代碼在段 #2.9之上:
1 | if ([source hasPrefix:@ "Harry" ]) { quote = [NSString stringWithFormat:@ "HARRY ROCKS!!\n\n%@" , quote]; } |
就像你猜測的那樣, hasPrefix 用來檢測是否一個字符串的前綴包含特定的文本值。
編譯并運行你的應用,看它是否工作正常。特別注意一下來自不同的類別和來自哈利波特電影的名言是否正確顯示。
這是數(shù)組名言
為了好玩,你可以把你的所有名言串接起來,就好像他們是一個。這會演示如何數(shù)組循環(huán),這在你想遍歷處理數(shù)組每一個元素是很有用。
在 quoteButtonTapped: 中用下面的代碼替換段#1的代碼:
01 | if (self.quoteOpt.selectedSegmentIndex == 2) { |
02 | // 1.1 - Get array count |
03 | int array_tot = [self.myQuotes count]; |
04 | // 1.2 - Initialize string for concatenated quotes |
05 | NSString *all_my_quotes = @ "" ; |
06 | NSString *my_quote = nil; |
07 | // 1.3 - Iterate through array |
08 | for ( int x=0; x < array_tot; x++) { |
09 | my_quote = self.myQuotes[x]; |
10 | all_my_quotes = [NSString stringWithFormat:@ "%@\n%@\n" , all_my_quotes,my_quote]; |
12 | self.quoteText.text = [NSString stringWithFormat:@ "%@" , all_my_quotes]; |
一個for循環(huán)用于遍歷數(shù)組第0行(row 0)到最后一行。x是跟蹤行數(shù)的計數(shù)器。
現(xiàn)在運行查看結果。
最后一件事。我之前提過有兩種不同的數(shù)組:
NSArray 和
NSMutableArray. 到現(xiàn)在為止,這兩個類型在這個項目功效一樣。
如果你想更新/修改數(shù)組,可以使用NSMutableArray。就像它的名字所說,一個可變(mutable)數(shù)組可以修改,但一個普通NSArray是不可變的,你不能添加或刪除數(shù)組項。
例如,在一行選中后為了表明這個名言已經(jīng)顯示過了,你想更新數(shù)組,你需要使用 NSMutableArray.
在這個項目, movieQuotes 是你的 NSMutableArray. 你使用一個謂詞(predicate), 所以你首先需要找到這一行然后更新它。在quoteButtonTapped:的段#2.9添加下面的代碼:
01 | // 2.10 - Update row to indicate that it has been displayed |
02 | int movie_array_tot = [self.movieQuotes count]; |
03 | NSString *quote1 = filteredArray[index][@ "quote" ]; |
04 | for ( int x=0; x < movie_array_tot; x++) { |
05 | NSString *quote2 = self.movieQuotes[x][@ "quote" ]; |
06 | if ([quote1 isEqualToString:quote2]) { |
07 | NSMutableDictionary *itemAtIndex = (NSMutableDictionary *)self.movieQuotes[x]; |
08 | itemAtIndex[@ "source" ] = @ "DONE" ; |
你的循環(huán)遍歷這個數(shù)組,檢查每一行看是否是你查找的行。再一次用到 isEqualToString; 但是這次是比較兩個字符串變量。
為了更新數(shù)組中的這一行,你獲取這一行,然后更新對象。這超過了這個教程的知識范圍一點點,但你提前了解一點也很有好。
因為你更行了source,而且source是為每個類別來選擇名言的。所以下次你用NSPredicate過濾數(shù)組是,這一行就不會被包括在結果中了。相當整潔。
下面需要學習什么?
這里是 實例工程文件 包含我們上面教程的所有代碼。
好,你已經(jīng)完成了這個小工程。你已經(jīng)學會了不同方法使用數(shù)組,從界面事件出發(fā)動作,在Interface Builder中使用XIB文件訪問文件和做各種字符串連接和比較。作為第一個iPhone應用,這個成果還不錯!
現(xiàn)在你具備了基礎知識,你可以開始試試我們“如何寫一個簡單iPhone應用“( How To Create a Simple iPhone App)系列教程了。或者訂閱我們的月度簡報(sign up for our monthly newsletter)獲得為初學者寫的長篇iOS教程。
如果你有任何問題,可以使用這個論壇。同樣,如果你喜歡這個教程而且想看更多這個系列的,請在論壇上告訴我!
同時,祝你好運,生活快樂! :]