鍍金池/ 教程/ iOS/ 添加數(shù)據(jù)
查找信息
使用設(shè)計(jì)模式
設(shè)計(jì)用戶界面
應(yīng)用程序開發(fā)過(guò)程
iOS 技術(shù)
接下來(lái)做什么
串聯(lián)圖
處理 Foundation
編寫自定類
整合數(shù)據(jù)
基礎(chǔ)
設(shè)置
添加數(shù)據(jù)
定義交互

添加數(shù)據(jù)

本教程以第二個(gè)教程(“教程:串聯(lián)圖”)中創(chuàng)建的項(xiàng)目為基礎(chǔ)。您將用到從使用設(shè)計(jì)模式、使用 Foundation 以及編寫自定類中學(xué)到的知識(shí),在 ToDoList 應(yīng)用程序中添加對(duì)動(dòng)態(tài)數(shù)據(jù)的支持。

本教程講述了以下操作:

  • 使用常見的 Foundation 類
  • 創(chuàng)建自定數(shù)據(jù)類
  • 實(shí)現(xiàn)委托和數(shù)據(jù)源協(xié)議
  • 在視圖控制器之間傳遞數(shù)據(jù)

完成本教程中的所有步驟后,您的應(yīng)用程序外觀大致是這樣的:

http://wiki.jikexueyuan.com/project/ios-developer-library/images/ios_simulator_add_new_item_2x.png" alt="" />

創(chuàng)建數(shù)據(jù)類

現(xiàn)在就開始吧,請(qǐng)?jiān)?Xcode 中打開您的現(xiàn)有項(xiàng)目。

目前,使用串聯(lián)圖的 ToDoList 應(yīng)用程序有一個(gè)界面和一個(gè)導(dǎo)航方案?,F(xiàn)在,是時(shí)候使用模型對(duì)象來(lái)添加數(shù)據(jù)儲(chǔ)存和行為了。

應(yīng)用程序的目標(biāo)在于創(chuàng)建一個(gè)待辦事項(xiàng)列表,因此首先您將創(chuàng)建一個(gè)自定類 XYZToDoItem 來(lái)表示單個(gè)待辦事項(xiàng)。您應(yīng)該記得,XYZToDoItem 類已經(jīng)在編寫自定類中討論過(guò)。

創(chuàng)建 XYZToDoItem 類

  • 選取“File”>“New”>“File”(或按下 Command-N)。
  • 這時(shí)將會(huì)出現(xiàn)一個(gè)對(duì)話框,提示您為新文件選取模板。
  • 從左側(cè)的 iOS 下方選擇“Cocoa Touch”。
  • 選擇“Objective-C Class”,并點(diǎn)按“Next”。
  • 在“Class”欄中,在 XYZ 前綴后鍵入 ToDoItem。
  • 從“Subclass of”彈出式菜單中選取“NSObject”。
  • 如果您完全按照本教程操作,那么在這個(gè)步驟之前,“Class”標(biāo)題可能是 XYZToDoItemViewController。選取 NSObject 作為“Subclass of”后,Xcode 會(huì)知道您創(chuàng)建了一個(gè)正常的自定類,并移除了它先前添加的ViewController 文本。
  • 點(diǎn)按“Next”。
  • 存儲(chǔ)位置默認(rèn)為您的項(xiàng)目目錄。此處無(wú)需更改。
  • “Group”選項(xiàng)默認(rèn)為您的應(yīng)用程序名稱“ToDoList”。此處無(wú)需更改。
  • “Targets”部分默認(rèn)選定您的應(yīng)用程序,未選定應(yīng)用程序的測(cè)試。好極了,這些都無(wú)需更改。
  • 點(diǎn)按“Create”。

XYZToDoItem 類很容易實(shí)現(xiàn)。它具有項(xiàng)目名稱、創(chuàng)建日期,以及該項(xiàng)目是否已完成等屬性。繼續(xù)將這些屬性添加到 XYZToDoItem 類接口。

配置 XYZToDoItem 類

在項(xiàng)目導(dǎo)航器中,選擇 XYZToDoItem.h。 將以下屬性添加到該接口,使聲明如下所示:

@interface XYZToDoItem : NSObject

@property NSString *itemName;
@property BOOL completed;
@property (readonly) NSDate *creationDate;

@end

檢查點(diǎn):通過(guò)選取“Product”>“Build”(或按下 Command-B)來(lái)生成項(xiàng)目。盡管該新類尚未實(shí)現(xiàn)任何功能,但是生成它有助于編譯器驗(yàn)證任何拼寫錯(cuò)誤。如果發(fā)現(xiàn)錯(cuò)誤,請(qǐng)及時(shí)修正:通讀編輯器提供的警告或錯(cuò)誤,然后回顧本教程中的說(shuō)明,確保所有內(nèi)容與此處的描述相符。

