iOS 6 Objective-C 速成

oschina發表於2013-04-09

  你是一位熟悉另一平臺,希望開始 IPhone 開發(從而接觸 Objective-C)的軟體開發者?不久之前的我就處於如此境地,而且坦率的說,由於日常工作的需求,我遠離開發很久了。

  在兩年之後,我已經創造了很多 iPhone 和 iPad 的應用。正如我的學習過程千辛萬苦,我希望你能夠從我所經受的磨難中獲得一些益處。

  本教程針對有開發經驗的讀者。假定你分得出 while loop 同 fruit loops 的區別,以及 debug 同 ladybug 的區別!如果你對程式設計完全陌生,你可以先看看 給高中學生的 iOS 書 系列。

此教程的目標是通過 Objective-C 的基礎內容,給予你一些信心。不同於“讓我們分析一下語法的每個片段”的做法,你將得到實踐和工作例項。這樣,當需要更進一步時,你能夠更容易的查閱參考(類似 這種)。

  在此教程中,你將建立一個從儲存的列表中隨機抽取格言的簡單應用。在此過程中,你將能夠熟悉 Objective-C 的某些部分,包括:

  • 變數
  • 陣列
  • 屬性列表
  • 字串
  • 斷言
  • 隨機選擇
  • 簡單的介面物件和事件

  我需要先警告你——使用 Objective-C 進行 iPhone 開發的過程很有趣,你可能會有些上癮。做好放棄一些睡眠,家務堆積的心理準備!:]

  在開始前,先確認你擁有一個蘋果開發者賬號,並配置完畢,同時安裝了最新版的 Xcode (你可以在 Mac App Store)免費下載。

  如果都準備好了,I will (Objective) C-you after the jump! :]

  準備開始

  凡事有先後:建立一個 Xcode 專案。這篇教程以 Xcode 4.5+ 和 iOS 6+ 環境為基礎——如果你的 Xcode 版本較老,要不然升級,要不然改看本教程 iOS 6 之前的版本

 啟動 Xcode 並通過 iOS\Application\Single View Application (單檢視應用)模板建立一個新專案。

  鍵入 QuoteGen 作為專案名稱,將裝置家族設為 iPhone,確認 Use Automatic Reference CountingUse Storyboards 被點選(且其它核取方塊未點選)。然後就可以點選下一步並選擇專案儲存路徑了。

使用 Single-View Application 模板建立新專案

你可以看到,你的專案中自動建立的檔案有: AppDelegate.hAppDelegate.mViewController.h 以及 ViewController.m 。同時還有 MainStoryboard.storyboard

View-based application 模板的預設檔案

  AppDelegate 包含初始化應用的程式碼。在此教程中,你只需要知道這些。下面是你需要應對的檔案的簡要說明:

  • MainStoryboard.storyboard 是一個介面佈局檔案,通過此檔案你可以視覺化的建立/設計程式在 iPhone 螢幕上的顯示介面。
  • ViewController.m 是介面控制器類。介面佈局檔案被連結到此類。這個過程是自動完成的,因此現階段你你只需要瞭解你在介面類中設定的物件和事件將會很容易的同你的介面佈局連結。同時,這個檔案也是承載你即將編寫的 Objective-C 程式碼.
  • ViewController.h是介面控制器類的標頭檔案,實體變數以及螢幕介面需要訪問的物件和事件都將被定義在這裡。

  注意:在 Xcode 中建立介面有兩種方法——使用 Storyboards 或 Xcode Interface Builder files (XIBs)。每個方式都很好用,在這篇教程裡我們將使用 Storyboards,這是現在最流行的方式。不過兩種方法非常相似——只要瞭解了一種,即可觸類旁通。

  關於 Storyboards 的更多資訊,請在之後參閱 此教程

  向前進

  首先你要做的事情就是為在此應用中顯示的摘引建立幾個變數——普通摘引和視訊相關摘引。

  為此,你需要建立兩個 Objective-C 屬性。關於屬性有一些微妙之處,但現在你只需要將其視為為你的類建立變數的方式。

  建立屬性很容易——讓我們通過為摘引陣列建立屬性示例。在 ViewController.h 檔案中 @interface 和 @end 行之間加入下面一行:

