在開始這章之前,先做個(gè)說明,從這篇開始,我所使用的xcode更新成了最新的版本,版本是4.6.1(4H512),如下:
大家可以打開自己電腦上的App Store,然后搜索xcode,第一個(gè)出現(xiàn)的就是Xcode,然后直接點(diǎn)擊安裝就行,很方便且智能,如果你的電腦上有舊版本的xcode,它還會(huì)提示你刪除,反正整個(gè)過程我按住下來還是很容易的。
另外,從這篇開始,我使用的教程也做了相應(yīng)的升級(jí),現(xiàn)在使用的教程為
這個(gè)大家去搜一下就可以找到,很方便。
好了,其他的沒什么不同,下面開始我們這一篇的學(xué)習(xí)。
1)Storyboard簡介
這次學(xué)習(xí)的內(nèi)容是在iOS 5的時(shí)候才加入的一個(gè)新的東西:Storyboard,簡單的翻譯成“故事版”(好吧,我覺得這個(gè)名字蠻挫的),它簡化了一些開發(fā)app時(shí)的一些步驟,例如自動(dòng)為我們添加必要的delegate/dataSource,在多個(gè)view之間的切換,使用圖和線連接各個(gè)view,讓我們能夠清晰的看到各個(gè)view之間的前后關(guān)系。這樣的好處是減輕了我們在管理view之前切換時(shí)的工作量,能夠讓我們把更多的注意力集中在具體的功能實(shí)現(xiàn)上,然后是我們對(duì)整個(gè)的app view之間的關(guān)系有一個(gè)整體的了解。
Storyboard還是基于xib(Xcode's Interface Builder),但是一個(gè)Storyboard中又可以包含多個(gè)xib,每個(gè)xib都一個(gè)它自己的controller綁定。好像,下面先舉一個(gè)簡單的例子,來直觀的感受一下什么是Storyboard。
2)Create a Simple Storyboard
創(chuàng)建一個(gè)PRoject,左邊選擇application,右邊選擇Single View Application,點(diǎn)擊Next
將項(xiàng)目命名為“Simple Storyboard”,然后選中Use Storyboards,單擊Next
找個(gè)地方保存新建的項(xiàng)目,完成創(chuàng)建
在project navigator中,默認(rèn)幫我們創(chuàng)建的文件有很多都是和之前一樣的,有BIDAppDelegate,BIDViewController,但是我們沒有發(fā)現(xiàn)xib文件,取而代之的是一個(gè)MainStoryboard.storyboard,在這個(gè)storyboard中,藏著一個(gè)系統(tǒng)默認(rèn)幫我們創(chuàng)建的xib,選中MainStoryboard.storyboard,在editor area中,會(huì)出現(xiàn)一個(gè)xib,而整個(gè)xib的controller文件正是BIDViewController,至此所有默認(rèn)創(chuàng)建的文件都已經(jīng)對(duì)上號(hào)了。
打開dock,選中View Controller節(jié)點(diǎn)并展開,你會(huì)發(fā)現(xiàn),在layout area下的一個(gè)黑色區(qū)域中顯示的圖標(biāo)和dock中是一樣的,這個(gè)黑色區(qū)域和上方的view組成了一個(gè)場景,叫做scene。(在scene中還有一個(gè)Exit,這個(gè)就不作介紹了,因?yàn)闀纠锩嬉彩鞘÷缘模┰趘iew的左邊有一個(gè)大大的箭頭,這個(gè)箭頭是用來說明該app的起始view是哪個(gè)。
在layout area的右下方有一個(gè)小圖標(biāo)
,這個(gè)是用來切換iphone4和iphone5的(我們的這個(gè)例子還是基于iphone4的界面)