載入數(shù)據(jù)

您現(xiàn)在有一個(gè)類,可以用它作為基礎(chǔ)來(lái)為單個(gè)列表項(xiàng)目創(chuàng)建并儲(chǔ)存數(shù)據(jù)。您還需要保留一個(gè)項(xiàng)目列表。在 XYZToDoListViewController 類中跟蹤此內(nèi)容較為合適,視圖控制器負(fù)責(zé)協(xié)調(diào)模型和視圖,所以需要對(duì)模型進(jìn)行引用。

Foundation 框架有一個(gè) NSMutableArray 類,很適合跟蹤項(xiàng)目列表。此處必須使用可變數(shù)組,這樣用戶就可以將項(xiàng)目添加到數(shù)組。因?yàn)椴豢勺償?shù)組 NSArray 在其初始化后將不允許添加項(xiàng)目。

要使用數(shù)組,您需要聲明并創(chuàng)建它??梢酝ㄟ^(guò)分配并初始化數(shù)組來(lái)完成。

要分配并初始化數(shù)組

1.在項(xiàng)目導(dǎo)航器中,選擇 XYZToDoListViewController.m。

由于項(xiàng)目數(shù)組是表格視圖控制器的實(shí)現(xiàn)細(xì)節(jié),所以應(yīng)該在 .m 文件中進(jìn)行聲明,而不是 .h 文件。此操作可讓項(xiàng)目數(shù)組成為您自定類的私有數(shù)組。

2.將以下屬性添加到接口類別中,它是由 Xcode 在您的自定表格視圖控制器類中創(chuàng)建的。聲明應(yīng)該是這樣的:

@interface XYZToDoListViewController ()

@property NSMutableArray *toDoItems;

@end

3.在 viewDidLoad 方法中分配并初始化 toDoItems 數(shù)組:

- (void)viewDidLoad
{
    [super viewDidLoad];
    self.toDoItems = [[NSMutableArray alloc] init];
}

viewDidLoad 的實(shí)際代碼中有一些附加行被注釋掉了,那些行是 Xcode 創(chuàng)建 XYZListViewController 時(shí)插入的。保留與否都沒(méi)有影響。

現(xiàn)在,您已經(jīng)擁有了一個(gè)可以添加項(xiàng)目的數(shù)組。添加項(xiàng)目將在單獨(dú)的方法 loadInitialData 中進(jìn)行,并將通過(guò) viewDidLoad 調(diào)用該方法。由于此代碼是一個(gè)模塊化任務(wù),所以會(huì)進(jìn)入其自身的方法中。當(dāng)然您也可以將方法分離出來(lái),從而提高代碼的可讀性。在真正的應(yīng)用程序中,此方法可能會(huì)從某種永久儲(chǔ)存形式載入數(shù)據(jù),例如文件?,F(xiàn)在,我們的目標(biāo)是了解表格視圖如何處理自定數(shù)據(jù)項(xiàng)目,那么讓我們創(chuàng)建一些測(cè)試數(shù)據(jù)來(lái)體驗(yàn)一下吧。

以創(chuàng)建數(shù)組的方式創(chuàng)建項(xiàng)目:分配并初始化。然后,給項(xiàng)目命名。該名稱將顯示在表格視圖中。按照此方法創(chuàng)建一組項(xiàng)目。

載入初始數(shù)據(jù)

在 @implementation 行下方,添加一個(gè)新方法 loadInitialData。

- (void)loadInitialData {
}

在此方法中,創(chuàng)建幾個(gè)列表項(xiàng)目,并將它們添加到數(shù)組。

- (void)loadInitialData {
    XYZToDoItem *item1 = [[XYZToDoItem alloc] init];
    item1.itemName = @"Buy milk";
    [self.toDoItems addObject:item1];
    XYZToDoItem *item2 = [[XYZToDoItem alloc] init];
    item2.itemName = @"Buy eggs";
    [self.toDoItems addObject:item2];
    XYZToDoItem *item3 = [[XYZToDoItem alloc] init];
    item3.itemName = @"Read a book";
    [self.toDoItems addObject:item3];
}

在 viewDidLoad 方法中調(diào)用 loadInitialData。

- (void)viewDidLoad
{
    [super viewDidLoad];
    self.toDoItems = [[NSMutableArray alloc] init];
    [self loadInitialData];
}