@property (nonatomic, strong) NSArray *myQuotes;

  讓我們一點點分解它:

  • 首先你需要新增 @property 關鍵詞。
  • 然後,你將列出 屬性的特性。在此不再深入—— nonatomic 特性將提高執行緒安全消耗的效能,而 strong 特性則表示只要持有某個指標的物件存在,則此指向具體變數指標將一直儲存在記憶體之中。
  • 之後,你將列出 屬性的型別。這裡選擇 NSArray *,意為“指向 NSArray 類的指標”NSArray 是蘋果公司提供的一個方便儲存列表資訊的類——我們很快就會談到它。
  • 最後,新增屬性名

  通過新增這一行,你有了一個這個類可以讀取和設定的變數!

  注意:在過去,在建立屬性之後你還需要 @synthesize 處理,而在更早的過去,你還需要手動宣告你的實體變數。這一切現在都不必再做了——現在你只需要一行程式碼,即可新增一個屬性。

  同樣在過去,你需要自己處理所有的記憶體管理,但通過新的名為 Automatic Reference Counting (ARC) 的特性,這個過程變得自動化了。閱讀更多 ARC 相關,參閱 此教程.

  我是不是因為知道這一切洩露了我的年紀了? :]

  這個應用也將儲存一些電影中的名言。因此你需要建立第二個陣列:

@property (nonatomic, strong) NSMutableArray *movieQuotes;

  這裡使用 NSMutableArray 只是為了說明陣列的不同型別。卻別在於,在建立 NSArray 之後,你不能增加或減少其專案,而 NSMutabelArray 則隨時可以。

  體力活

  現在,你可以將你最喜歡的名言儲存在 myQuotes 陣列中了。你將在 viewDidLoad 中完成這一步,此方法在檢視(螢幕)第一次被建立時被呼叫

  viewDidLoad 的 [super viewDidLoad]; 一行後新增下面的程式碼。你可以新增你喜歡的名言。這是一項“體力活”,我們可以只加幾個陣列項。

// 1 - Add array of personal quotes
 self.myQuotes = @[
                     @"Live and let live",
                     @"Don't cry over spilt milk",
                     @"Always look on the bright side of life",
                     @"Nobody's perfect",
                     @"Can't see the woods for the trees",
                     @"Better to have loved and lost then not loved at all",
                     @"The early bird catches the worm",
                     @"As slow as a wet week"
                     ];

  這樣,我們就將 myQuates 屬相填充完畢了。這裡有一些你可能沒有用過的時髦語法,讓我們破解它。

  • self 是一個特殊關鍵詞,意為“當前類”——類似其它語言中的 this
  • 你可以通過點+屬性名的方式訪問類屬性——例如:使用 self.myQuotes 訪問你之前建立的myQuates 屬性。
  • 要想建立陣列,Objective-C 中有一個方便的新簡寫——@[ item1, item 2, item 3 ]
  • 陣列中的每一項都是一個字串。要在 Objective-C 中建立字串,你需要為其新增 @ 符號作為字首。如果你習慣於其它語言,這點很容易被忘記,並導致你的應用崩潰 :] 所以當你的使用字串的應用崩潰時,重新檢查一下你是否忘記使用 @ 符號了!

  很好——現在你的名言陣列已經準備就緒。是時候新增隨機顯示名言的程式碼了。

  開始Outlets

  你還沒建立使用者介面,但要做的話,你需要新增一個文字框來顯示名言,一個按鈕點選獲取隨機名言。

  為了顯示一個隨機名言在螢幕上,你需要兩件事情-一個文字框的引用來設定文字,和按鈕被點選的通知。

  但是你如何把介面上的事情和程式碼關聯起來?通過兩個關鍵字 – IBOutlet 和 IBAction!

  讓我們看看它們怎麼工作,先看看IBOutlet。在ViewController.h 陣列下面新增下面的程式碼:

@property (nonatomic, strong) IBOutlet UITextView *quoteText;

  這裡你像之前一樣宣告瞭一個屬性(UITextView型別的),但你用一個特殊的關鍵字標記它 – IBOutlet.

  IBOutlet 意味著 quote_text 是一個可以連結到XIB檔案的一個介面元素的物件。這樣view controller 就可以通過這個屬性訪問(修改)那個介面元素了。 這樣, 我們將設定這個UITextView顯示的文字,但你也可以輕鬆的修改它的顏色,字型,大小,等等。

  下來,在屬性列表的後面新增下面的程式碼:

- (IBAction)quoteButtonTapped:(id)sender;

  它定義了一個方法,你必須在這個類裡實現它。這是第一次你看到在Objective-C看到定義方法,所以我們一點點的講解一下:

  1. 首先你輸入一個破折號 -, 表明你要宣告一個例項方法。
  2. 下來你輸入這個方法的返回型別。這個方法返回一個 IBAction, 實際上IBAction定義為void - 也就是說這個方法什麼也不返回。但 IBAction 有特別的用途 – 它標記這個方法可以連結到一個介面元素的動作上。在這個例子,你把按鈕點選動作,關聯到這個方法上,也就是當按鈕點選後,這個方法被呼叫。
  3. 下來你輸入方法的名字 – quoteButtonTapped.
  4. 下面輸入冒號,然後在圓括號中輸入第一個引數的型別。id 是一個特殊型別,代表“任何從NSObject繼承的物件”。通常當你設定按鈕或者其他控制元件的回撥時,它們用這個按鈕/控制元件作為第一個引數呼叫回撥。因為你不需要知道它是什麼型別,所以你在這裡輸入id。
  5. 下來你輸入引數的名字 – sender

  如果你有超過一個的引數,你應該重複步驟3-5。在Objective-C命名方法的語法有點奇怪,但你習慣了就會喜歡上它。

  下來,切換到 ViewController.m 檔案去新增quoteButtonTapped:的實現。在檔案的結尾處(但要在@end之上)新增下面的程式碼 :