好了,簡單的介紹就到這里,下面繼續(xù)我們這個(gè)例子,從Object library中拖一個(gè)Label放到view的中間,雙擊Label,輸入“Simple”
好了編譯運(yùn)行你的程序,一個(gè)最簡單的Storyboard app完成了
當(dāng)我們使用Storyboard開發(fā)app的時(shí)候,很多事情程序都會(huì)幫我們完成,包括如何載入默認(rèn)的xib。如果你選中project navigator中的項(xiàng)目名稱
在editing pane中你可以找到程序默認(rèn)載入的storyboard,這里例子中默認(rèn)的storyboard是MainStoryboard.storyboard
3)Storyboard with UITableViewController
在之前幾篇的例子中,我們已經(jīng)很多次的使用到了UITableViewController,對(duì)其操作的方式應(yīng)該已經(jīng)很熟悉了,一般是tableview中包含很多個(gè)cell,每個(gè)cell有一個(gè)identifier,創(chuàng)建cell的時(shí)候用到的方法是cellForRowAtIndexPath。在storyboard中,還是會(huì)用到這些,但是會(huì)相對(duì)簡單一些,下面我們接著上面的例子做下去。
選中Project navigator中的Simple Storyboard文件夾,單擊鼠標(biāo)右鍵,選擇“New File...”,在彈出的窗口中,左邊選擇Cocoa Touch,右邊選擇Objective-C class,點(diǎn)擊Next按鈕,在下一個(gè)窗口中將class命名為BIDTaskListController,Subclass of命名為UITableViewController,點(diǎn)擊Next按鈕,完成創(chuàng)建。
接著選中MainStoryboard.storyboard,從Object library中拖一個(gè)Table View Controller
到layout area
在dock中,選中剛才拖入的Table View Controller(這樣能夠抱著我們選中的是layout area中整個(gè)的Table View Controller)
打開Identity inspector,將Class設(shè)置為BIDTaskListController,當(dāng)設(shè)置完成后,dock中的table view會(huì)變成Task List Controller
這樣我們新加的TableViewController就和它的類對(duì)應(yīng)起來了,tableview也知道它可以去哪里取得它所需要的數(shù)據(jù)。
在拖入的Table View Controller上,有一個(gè)默認(rèn)的cell(Prototype Cells),我們可以為其添加identifier,在其上面定制自己的cell樣式(注意,我們可以添加任意多個(gè)Prototype Cells,每個(gè)cell用identifier來區(qū)分,定制不同的樣式,這里的cell只是一個(gè)原型,根據(jù)原型cell生成正式的cell,之后會(huì)有一個(gè)這樣的例子)。我們選中整個(gè)默認(rèn)的cell,并打開attributes inspector,找到Identifier,輸入plainCell
然后從object library中,拖一個(gè)Label放到原型cell上,Label如何布局看個(gè)人愛好,選中Label,在attributes inspector中找到Tag,將其值設(shè)為1,這樣在code中就可以通過Tag的值找到Label了。
接著,我們選中整個(gè)的cell,你也可以到dock中去選,這樣比較準(zhǔn)確,然后按Command + D,或者從菜單欄中選擇Edit>Duplicate,復(fù)制一個(gè)cell,這樣在table view中就有2個(gè)prototype cell了
(這里有一個(gè)非常有用的小技巧,當(dāng)你想直接在view中選擇自己想要的元素時(shí),但是又礙于一個(gè)view上疊加的元素太多很難直接選中,那么在這時(shí),你同時(shí)按住鍵盤上的shift和control鍵,然后在你想選擇的元素上點(diǎn)擊鼠標(biāo),會(huì)彈出一個(gè)窗口,上面羅列了鼠標(biāo)點(diǎn)擊的位置下所有存在的元素,然后你再去進(jìn)行選擇會(huì)變的異常的簡單。
例如我在cell中的Label上點(diǎn)鼠標(biāo)
鼠標(biāo)點(diǎn)擊的位置一共有4個(gè)層疊元素
如果我們在cell上點(diǎn)擊鼠標(biāo)
Label不見了,只有三個(gè)元素。
這么樣,用這樣的方法去選擇元素很簡單吧,還是蠻佩服蘋果在細(xì)節(jié)方面的考慮和設(shè)計(jì)的。)
選中新加的cell,在attributes inspector中將Identifier賦值為attentionCell
選中Label,在attributes inspector中將其顏色設(shè)置為紅色
好了,對(duì)于這個(gè)table view的操作全部完成,在開始具體的編寫代碼之前,還有一件事情,將layout area中的那個(gè)大大的箭頭移到這個(gè)table view上,這樣程序在載入的時(shí)候默認(rèn)的會(huì)顯示這個(gè)view
保存一下MainStoryboard.storyboard,下面開始具體的編碼。
打開BIDTaskListController.m文件,你會(huì)發(fā)現(xiàn)很多常用的方法已經(jīng)存在:
viewDidLoad
didReceiveMemoryWarning
numberOfSectionsInTableView
numberOfRowsInSection
cellForRowAtIndexPath
didSelectRowAtIndexPath
我們只要直接往這些方法中填代碼就可以了,添加如下代碼
#import "BIDTaskListController.h"@interface BIDTaskListController ()@property (strong, nonatomic) NSArray *tasks;@end@implementation BIDTaskListController......- (void)viewDidLoad{ [super viewDidLoad]; // Uncomment the following line to preserve selection between presentations. // self.clearsselectionOnViewWillAppear = NO; // Uncomment the following line to display an Edit button in the navigation bar for this view controller. // self.navigationItem.rightBarButtonItem = self.editButtonItem; self.tasks = @[@"Walk the dog", @"URGENT: Buy milk", @"Clean hidden lair", @"Invent miniature dolphins", @"Find new henchmen", @"Get revenge on do-gooder heroes", @"URGENT: Fold laundry", @"Hold entire world hostage", @"Manicure"];}......#pragma mark - Table view data source- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView{#warning Potentially incomplete method implementation. // Return the number of sections. return 1;}- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{#warning Incomplete method implementation. // Return the number of rows in the section. return [self.tasks count];}首先和之前一樣,定義一個(gè)NSArray類型的tasks,用于保存table view中的行,然后在viewDidLoad中對(duì)tasks進(jìn)行賦值(這里的語法和之前我看到的賦值方法有點(diǎn)不同,越到后面,語句寫的越是精煉啊),在numberOfSectionsInTableView中,返回1,表示只有一個(gè)section,在numberOfRowsInSection中返回section中row的數(shù)量。這些都很簡單,接著添加代碼
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{ static NSString *CellIdentifier = @"Cell"; UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath]; NSString *identifier = nil; NSString *task = [self.tasks objectAtIndex:indexPath.row]; NSRange urgentRange = [task rangeOfString:@"URGENT"]; if (urgentRange.location == NSNotFound) { identifier = @"plainCell"; } else { identifier = @"attentionCell"; } UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:identifier]; // Configure the cell... UILabel *cellLabel = (UILabel *)[cell viewWithTag:1]; NSMutableAttributedString *richTask = [[NSMutableAttributedString alloc] initWithString:task]; NSDictionary *urgentAttributes = @{NSFontAttributeName : [UIFont fontWithName:@"Courier" size:24], NSStrokeWidthAttributeName : @3.0}; [richTask setAttributes:urgentAttributes range:urgentRange]; cellLabel.attributedText = richTask; return cell;}代碼一開始定義了一個(gè)identifier,然后根據(jù)indexPath獲得tasks中的task,NSRange可以認(rèn)為是一個(gè)范圍或者一種排列,它根據(jù)這個(gè)范圍或者排列到另一個(gè)對(duì)象去尋找,如果找到了就返回開始的位置,如果沒有找到就返回NSNotFound。NSRange的對(duì)象urgentRange定義了一個(gè)字段“URGENT”,它在task中進(jìn)行匹配,如果存在,那么這個(gè)cell就把plainCell賦給identifier,如果不存在則將attentionCell賦給identifier。然后根據(jù)identifier從tableView的方法dequeueReusableCellWithIdentifier中得到相應(yīng)的cell。
之后的一段是對(duì)cell上的label進(jìn)行操作,還記得剛才我們在attributes inspector中將Label的Tag設(shè)置為1了嗎?這里就用到了,使用viewWithTag的方法在cell中找到Label,然后對(duì)Label進(jìn)行賦值,之后的一些操作是對(duì)特定的字符串“URGENT”進(jìn)行操作,更改它的字體屬性。這些就一筆帶過吧,畢竟我們的注意力不是集中在這個(gè)上面,對(duì)Label操作完后,將其賦給cellLabel,最后返回cell。
好了,編譯運(yùn)行(編譯時(shí)會(huì)有warning產(chǎn)生,這個(gè)不用去理會(huì),編譯還是會(huì)成功,這些warning是告訴你在Storyboard中有xib是沒有被使用的,我們的箭頭沒有指向它且和當(dāng)前的view又沒有聯(lián)系,所以不會(huì)對(duì)其進(jìn)行操作,有warning是正常的),效果如下
如果task中包含字符串“URGENT”那么將會(huì)使用identifier為attentionCell的cell,否則就使用identifier為plainCell的cell。
4)Static Cells
在大部分的情況下,table view中的cell都是需要?jiǎng)討B(tài)生成了,app運(yùn)行時(shí),根據(jù)source的不同生成不同數(shù)量或者樣式的cell,但是在某些情況下,我們可以事先知道將要生成的cell是什么樣子的,且它是固定不變的,我們把這樣的cell稱之為Static Cells,與其對(duì)應(yīng)的則是dynamic cell。在這里我們舉一個(gè)簡單的例子來說明這種情況。
我們不用創(chuàng)建一個(gè)新的project,直接在上面的程序中接著添加代碼。選中Project navigator中的Simple Storyboard文件夾,單擊鼠標(biāo)右鍵,選擇“New File...”,在彈出的窗口中,左邊選擇Cocoa Touch,右邊選擇Objective-C class,點(diǎn)擊Next按鈕,在下一個(gè)窗口中將class命名為BIDStaticCellsController,Subclass of命名為UITableViewController,點(diǎn)擊Next按鈕,完成創(chuàng)建。
選中MainStoryboard.storyboard,再從Object library中拖一個(gè)Table View Controller到layout area,就放在原有2個(gè)view的右邊,接著將箭頭指向這個(gè)新添加的view
圖中最右邊的是新添加的view,這些view看上去比較小,是因?yàn)槲伊薼ayout area右下角的
,這樣可以方便觀察每一個(gè)view(當(dāng)然在縮小的狀態(tài)下,是沒有辦法對(duì)view進(jìn)行操作的,只能移動(dòng)其位置,要操作view,必須將view放大回正常的大小)
選中剛才添加的controller中的table view,打開attributes inspector,找到Content,在其下拉框中選擇“Static Cells”,找到Style,在其下拉框中選擇“Grouped”
table view的樣式也隨之發(fā)生了變化,出現(xiàn)了3行row,section的樣式變成了一個(gè)圓角矩形
選中section,在其attributes inspector設(shè)置如下,Rows改為2,Header中填寫“Silliest Clock Ever”
改完后的section
下面對(duì)2個(gè)cell進(jìn)行設(shè)置,選中第一個(gè)cell,在attributes inspector中將其Style設(shè)置為“Left Detail”