檢查點(diǎn):通過(guò)選取“Product”>“Build”來(lái)生成項(xiàng)目。您應(yīng)該會(huì)在 loadInitialData 方法的代碼行上看到大量錯(cuò)誤。第一行是出錯(cuò)的關(guān)鍵所在,錯(cuò)誤提示應(yīng)該是“Use of undeclared identifier XYZToDoItem”。這說(shuō)明編譯器在編譯 XYZToDoListViewController 時(shí)不知道 XYZToDoItem。編譯器比較特別,您需要明確告知它應(yīng)當(dāng)注意什么。

讓編譯器注意您的自定列表項(xiàng)目類

在 XYZToDoListViewController.m 文件的頂部附近找到 #import "XYZToDoListViewController.h" 行。 緊接著在其下方添加以下行:

#import "XYZToDoItem.h"

檢查點(diǎn):通過(guò)選取“Product”>“Build”來(lái)生成項(xiàng)目。項(xiàng)目生成時(shí)應(yīng)該沒(méi)有錯(cuò)誤。

顯示數(shù)據(jù)

目前,表格視圖具有一個(gè)可變數(shù)組,預(yù)填充了幾個(gè)示例待辦事項(xiàng)?,F(xiàn)在您需要在表格視圖中顯示數(shù)據(jù)。

通過(guò)讓 XYZToDoListViewController 成為表格視圖的數(shù)據(jù)源,可以實(shí)現(xiàn)這一點(diǎn)。無(wú)論要讓什么成為表格視圖的數(shù)據(jù)源,都需要實(shí)施 UITableViewDataSource 協(xié)議。需要實(shí)施的方法正是您在第二個(gè)教程中注釋掉的那些。創(chuàng)建有效的表格視圖需要三個(gè)方法。第一個(gè)方法是 numberOfSectionsInTableView:,它告訴表格視圖要顯示幾個(gè)部分。對(duì)于此應(yīng)用程序,表格視圖只需要顯示一個(gè)部分,所以實(shí)現(xiàn)比較簡(jiǎn)單。

在表格中顯示一個(gè)部分

  • 在項(xiàng)目導(dǎo)航器中,選擇 XYZToDoListViewController.m。
  • 如果您在第二個(gè)教程中注釋掉了表格視圖數(shù)據(jù)源方法,現(xiàn)在請(qǐng)移除那些注釋標(biāo)記。
  • 模板實(shí)現(xiàn)的部分如下所示。
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
#warning Potentially incomplete method implementation.
    // Return the number of sections.
    return 0;
}

您想要單個(gè)部分,所以需要移除警告行并將返回值由 0 更改為 1。

  • 更改 numberOfSectionsInTableView: 數(shù)據(jù)源方法以便返回單個(gè)部分,像這樣:
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
    // Return the number of sections.
    return 1;
}

下一個(gè)方法 tableView:numberOfRowsInSection: 告訴表格視圖要在給定部分中顯示幾行?,F(xiàn)在表格中有一個(gè)部分,并且每個(gè)待辦事項(xiàng)在表格視圖中都應(yīng)該有它自己的行。這意味著行數(shù)應(yīng)該等于 toDoItems 數(shù)組中的 XYZToDoItem 對(duì)象數(shù)。

返回表格中的行數(shù)

在項(xiàng)目導(dǎo)航器中,選擇 XYZToDoListViewController.m。

您可看到模板實(shí)現(xiàn)的部分是這樣的:

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
#warning Incomplete method implementation.
    // Return the number of rows in the section.
    return 0;
}

您想要返回所擁有的列表項(xiàng)目的數(shù)量。幸運(yùn)的是,NSArray 有一個(gè)很方便的方法,稱為 count,它會(huì)返回?cái)?shù)組中的項(xiàng)目數(shù),因此行數(shù)是 [self.toDoItems count]。

更改 tableView:numberOfRowsInSection: 數(shù)據(jù)源方法,使其返回正確的行數(shù)。

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    // Return the number of rows in the section.
    return [self.toDoItems count];
}

最后一個(gè)方法,tableView:cellForRowAtIndexPath: 請(qǐng)求一個(gè)單元格來(lái)顯示給定行。到現(xiàn)在為止,您只是處理了代碼,但是界面的絕大部分是針對(duì)行顯示的單元格。幸運(yùn)的是,Xcode 可讓您輕松地在 Interface Builder 中設(shè)計(jì)自定單元格。首個(gè)任務(wù)是設(shè)計(jì)您的單元格,并告訴表格視圖不要使用靜態(tài)內(nèi)容,而要使用具有動(dòng)態(tài)內(nèi)容的原型單元格。