-(IBAction)quoteButtonTapped:(id)sender {
    // 1 - Get number of rows in array
    int array_tot = [self.myQuotes count];
    // 2 - Get random index
    int index = (arc4random() % array_tot);
    // 3 - Get the quote string for the index
    NSString *my_quote = self.myQuotes[index];
    // 4 - Display the quote in the text view
    self.quoteText.text = [NSString stringWithFormat:@"Quote:\n\n%@",  my_quote];
}

  讓我們一行一行的講解一下:

  1. 首先你得到一個陣列的資料項個數。這是你第一次看到在Objective-C呼叫函式的例子。這個語法有點奇怪 – 你輸入左方括號([), 然後輸入包含你要呼叫的方法的物件的名字(self.myQuotes), 然後輸入你要呼叫的方法名(count). 最後,你輸入右方括號結束方法呼叫 (]). 注意這個方法不包含任何引數 – 你會在第四步看到包含引數的例子。
  2. 下來你使用 arc4random 函式來產生一個隨機數。arc4random() 是一個常規的 C 風格的函式 (而不是方法), 所以你使用你熟悉和熱愛的括號語法。在這個例子,因為你想隨機選擇一個名言,這個最大可能性就是陣列的行數,最小可能性就是0.在Objective-C (就像其他很多的語言), 輸入的第一行是從0開始而不是1.
  3. 下來你要在myQuotes查詢一個資料項。 Objective-C’新的字面語法(literal syntax)允許你在訪問一個NSArray資料項時使用你這裡看到的下標語法。
  4. 最後,你使用 stringWithFormat 方法格式化最終的輸出字串,這樣你就可以在顯示名言前顯示一個標籤和換行。它就象C/C++的printf一樣使用變數替換。%f 是浮點型, %d 是整形, %@ 是Objective-C 物件.

  現在為了真正的在螢幕上顯示名言,你需要把這個類的文字框的outlet連結到你的XIB檔案的一個文字框介面元素。

  關聯控制元件

  讓我們看看怎麼做, 開啟 MainStoryboard.storyboard. 下來,檢視Xcode視窗的右邊側邊欄。如果你看不到,你需要點選上面工具欄中“Views”這一區中的最右邊的按鈕,來顯示右邊的側邊欄。

  右邊的側邊欄的下半部分有四個tabs,你可以點選相應的圖示來切換tab,我們現在要切換到 Object Library.

  從 Object Library 拖拽一個文字框(Text View)和一個圓角矩形按鈕(Round Rect Button)到我們的view上。按照自己的喜好把它們放好位置。在按鈕上加一個標題,例如“名言”。如果喜歡還可以改變一下控制元件的顏色和字型。你可以通過右邊側邊欄的上半部分改變控制元件大部分屬性,它也有好幾個可以切換的tab - 我們最常用的是用來定製控制元件外觀的 Attributes Inspector tab.

Storyboard layout for Objective-C tutorial

  因為這個文字框只是用來顯示,所以要取消 Behavior 中的 Editable 核取方塊.

  現在你需要連結這個按鈕和文字框到你之前在類中設定好的outlet和action。

  按著control鍵,滑鼠左鍵點選左邊側邊欄的View Controller,然後拖拽到中間的文字框,然後釋放滑鼠左鍵,會彈出一個彈出選單,選擇其中的“quoteText”.

Connecting the text view to the outlet

  另外的一種方式是,你可以先選中左邊欄的view controller,然後點選右邊欄上面圖示,切換tab到 Connections Inspector tab. 然後就可以看到你的View Controller的所有可用的連結,然後拖拽“quoteText”到中間文字框。

Connections Inspector

  請記住,Storyboard之所以知道你的quoteText屬性,是因為你之前新增的IBOutlet關鍵字!

  連結動作

  連結一個控制元件的動作(例如按鈕的點選)到一個方法,過程和之前連結控制元件到屬性的過程很類似。

  這次,按control鍵,拖拽View Controller中的按鈕, 釋放按鍵,從彈出選單選擇“quoteButtonTapped:” :