然后雙擊Title,改成“The Date”,重復(fù)上面的步驟,將第二個(gè)cell的Title改成“The Time”,改完后的效果
之后,我們將創(chuàng)建兩個(gè)outlet對(duì)象,分別指向2個(gè)Detail,這樣在app運(yùn)行后,就可以改變它們的值了。
現(xiàn)在先關(guān)聯(lián)這個(gè)table view controller和它的類,在dock中選中Table View Controller,然后打開identity inspector,在Class中輸入“BDIStaticCellsController”,dock中的名字也隨之發(fā)生改變
還是在dock中選中controller的狀態(tài)下,將Editor的模式設(shè)置成Assistant editor
,這樣BIDStaticCellsController.h文件會(huì)打開(如果打開的不是這個(gè)文件,那么就手動(dòng)打開吧),選中第一個(gè)cell中的Detail,然后control-drag到BIDStaticCellsController.h中并釋放,會(huì)彈出個(gè)窗口,將Name命名為“dateLabel”
對(duì)第二個(gè)cell中的Detail進(jìn)行相同的操作,將Name命名為“timeLabel”,添加完成后的BIDStaticCellsController.h
#import <UIKit/UIKit.h>@interface BIDStaticCellsController : UITableViewController
@property (weak, nonatomic) IBOutlet UILabel *dateLabel;@property (weak, nonatomic) IBOutlet UILabel *timeLabel;@end下面開始編寫代碼,打開BIDStaticCellsController.m,先將下面三個(gè)方法刪除
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView{#warning Potentially incomplete method implementation. // Return the number of sections. return 0;}- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{#warning Incomplete method implementation. // Return the number of rows in the section. return 0;}- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{ static NSString *CellIdentifier = @"Cell"; UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath]; // Configure the cell... return cell;}因?yàn)槲覀兪褂玫氖莝tatic cell,因此table view中section的數(shù)量,section中cell的數(shù)量都是固定不變的,我們也不需要從新創(chuàng)建cell,cell一共才2個(gè),會(huì)一直顯示在屏幕上。
接著添加下面的代碼
- (void)viewDidLoad{ [super viewDidLoad]; // Uncomment the following line to preserve selection between presentations. // self.clearsSelectionOnViewWillAppear = NO; // Uncomment the following line to display an Edit button in the navigation bar for this view controller. // self.navigationItem.rightBarButtonItem = self.editButtonItem; NSDate *now = [NSDate date]; self.dateLabel.text = [NSDateFormatter localizedStringFromDate:now dateStyle:NSDateFormatterLongStyle timeStyle:NSDateFormatterNoStyle]; self.timeLabel.text = [NSDateFormatter localizedStringFromDate:now dateStyle:NSDateFormatterNoStyle timeStyle:NSDateFormatterLongStyle];}在viewDidLoad中,分別對(duì)dateLabel和timeLabel進(jìn)行了設(shè)置,至于NSDate和NSDateFormatter的說明大家就去google一下吧,這里不做詳細(xì)解釋了。
編譯運(yùn)行,效果如下
新聞熱點(diǎn)
疑難解答
圖片精選
網(wǎng)友關(guān)注