配置表格視圖

  • 打開串聯(lián)圖。
  • 在大綱中選擇表格視圖。
  • 選定表格視圖后,在實(shí)用工具區(qū)域中打開“Attributes”檢查器 http://wiki.jikexueyuan.com/project/ios-developer-library/images/inspector_attributes_2x.png" alt="" />。
  • 在“Attributes”檢查器中,將表格視圖的“Content”屬性從“Static Cells”更改為“Dynamic Prototypes”。

Interface Builder 會(huì)采用您配置的靜態(tài)單元格,并將它們?nèi)哭D(zhuǎn)換為原型。原型單元格,顧名思義,是使用您要顯示的文本樣式、顏色、圖像或其他屬性進(jìn)行配置,并在運(yùn)行時(shí)從數(shù)據(jù)源獲取其數(shù)據(jù)的單元格。數(shù)據(jù)源會(huì)為每一行載入一個(gè)原型單元格,然后配置該單元格來(lái)顯示該行的數(shù)據(jù)。

要載入正確的單元格,數(shù)據(jù)源需要知道單元格的名稱,并且該名稱也必須在串聯(lián)圖中進(jìn)行配置。

設(shè)定原型單元格名稱時(shí),也將配置另一個(gè)屬性—單元格選擇樣式,該樣式用于確定用戶輕按單元格時(shí)單元格的外觀。將單元格選擇樣式設(shè)定為“None”,使用戶輕按單元格時(shí)單元格不會(huì)高亮顯示。這是當(dāng)用戶輕按待辦事項(xiàng)列表中的項(xiàng)目,將其標(biāo)記為已完成或未完成時(shí),您想要單元格呈現(xiàn)的行為。稍后會(huì)在本教程中實(shí)現(xiàn)該功能。

配置原型單元格

  • 在表格中選擇第一個(gè)表格視圖單元格。
  • 在“Attributes”檢查器中,找到“Identifier”欄并鍵入 ListPrototypeCell。
  • 在“Attributes”檢查器中,找到“Selection”欄并選取“None”。

您也可以更改原型單元格的字體或其他屬性?;九渲煤苋菀淄瓿?,您可以輕松記下。

下一步是實(shí)現(xiàn) tableView:cellForRowAtIndexPath: 方法,讓數(shù)據(jù)源為給定行配置單元格。表格視圖在想要顯示給定行時(shí)會(huì)調(diào)用此數(shù)據(jù)源方法。對(duì)于行數(shù)較少的表格視圖,所有行可能會(huì)同時(shí)出現(xiàn)在屏幕上,所以表格中的每一行都會(huì)調(diào)用此方法。但是,行數(shù)很多的表格視圖在給定時(shí)間內(nèi)只會(huì)顯示全部項(xiàng)目中的一小部分。最有效的方式是讓表格視圖僅請(qǐng)求要顯示行的單元格,而這一點(diǎn)可通過(guò) tableView:cellForRowAtIndexPath: 讓表格視圖實(shí)現(xiàn)。

對(duì)于表格中的任何給定行,取回 toDoItems 數(shù)組中的相應(yīng)條目,然后將單元格的文本標(biāo)簽設(shè)定為項(xiàng)目的名稱。

在表格中顯示單元格

在項(xiàng)目導(dǎo)航器中,選擇 XYZToDoListViewController.m。

找到 tableView:cellForRowAtIndexPath: 數(shù)據(jù)源方法。模板實(shí)現(xiàn)是這樣的:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *CellIdentifier = @"Cell";
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];

    // Configure the cell...

    return cell;
}

該模板執(zhí)行多個(gè)任務(wù)。它會(huì)創(chuàng)建一個(gè)變量來(lái)保存單元格的標(biāo)識(shí)符,向表格視圖請(qǐng)求具有該標(biāo)識(shí)符的單元格,添加一個(gè)注釋注明配置該單元格的代碼應(yīng)該寫在哪里,然后返回該單元格。

要讓此代碼為您的應(yīng)用程序所用,需要將標(biāo)識(shí)符更改為您在串聯(lián)圖中設(shè)定的標(biāo)識(shí)符,然后添加代碼來(lái)配置該單元格。

將單元格標(biāo)識(shí)符更改為您在串聯(lián)圖中設(shè)定的標(biāo)識(shí)符。為了避免拼寫錯(cuò)誤,請(qǐng)將串聯(lián)圖中的標(biāo)識(shí)符拷貝并粘貼到實(shí)現(xiàn)文件中。該單元格標(biāo)識(shí)符行現(xiàn)在應(yīng)該是這樣的:

static NSString *CellIdentifier = @"ListPrototypeCell";