Hooking up an action to a method

  同樣有另外的方法,先選擇按鈕,跳轉右邊欄的tab頁到 Connections Inspector,你可以看到按鈕的所有事件,拖拽“Touch Up Inside”事件到View Controller,下來的步驟和上面的過程一樣。

  全速前進!

  猜怎麼了? 你準備好開始搖滾吧。只要在Xcode的“Run”按鈕上點選一下(在螢幕頂端的第一個按鈕)就開始編譯並且在模擬器上執行應用了。

  如果期間發生錯誤,不要緊張。在這個階段,這些錯誤的提示都是明確。常常是宣告變數是打錯字了。記住Xcode是大小寫敏感的。

  如果的應用通過了編譯,並且執行起來了。然後點選"Quote"按鈕獲取一個隨機的名言:

  好的 – 你有了個可以工作的應用,而且你已經學了很多關於Objective-C的知識 – 你已經學會建立屬性,方法,類,等等!

  但是等等 – 還有些不完美的地方! 現在你的名言的列表是硬編碼在應用中。如果你可以從外部檔案載入他們那該有多好阿?

  啊哈,屬性列表的精彩之處開始登場了!

  屬性列表規則

  屬性列表是一種特殊的 XML 格式,它由蘋果公司設計,用來儲存基本資料型別如字串、數字、陣列和字典。它建立容易,並以程式碼的形式讀寫,因此是在應用中引入少量資料的良好方式。
  讓我們嘗試一下!在左側欄你的專案 root 上(專案導航欄)右擊滑鼠建立,並選擇 New File 建立新檔案。然後選擇 iOS\Resource\Property List 模板並單擊 Next。選擇儲存路徑(通常是專案資料夾中的某處)並將檔案命名為 quotes.plist
  在 Xcode 中,你可以通過網格檢視(如同屬性的列表)或文字模式編輯屬性列表。如果想要以文字方式編輯,在專案導航欄中的 quotes 檔案上右擊滑鼠並選擇 Open As\Source Code。

  如果你希望通過複製貼上快速新增所有名言,以原始碼開啟或許是更快的方法。如果你想這樣,你可以嘗試使用網格式圖方式,並嘗試找出如果使用該方法新增與下面的值。現在,通過複製貼上下面程式碼到 quotes(用原始碼模式) 新增你的電影名言:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<!-- Quotes -->