在 return 語(yǔ)句前,添加以下代碼行:

XYZToDoItem *toDoItem = [self.toDoItems objectAtIndex:indexPath.row];
cell.textLabel.text = toDoItem.itemName;

您的 tableView:cellForRowAtIndexPath: 方法應(yīng)如下圖所示:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *CellIdentifier = @"ListPrototypeCell";
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
    XYZToDoItem *toDoItem = [self.toDoItems objectAtIndex:indexPath.row];
    cell.textLabel.text = toDoItem.itemName;
    return cell;
}

檢查點(diǎn):運(yùn)行您的應(yīng)用程序。您在 loadInitialData 中添加的項(xiàng)目列表應(yīng)該在表格視圖中顯示為單元格。

將項(xiàng)目標(biāo)記為已完成

如果無(wú)法將待辦事項(xiàng)列表中的項(xiàng)目標(biāo)記為已完成,那么這個(gè)待辦事項(xiàng)列表還不夠好?,F(xiàn)在,讓我們來(lái)添加這樣的支持。一個(gè)簡(jiǎn)單的界面應(yīng)該可以在用戶輕按單元格時(shí)切換完成狀態(tài),并在已完成的項(xiàng)目旁邊顯示勾號(hào)。幸運(yùn)的是,表格視圖附帶了一些內(nèi)建行為,您可以利用這些行為來(lái)實(shí)現(xiàn)這樣的簡(jiǎn)單界面。需要注意的是,在用戶輕按單元格時(shí),表格視圖要通知它們的委托。所以我們的任務(wù)是寫一段代碼,對(duì)用戶輕按表格中的待辦事項(xiàng)這個(gè)操作作出響應(yīng)。

您在串聯(lián)圖中配置 XYZToDoListViewController 時(shí),Xcode 就已經(jīng)讓它成為表格視圖的委托了。您要做的只是實(shí)現(xiàn) tableView:didSelectRowAtIndexPath: 委托方法,使其響應(yīng)用戶輕按,并在適當(dāng)時(shí)候更新您的待辦事項(xiàng)列表。

選定單元格后,表格視圖會(huì)調(diào)用 tableView:didSelectRowAtIndexPath: 委托方法,來(lái)查看它應(yīng)如何處理選擇操作。在此方法中,您需要編寫代碼來(lái)更新待辦項(xiàng)目的完成狀態(tài)。

將項(xiàng)目標(biāo)記為已完成或未完成

在項(xiàng)目導(dǎo)航器中,選擇 XYZToDoListViewController.m。

將以下代碼行添加到文件末尾,在 @end 行正上方:

#pragma mark - Table view delegate

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{

}

請(qǐng)?jiān)囍I入第二行,而不是拷貝和粘貼。您將發(fā)現(xiàn)代碼補(bǔ)全是 Xcode 最節(jié)省時(shí)間的功能之一。當(dāng) Xcode 顯示出可能的補(bǔ)全建議列表時(shí),請(qǐng)滾動(dòng)瀏覽列表,找到想要的建議,然后按下 Return 鍵。Xcode 會(huì)為您插入整行。

您想要響應(yīng)輕按,但并不想讓單元格保持選定狀態(tài)。添加以下代碼,讓單元格在選定后立即取消選定:

[tableView deselectRowAtIndexPath:indexPath animated:NO];

在 toDoItems 數(shù)組中搜索相應(yīng)的 XYZToDoItem。

XYZToDoItem *tappedItem = [self.toDoItems objectAtIndex:indexPath.row];

切換被輕按項(xiàng)目的完成狀態(tài)。

tappedItem.completed = !tappedItem.completed;

告訴表格視圖重新載入您剛更新過(guò)數(shù)據(jù)的行。

[tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationNone];

您的 tableView:didSelectRowAtIndexPath: 方法應(yīng)如下圖所示:

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    [tableView deselectRowAtIndexPath:indexPath animated:NO];
    XYZToDoItem *tappedItem = [self.toDoItems objectAtIndex:indexPath.row];
    tappedItem.completed = !tappedItem.completed;
    [tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationNone];
}

檢查點(diǎn):運(yùn)行您的應(yīng)用程序。您在 loadInitialData 中添加的項(xiàng)目列表在表格視圖中顯示為單元格。但當(dāng)您輕按項(xiàng)目時(shí),并沒(méi)有任何反應(yīng)。為什么呢?

原因是您尚未配置顯示項(xiàng)目完成狀態(tài)的表格視圖單元格。要實(shí)現(xiàn)此功能,您需要回到 tableView:cellForRowAtIndexPath: 方法,并配置單元格以在項(xiàng)目完成時(shí)顯示指示。

指示項(xiàng)目已完成的一種方式是在其旁邊放置一個(gè)勾號(hào)。幸運(yùn)的是,表格視圖右邊可以有一個(gè)單元格附屬物。默認(rèn)情況下,單元格中沒(méi)有任何附屬物;不過(guò)您可以進(jìn)行更改,使其顯示不同的附屬物。其中的一個(gè)附屬物就是勾號(hào)。您要做的是根據(jù)待辦事項(xiàng)的完成狀態(tài),設(shè)定單元格的附屬物。

顯示項(xiàng)目的完成狀態(tài)

前往 tableView:cellForRowAtIndexPath: 方法。

在設(shè)定單元格的文本標(biāo)簽的代碼行下方添加以下代碼:

if (toDoItem.completed) {
    cell.accessoryType = UITableViewCellAccessoryCheckmark;
} else {
    cell.accessoryType = UITableViewCellAccessoryNone;
}

您的 tableView:cellForRowAtIndexPath: 方法現(xiàn)在應(yīng)如下圖所示:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *CellIdentifier = @"ListPrototypeCell";
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
    XYZToDoItem *toDoItem = [self.toDoItems objectAtIndex:indexPath.row];
    cell.textLabel.text = toDoItem.itemName;
    if (toDoItem.completed) {
        cell.accessoryType = UITableViewCellAccessoryCheckmark;
    } else {
        cell.accessoryType = UITableViewCellAccessoryNone;
    }
    return cell;
}

檢查點(diǎn):運(yùn)行應(yīng)用程序。您在 loadInitialData 中添加的項(xiàng)目列表在表格視圖中顯示為單元格。輕按項(xiàng)目時(shí),其旁邊應(yīng)該出現(xiàn)一個(gè)勾號(hào)。如果您再次輕按同一項(xiàng)目,勾號(hào)會(huì)消失。

添加新項(xiàng)目

構(gòu)建待辦事項(xiàng)列表應(yīng)用程序功能的最后一步是實(shí)現(xiàn)添加項(xiàng)目的能力。當(dāng)用戶在 XYZAddToDoItemViewController 場(chǎng)景的文本欄中輸入項(xiàng)目名稱,并輕按“Done”按鈕時(shí),您想要視圖控制器創(chuàng)建一個(gè)新的列表項(xiàng)目并將其傳遞回 XYZToDoListViewController,以顯示在待辦事項(xiàng)列表中。

首先,您需要擁有一個(gè)列表項(xiàng)目來(lái)進(jìn)行配置。就像表格視圖那樣,視圖控制器是將界面連接到模型的邏輯位置。為 XYZAddToDoItemViewController 添加一個(gè)屬性來(lái)保存新的待辦事項(xiàng)。

將 XYZToDoItem 添加到 XYZAddToDoItemViewController 類

在項(xiàng)目導(dǎo)航器中,選擇 XYZAddToDoItemViewController.h。

由于稍后需要從表格視圖控制器訪問(wèn)列表項(xiàng)目,所以務(wù)必將其設(shè)為公共屬性。這就是為什么要在接口文件 XYZAddToDoItemViewController.h 中聲明它,而不在實(shí)現(xiàn)文件 XYZAddToDoItemViewController.m 中聲明的原因所在。

將 import 聲明添加到 @interface 行上方的 XYZToDoItem 類中。

#import "XYZToDoItem.h"

將 toDoItem 屬性添加到該接口。

@interface XYZAddToDoItemViewController : UIViewController

@property XYZToDoItem *toDoItem;

@end

要獲得新項(xiàng)目的名稱,視圖控制器需要訪問(wèn)用戶輸入名稱的文本欄。要實(shí)現(xiàn)此功能,請(qǐng)創(chuàng)建從 XYZAddToDoItemViewController 類到串聯(lián)圖中的文本欄的連接。

將文本欄連接到視圖控制器

在大綱視圖中,選擇 XYZAddToDoItemViewController 對(duì)象。

點(diǎn)按窗口工具欄右上角的“Assistant”按鈕,打開輔助編輯器。

http://wiki.jikexueyuan.com/project/ios-developer-library/images/assistant_editor_2x.png" alt="" />

右邊的編輯器出現(xiàn)時(shí),應(yīng)該顯示有 XYZAddToDoItemViewController.m。如果未顯示,請(qǐng)點(diǎn)按右邊的編輯器中的文件名,并選取 XYZAddToDoItemViewController.m。

輔助編輯器可讓您一次打開兩個(gè)文件,以便能夠在它們之間執(zhí)行操作。例如,在源文件中鍵入一個(gè)屬性,而源文件的對(duì)象又在接口文件中。