<plist version="1.0">
    <array>
 
 
        <dict>
            <key>category</key>  <string>classic</string>
            <key>quote</key>  <string>Frankly my dear, I don't give a dam.</string>
            <key>source</key>  <string>Gone with the wind</string>
        </dict>
 
        <dict>
            <key>category</key>  <string>classic</string>
            <key>quote</key>  <string>Here's looking at you kid.</string>
            <key>source</key>  <string>Casablanca</string>
        </dict>
 
        <dict>
            <key>category</key>  <string>classic</string>
            <key>quote</key>  <string>There's no place like home.</string>
            <key>source</key>  <string>Wizard of Oz</string>
        </dict>
        <dict>
            <key>category</key>  <string>classic</string>
            <key>quote</key>  <string>Play it again sam.</string>
            <key>source</key>  <string></string>
        </dict>
        <dict>
            <key>category</key>  <string>classic</string>
            <key>quote</key>  <string>Elementary my dear Watson.</string>
            <key>source</key>  <string>Sherlock Holmes</string>
        </dict>
 
        <dict>
            <key>category</key>  <string>classic</string>
            <key>quote</key>  <string>Fasten your seatbelts. It's going to be a bumpy night.</string>
            <key>source</key>  <string>All about Eve</string>
        </dict>
 
        <dict>
            <key>category</key>  <string>classic</string>
            <key>quote</key>  <string>I have not the pleasure of understanding you.</string>
            <key>source</key>  <string>Pride and Predice</string>
        </dict>
 
        <dict>
            <key>category</key>  <string>classic</string>
            <key>quote</key>  <string>O Romeo, Romeo! wherefore art thou Romeo?</string>
            <key>source</key>  <string>Romeo and Juliet</string>
        </dict>
 
        <dict>
            <key>category</key>  <string>classic</string>
            <key>quote</key>  <string>To be or not to be</string>
            <key>source</key>  <string>Hamlet</string>
        </dict>
 
        <dict>
            <key>category</key>  <string>classic</string>
            <key>quote</key>  <string>...Crime is only a left-handed form of human endeavor.</string>
            <key>source</key>  <string>The Asphalt Jungle</string>
        </dict>
 
        <dict>
            <key>category</key>  <string>classic</string>
            <key>quote</key>  <string>That's, uh, quite a dress you almost have on...What holds it up?</string>
            <key>source</key>  <string>An American in Paris</string>
        </dict>
 
        <dict>
            <key>category</key>  <string>classic</string>
            <key>quote</key>  <string>Love, desire, ambition, faith - without them life is so simple, believe me</string>
            <key>source</key>  <string>Invasion of the Body Snatchers</string>
        </dict>
 
        <dict>
            <key>category</key>  <string>modern</string>
            <key>quote</key>  <string>Go ahead make my day.</string>
            <key>source</key>  <string>Dirty Harry</string>
        </dict>
 
        <dict>
            <key>category</key>  <string>modern</string>
            <key>quote</key>  <string>May the Force be with you.</string>
            <key>source</key>  <string>Star wars</string>
        </dict>
        <dict>
            <key>category</key>  <string>modern</string>
            <key>quote</key>  <string>Hasta la vista, baby</string>
            <key>source</key>  <string>Terminator</string>
        </dict>
        <dict>
            <key>category</key>  <string>modern</string>
            <key>quote</key>  <string>I feel the need for speed.</string>
            <key>source</key>  <string>Top Gun</string>
        </dict>
        <dict>
            <key>category</key>  <string>modern</string>
            <key>quote</key>  <string>She doesn't even go here.</string>
            <key>source</key>  <string>Mean Girls</string>
        </dict>
        <dict>
            <key>category</key>  <string>modern</string>
            <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>
            <key>source</key>  <string>Harry Potter</string>
        </dict>
        <dict>
            <key>category</key>  <string>modern</string>
            <key>quote</key>  <string>I solemnly swear that I am up to no good.</string>
            <key>source</key>  <string>Harry Potter</string>
        </dict>
 
        <dict>
            <key>category</key>  <string>modern</string>
            <key>quote</key>  <string>You like pain? Try wearing a corset.</string>
            <key>source</key>  <string>Pirates of the Carribean</string>
        </dict>
 
        <dict>
            <key>category</key>  <string>modern</string>
            <key>quote</key>  <string>Houston, we have a problem.</string>
            <key>source</key>  <string>Apollo 13</string>
        </dict>
 
        <dict>
            <key>category</key>  <string>modern</string>
            <key>quote</key>  <string>I'll be back.</string>
            <key>source</key>  <string>The Terminator</string>
        </dict>
        <dict>
            <key>category</key>  <string>modern</string>
            <key>quote</key>  <string>E.T. phone home.</string>
            <key>source</key>  <string>E.T.</string>
        </dict>
        <dict>
            <key>category</key>  <string>modern</string>
            <key>quote</key>  <string>Why are you trying so hard to fit in when you were born to stand out?</string>
            <key>source</key>  <string>What a girl wants</string>
        </dict>
 
        <dict>
            <key>category</key>  <string>modern</string>
            <key>quote</key>  <string>Watch you talkin about Willis?</string>
            <key>source</key>  <string>Different Strokes</string>
        </dict>
 
        <dict>
            <key>category</key>  <string>modern</string>
            <key>quote</key>  <string>The plane, the plane</string>
            <key>source</key>  <string>Fantasy Island</string>
        </dict>
        <dict>
            <key>category</key>  <string>modern</string>
            <key>quote</key>  <string>D'oh</string>
            <key>source</key>  <string>The Simpsons</string>
        </dict>
        <dict>
            <key>category</key>  <string>modern</string>
            <key>quote</key>  <string>Kids, you tried your best and you failed miserably. The lesson is, never try.</string>
            <key>source</key>  <string>The Simpsons</string>
        </dict>
 
        <dict>
            <key>category</key>  <string>modern</string>
            <key>quote</key>  <string>You don’t win friends with salad.</string>
            <key>source</key>  <string>The Simpsons</string>
        </dict>
 
        <dict>
            <key>category</key>  <string>modern</string>
            <key>quote</key>  <string>Whoever said that money doesn't buy happiness didn't know where to shop.</string>
            <key>source</key>  <string>Gossip Girl</string>
        </dict>
 
        <dict>
            <key>category</key>  <string>modern</string>
            <key>quote</key>  <string>If I were you, I'd accessorize with some gloves. Even a manicure can't mask those peasant hands.</string>
            <key>source</key>  <string>Gossip Girl</string>
        </dict>
 
        <dict>
            <key>category</key>  <string>modern</string>
            <key>quote</key>  <string>I tried to be diplomatic, but mostly I just lied a lot.</string>
            <key>source</key>  <string>Twilight</string>
        </dict>
 
 
        <dict>
            <key>category</key>  <string>modern</string>
            <key>quote</key>  <string>Once people start throwing wet stuff, I go inside.</string>
            <key>source</key>  <string>Twilight</string>
        </dict>
 
        <dict>
            <key>category</key>  <string>modern</string>
            <key>quote</key>  <string>I don’t like to lie – so there’d better be a good reason why I’m doing it..</string>
            <key>source</key>  <string>Twilight</string>
        </dict>
 
    </array>
</plist>

  這裡只有少量作為示例的名言。找點樂子,新增你喜歡的那些。如果你比較懶,示例專案中有一個包含很多名言的屬性列表

  名言被分類為 classic 或 modern 用來說明你以後會了解到的非常好的功能。

  你也可以切換到屬性列表檢視(Property List view) (在Project Navigator上滑鼠右鍵點選quotes檔案,選擇Open As\Property List) 看看你新增的值在網格檢視中如何顯示和組織的。現在你知道不同的編輯模式如何工作的了,你可以任意來回切換。

  屬性列表很酷,但是如果你出錯了,就不酷了。新人常犯的錯誤是忘記結束的tag或者偶然刪除掉< 或者 >. 如果你的屬性列表不能載入,那你就需要仔細查閱,找出來原因。Xcode早期的版本可以提示錯誤行號。我想從版本4之後這個有用的特性就去掉了。

  如果你被那個錯誤卡住了,你需要點技巧來查閱你的檔案。我用了個方法(坦白說,這個方法用的有點多)來簡化查閱:備份我的plist檔案,然後依次移除一點內容來定位大致的錯誤位置。

  建立完你可愛的屬性列表後,你要準備載入它到陣列開始使用。所以,讓我們切換回ViewController.m 然後新增下面的程式碼到viewDidLoad:結尾處:

// 2 - Load movie quotes
NSString *plistCatPath = [[NSBundle mainBundle] pathForResource:@"quotes" ofType:@"plist"];
self.movieQuotes= [NSMutableArray arrayWithContentsOfFile:plistCatPath];

  就這麼簡單 – 現在你把你之前輸入到屬性列表的所有電影名言載入到一個陣列裡了!

  要試用你的新陣列,你可能想你只要把電影名言陣列替換成你的名言陣列就可以了。所以,在quoteButtonTapped: 裡你簡單的替換所有movieQuotes引用成myQuotes, 對嗎?

  如果你試了你就會發現,但是隻這樣做是不行的。因為myQuotes是一個名言字串陣列,但是movieQuotes不是。它是一個字典陣列,字典是一列資料,每個資料可以通過唯一的鍵值(a unique key)來訪問。

為什麼?因為你設定了屬性列表導致的(再仔細看看就會明白了)

注意: 字典(Dictionaries) 是一個 key/value 容器, 類似於其他語言的雜湊表(hashtables)。你可以用valueForKey查詢字典裡每一項。

所以用下列的程式碼替換quoteButtonTapped,它替換成movieQuotes陣列,而且使用正確鍵值從每項名言字典裡獲取名言:

-(IBAction)quoteButtonTapped:(id)sender {
    // 1 - Get number of rows in array
    int array_tot = [self.movieQuotes count];
    // 2 - Get random index
    int index = (arc4random() % array_tot);
    // 3 - Get the quote string for the index
    //NSString *my_quote = [self.myQuotes objectAtIndex:index];
    NSString *my_quote = self.movieQuotes[index][@"quote"];
    // 4 - Display the quote in the text view
    self.quoteText.text = [NSString stringWithFormat:@"Quote:\n\n%@",  my_quote];
}

  為了後面方便,保持段#3的註釋行。編譯,執行,享受你新的電影名言吧!

Movie Quotes

  真棒,現在你有了個檔案可以從外部檔案讀取名言了。這非常方便,如果你希望其他人為你填寫一些名言。

  接下來,你準備玩一些花樣了。允許使用者選擇看 myQuotes 或者 movieQuotes, 和選擇看經典或者現代電影名言。

  選項,選項,選項

  首先你需要回到類的標頭檔案 ViewController.h 並新增一個新的屬性。

@property (nonatomic, strong) IBOutlet UISegmentedControl *quoteOpt;

  這裡你新增了一個連線到 Segmented Control 的屬性,這樣你就可以在選擇列表中選擇一個專案——這是選擇名言型別的最佳方式。
現在到 MainStoryboard.storyboard 頁面,並將一個 Segmented Control 空間拖到介面中。

新增一個 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

  這樣就獲得了三個不同名言型別的效果,或者更準確的說,是三選一的能力。

  建立好您的 Segmented Control 後, 您需要將它與您的類建立連結。 你可以使用之前相同的方式將控制元件連結到檢視控制器的 quoteOpt 屬性。

  這個控制元件不需要使用行為事件。

  為什麼您不生成並執行一下看看你螢幕上的新控制元件呢?目前它還不會有任何功能,但是看到它在那裡感覺很不錯!