在串聯(lián)圖中選擇文本欄。

按住 Control 鍵從畫布上的文本欄拖到右邊編輯器中的代碼顯示窗口,到達(dá) XYZAddToDoItemViewController.m 中的 @interface 行正下方時(shí)停止拖移。

http://wiki.jikexueyuan.com/project/ios-developer-library/images/assistant_editor_drag_2x.png" alt="" />

在出現(xiàn)的對(duì)話框中,為“Name”欄鍵入“textField”。 讓選項(xiàng)的其余部分保持不變。您的對(duì)話框應(yīng)如下圖所示:

http://wiki.jikexueyuan.com/project/ios-developer-library/images/configure_text_field_outlet_2x.png" alt="" />

點(diǎn)按“Connect”。

Xcode 會(huì)將必要的代碼添加到 XYZAddToDoItemViewController.m,用于儲(chǔ)存指向文本欄的指針,并配置串聯(lián)圖來(lái)設(shè)置該連接。

另外,您需要知道何時(shí)創(chuàng)建項(xiàng)目。如果想要僅在“Done”按鈕被輕按時(shí)才創(chuàng)建項(xiàng)目,那么請(qǐng)將“Done”按鈕添加為 Outlet。

將“Done”按鈕連接到視圖控制器

在串聯(lián)圖中,打開輔助編輯器,將最右邊的窗口設(shè)定為 XYZAddToDoItemViewController.m。

在串聯(lián)圖中選擇“Done”按鈕。

按住 Control 鍵從畫布上的“Done”按鈕拖到右邊的編輯器中的代碼顯示窗口,到達(dá) XYZAddToDoItemViewController.m 中的 textField 屬性正下方的行時(shí)停止拖移。

在出現(xiàn)的對(duì)話框中,在“Name”欄鍵入“doneButton”。

保持選項(xiàng)的其余部分不變。對(duì)話框應(yīng)如下圖所示:

http://wiki.jikexueyuan.com/project/ios-developer-library/images/configure_done_button_outlet_2x.png" alt="" />

點(diǎn)按“Connect”。

您現(xiàn)在有了識(shí)別“Done”按鈕的方式。由于想在輕按“Done”按鈕時(shí)創(chuàng)建一個(gè)項(xiàng)目,所以需要知道該按鈕何時(shí)被按下。

當(dāng)用戶輕按“Done”按鈕時(shí),它會(huì)啟動(dòng)一個(gè) unwind segue,返回到待辦事項(xiàng)列表,這正是您在第二個(gè)教程中配置的接口。在 segue 執(zhí)行前,系統(tǒng)通過(guò)調(diào)用 prepareForSegue:,給所包含的視圖控制器一次準(zhǔn)備機(jī)會(huì)。這便是檢查用戶是否輕按了“Done”按鈕的時(shí)候。如果是,將會(huì)創(chuàng)建一個(gè)新的待辦事項(xiàng)。您可以檢查輕按了哪一個(gè)按鈕,如果是“Done”按鈕,將會(huì)創(chuàng)建項(xiàng)目。

輕按“Done”按鈕后創(chuàng)建項(xiàng)目

在項(xiàng)目導(dǎo)航器中選擇 XYZAddToDoItemViewController.m。

在 @implementation 行下方添加 prepareForSegue: 方法:

- (void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
}

在此方法中,請(qǐng)查看“Done”按鈕是否按下。

如果未按下就不存儲(chǔ)項(xiàng)目,而是讓方法返回但不執(zhí)行任何其他操作。

if (sender != self.doneButton) return;

查看一下文本欄中是否有文本。

if (self.textField.text.length > 0) {
}

如果有文本,將會(huì)創(chuàng)建一個(gè)新項(xiàng)目,并用文本欄中的文本為其命名。另外,請(qǐng)確保完成狀態(tài)被設(shè)定為“NOfalse”。

self.toDoItem = [[XYZToDoItem alloc] init];
self.toDoItem.itemName = self.textField.text;
self.toDoItem.completed = NO;

如果沒(méi)有文本,您就不需要存儲(chǔ)項(xiàng)目,也不需要執(zhí)行任何其他操作。 您的 prepareForSegue: 方法應(yīng)如下圖所示:

- (void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
    if (sender != self.doneButton) return;
    if (self.textField.text.length > 0) {
        self.toDoItem = [[XYZToDoItem alloc] init];
        self.toDoItem.itemName = self.textField.text;
        self.toDoItem.completed = NO;
    }
}