Segmented control now appears

  謂詞的使用

  謂詞是一個可以用於過濾陣列的物件。它就像我們在 SQL 中 select 查詢的 where 子句。 我發現將它用於處理分類屬性表非常有用。這樣可以節省你不必建立單獨的屬性列表.

  別生我的氣,你得回到 ViewController.m 並修改 quoteButtonTapped: 使用 myQuotes 代替 movieQuotes,很快你就將為你的電影對白做一些完全不同的東西。您需要為它設定一個條件,所以你將在 Segmented Control 的第三項選中時使用它。

  或者如果你願意,只需使用以下程式碼替換 quoteButtonTapped:

-(IBAction)quoteButtonTapped:(id)sender {
    // 1 - 當第三段選中時獲取 quotes 
    if (self.quoteOpt.selectedSegmentIndex == 2) {
        // 1 - 獲取陣列大小
        int array_tot = [self.myQuotes count];
        // 2 - 獲取隨機索引
        int index = (arc4random() % array_tot);
        // 3 - 從索引處獲取 quote 字串
        NSString *my_quote = self.myQuotes[index];
        // 4 - 在文字檢視上顯示 quote 字串
        self.quoteText.text = [NSString stringWithFormat:@"Quote:\n\n%@",  my_quote];
    }
}

  現在當使用者選擇第三項時,它將只看到 myQuotes 。正如你會發現剩下的程式碼是和以前一樣,唯一的區別是隻在段索引2選中時顯示 quote(來源於 personal quote 列表的 quote) 您可能還記得,因為段控制元件在索引0開始,索引2表示的第三個專案。

  生成並測試你的程式碼確保它正常工作,只有選中“Mine”時顯示 guotes 。

  對謂詞的使用,首先你需要通過段控制元件當前選擇的段確定要用於陣列過濾的分類,一起來!

  這段是 quoteButtonTapped: if 段的後一段內容 – 所以在第一節之後 if 段後如下:

// 2 - 獲取電影 quotes
else {
    // 2.1 - 確定類別
    NSString *selectedCategory = @"classic";
    if (self.quoteOpt.selectedSegmentIndex == 1) {
        selectedCategory = @"modern";
    }
    // 2.2 - 使用謂詞通過分類過濾陣列
    NSPredicate *predicate = [NSPredicate predicateWithFormat:@"category == %@", selectedCategory];
    NSArray *filteredArray = [self.movieQuotes filteredArrayUsingPredicate:predicate];
    // 2.3 - 獲取過濾後的陣列大小
    int array_tot = [filteredArray count];
    // 2.4 - 作為保障只有當資料有內容時繼續
    if (array_tot > 0) {
        // 2.5 - 獲取隨機索引
        int index = (arc4random() % array_tot);
        // 2.6 - 獲取索引處的 quote 字串
        NSString *quote = filteredArray[index][@"quote"];
        self.quoteText.text = [NSString stringWithFormat:@"Movie Quote:\n\n%@",  quote];
    } else {
        self.quoteText.text = [NSString stringWithFormat:@"No quotes to display."];
    }
}

  好的,生成並執行。檢查,你看到的引述取決於你的選擇正確的型別。如果你總是得到相同的型別,我的猜測是,你可能沒有將 Segmented Control 連結到你的類。

  字串 交響樂

  到目前位置一切都很好! 現在讓我們探索Objective-C中一些不同的字串選項和語法吧。

  如果這個名言在屬性列表中有一個來源(source), 那麼我們的應用也應該顯示它。檢查字串是否包含值,可以檢查字串的長度。

  所以在quoteButtonTapped:的section #2.6第一行之後新增下面的程式碼(第一行不包括註釋行):

// 2.7 - Check if there is a source    
NSString *source = [[filteredArray objectAtIndex:index] valueForKey:@"source"];
if (![source length] == 0) {
    quote = [NSString stringWithFormat:@"%@\n\n(%@)",  quote, source];
}
// 2.8 - Set display string

  同樣,註釋掉這一行,你不在需要它了:

//self.quoteText.text = [NSString stringWithFormat:@"Movie Quote:\n\n%@",  quote];

  你從陣列中獲得來源(source)然後檢查它的長度不為0來確認它是否包含有效值。 ! 代表 NOT. 當檢查整數是否相等時使用 == 。

  然後你用stringWithFormat 把名言和來源連線起來,構建一個新的字串來顯示。

  為了更有趣些,為什麼你把來自經典電影的名言顯示的稍微不一樣些?這需要檢查選擇的名言歸屬那個類別。

  用下面的程式碼替換quoteButtonTapped:中的段 #2.8:

// 2.8 - Customize quote based on category
if ([selectedCategory isEqualToString:@"classic"]) {
    quote = [NSString stringWithFormat:@"From Classic Movie\n\n%@",  quote];
} else {
    quote = [NSString stringWithFormat:@"Movie Quote:\n\n%@",  quote];
}
// 2.9 - Display quote
self.quoteText.text = quote;

  這裡檢查是否字串等於特定的值“classis”,然後特殊顯示這個類別裡的label。

  如果你想檢查一個電影的標題(或者其他任意的字串)以特定的值開始。你也可以做到。如果你想特殊顯示一個來自哈利波特電影的的名言 - 新增下面的程式碼在段 #2.9之上:

if ([source hasPrefix:@"Harry"]) { quote = [NSString stringWithFormat:@"HARRY ROCKS!!\n\n%@",  quote]; }

  就像你猜測的那樣, hasPrefix 用來檢測是否一個字串的字首包含特定的文字值。

  編譯並執行你的應用,看它是否工作正常。特別注意一下來自不同的類別和來自哈利波特電影的名言是否正確顯示。

 

  這是陣列名言

  為了好玩,你可以把你的所有名言串接起來,就好像他們是一個。這會演示如何陣列迴圈,這在你想遍歷處理陣列每一個元素是很有用。

  在 quoteButtonTapped: 中用下面的程式碼替換段#1的程式碼:

if (self.quoteOpt.selectedSegmentIndex == 2) {
    // 1.1 - Get array count
    int array_tot = [self.myQuotes count];
    // 1.2 - Initialize string for concatenated quotes
    NSString *all_my_quotes = @"";
    NSString *my_quote = nil;
    // 1.3 - Iterate through array
    for (int x=0; x < array_tot; x++) {
        my_quote = self.myQuotes[x];
        all_my_quotes = [NSString stringWithFormat:@"%@\n%@\n",  all_my_quotes,my_quote];
    }
    self.quoteText.text = [NSString stringWithFormat:@"%@", all_my_quotes];
}

  一個for迴圈用於遍歷陣列第0行(row 0)到最後一行。x是跟蹤行數的計數器。

  現在執行檢視結果。

 

  最後一件事。我之前提過有兩種不同的陣列: NSArrayNSMutableArray. 到現在為止,這兩個型別在這個專案功效一樣。

  如果你想更新/修改陣列,可以使用NSMutableArray。就像它的名字所說,一個可變(mutable)陣列可以修改,但一個普通NSArray是不可變的,你不能新增或刪除陣列項。

  例如,在一行選中後為了表明這個名言已經顯示過了,你想更新陣列,你需要使用 NSMutableArray.

  在這個專案, movieQuotes 是你的 NSMutableArray. 你使用一個謂詞(predicate), 所以你首先需要找到這一行然後更新它。在quoteButtonTapped:的段#2.9新增下面的程式碼:

// 2.10 - Update row to indicate that it has been displayed
int movie_array_tot = [self.movieQuotes count];
NSString *quote1 = filteredArray[index][@"quote"];
for (int x=0; x < movie_array_tot; x++) {
    NSString *quote2 = self.movieQuotes[x][@"quote"];
    if ([quote1 isEqualToString:quote2]) {
        NSMutableDictionary *itemAtIndex = (NSMutableDictionary *)self.movieQuotes[x];
        itemAtIndex[@"source"] = @"DONE";
    }
}

 

  你的迴圈遍歷這個陣列,檢查每一行看是否是你查詢的行。再一次用到 isEqualToString; 但是這次是比較兩個字串變數。

  為了更新陣列中的這一行,你獲取這一行,然後更新物件。這超過了這個教程的知識範圍一點點,但你提前瞭解一點也很有好。

  因為你更行了source,而且source是為每個類別來選擇名言的。所以下次你用NSPredicate過濾陣列是,這一行就不會被包括在結果中了。相當整潔。

  下面需要學習什麼?

  這裡是 例項工程檔案 包含我們上面教程的所有程式碼。

  好,你已經完成了這個小工程。你已經學會了不同方法使用陣列,從介面事件出發動作,在Interface Builder中使用XIB檔案訪問檔案和做各種字串連線和比較。作為第一個iPhone應用,這個成果還不錯!

  現在你具備了基礎知識,你可以開始試試我們“如何寫一個簡單iPhone應用“( How To Create a Simple iPhone App)系列教程了。或者訂閱我們的月度簡報(sign up for our monthly newsletter)獲得為初學者寫的長篇iOS教程。

  如果你有任何問題,可以使用這個論壇。同樣,如果你喜歡這個教程而且想看更多這個系列的,請在論壇上告訴我!

  同時,祝你好運,生活快樂! :]

  英文原文:Objectively Speaking: A Crash Course in Objective C for iOS 6

相關文章