既然創(chuàng)建了一個(gè)新項(xiàng)目,那么就需要將該項(xiàng)目傳遞回 XYZToDoListViewController,以便它可以將項(xiàng)目添加到待辦事項(xiàng)列表。要完成此功能,請(qǐng)重新訪問(wèn)您在第二個(gè)教程中編寫的 unwindToList: 方法。當(dāng)用戶輕按“Cancel”或“Done”按鈕,XYZAddToDoItemViewController 場(chǎng)景會(huì)在關(guān)閉時(shí)調(diào)用該方法。

就像作為 unwind segue 目標(biāo)的所有方法一樣,unwindToList: 方法也采用 segue 作為參數(shù)。segue 參數(shù)是從 XYZAddToDoItemViewController 展開并返回到 XYZToDoListViewController 的過(guò)渡。由于 segue 是兩個(gè)視圖控制器之間的過(guò)渡,所以知道它的源視圖控制器是 XYZAddToDoItemViewController。通過(guò)向 segue 對(duì)象請(qǐng)求其源視圖控制器,您可以用 unwindToList: 方法訪問(wèn)儲(chǔ)存在源視圖控制器中的任何數(shù)據(jù)。目前,您想要訪問(wèn) toDoItem。如果它是 nil,則該項(xiàng)目并未創(chuàng)建。原因可能是文本欄沒(méi)有文本,或者是用戶輕按了“Cancel”按鈕。如果 toDoItem 有值,則可以取回該項(xiàng)目,再添加到 toDoItems 數(shù)組,并通過(guò)重新載入表格視圖中的數(shù)據(jù)將其顯示在待辦事項(xiàng)列表中。

儲(chǔ)存并顯示新項(xiàng)目

在項(xiàng)目導(dǎo)航器中,選擇 XYZToDoListViewController.m。

將 import 聲明添加到 @interface 行上方的 XYZAddToDoItemViewController 類中。

#import "XYZAddToDoItemViewController.h"

找到您在第二個(gè)教程中添加的 unwindToList: 方法。

在此方法中,取回源視圖控制器 XYZAddToDoItemViewController,即您要展開的控制器。

XYZAddToDoItemViewController *source = [segue sourceViewController];

取回控制器的待辦事項(xiàng)。

XYZToDoItem *item = source.toDoItem;

這就是輕按“Done”按鈕時(shí)創(chuàng)建的項(xiàng)目。

看看這個(gè)項(xiàng)目是否存在。

if (item != nil) {
}

如果是 nil,可能是“Cancel”按鈕關(guān)閉了屏幕,或者是文本欄中沒(méi)有文本,因此不必存儲(chǔ)該項(xiàng)目。

如果項(xiàng)目存在,請(qǐng)?zhí)砑拥?toDoItems 數(shù)組。

[self.toDoItems addObject:item];

重新載入表格中的數(shù)據(jù)。

因?yàn)楸砀褚晥D不會(huì)跟蹤其數(shù)據(jù),所以數(shù)據(jù)源(在這個(gè)程序中是表格視圖控制器)有責(zé)任通知表格視圖何時(shí)有新數(shù)據(jù)供其顯示。

[self.tableView reloadData];

您的 unwindToList: 方法應(yīng)如下圖所示:

- (IBAction)unwindToList:(UIStoryboardSegue *)segue
{
    XYZAddToDoItemViewController *source = [segue sourceViewController];
    XYZToDoItem *item = source.toDoItem;
    if (item != nil) {
        [self.toDoItems addObject:item];
        [self.tableView reloadData];
    }
}

檢查點(diǎn):運(yùn)行您的應(yīng)用程序?,F(xiàn)在,當(dāng)您點(diǎn)按添加按鈕 (+) 并創(chuàng)建一個(gè)新項(xiàng)目時(shí),它應(yīng)該會(huì)顯示在待辦事項(xiàng)列表中。恭喜您!您已經(jīng)創(chuàng)建了一個(gè)應(yīng)用程序,它能接收用戶的輸入,將其儲(chǔ)存在對(duì)象中,并在兩個(gè)視圖控制器之間傳遞該對(duì)象。對(duì)于基于串聯(lián)圖的應(yīng)用程序,這是在場(chǎng)景之間移動(dòng)數(shù)據(jù)的基礎(chǔ)。

小結(jié)

您差不多完成了開發(fā) iOS 應(yīng)用程序的入門之旅。最后一部分更詳細(xì)地講述了如何查詢文稿材料,并且為您學(xué)習(xí)創(chuàng)建更高級(jí)的應(yīng)用程序給出了一些后續(xù)建議。

上一篇:編寫自定類下一篇:基礎(chǔ)