1 簡介
1.1 故事板簡介
????????如果你的 app 有大量的窗口,故事板能幫你減少許多用于從一個窗口轉(zhuǎn)到另一個窗口的導(dǎo)航代碼。與每個viewcontroller一個單獨的 nib 文件不同,你的 app 只需用一個故事板文件(其中可以包含所有的viewcontroller 以及它們之間的關(guān)系)即可。
????????與傳統(tǒng)的 nib 文件不同,故事板文件有以下優(yōu)點:
? ? 1、通過一個故事板文件,你能對所有的窗口以及它們之間的關(guān)系一目了然。因為所有的窗口設(shè)計在一個故事板文件中,你會更容易把握每次改動帶給每個窗口的變化。
? ? 2、在故事板中可以描述各個窗口之間的轉(zhuǎn)換。這種轉(zhuǎn)換叫做 segue(連接)。創(chuàng)建 segue 只需用? ? ctrl+ 拖拽從一個 viewcontroller 拖到另一個 viewcontroller 即可。這將減少窗體導(dǎo)航的代碼。
? ? 3、哪怕在 tableview 上故事板仍然有用,例如定制? ? tableviewcell。你完全可以在故事板編輯器中設(shè)計自己的 tableview,這也節(jié)省了不少代碼。
????????點擊 MainStoryboard.storyboard 文件,將打開故事板編輯器:

????????故事板編輯器從外表上看很像是IB。你可以從 Object Library中拖控件(右下角)到viewcontroller 中,并修改它的布局。不同的是,故事板中不僅僅包含一個viewcontroller,而是包含 app 中的所有viewcontroller。
????????故事板有一個專門的術(shù)語“場景”,一個“場景”用于表示一個viewcontroller。你以前每個場景/viewcontroller就要用一個單獨的 nib 文件,但現(xiàn)在所有的東西都集中到了一個故事板中。
????????對于 iPhone 應(yīng)用,一次可以看一個場景,但iPad 應(yīng)用可以一次顯示多個場景,例如使用splitview 的“主-細”窗口,或者用popovercontroller 彈出內(nèi)容。
1.2 程序加載
????????如果你以前創(chuàng)建過基于 nib 的app(譯者注:Xcode 3.x),你可能知道MainWindow.xib 文件。這個nib 文件中包含了一個頂層的連接到 App Delegate 的UIWindow 對象,以及一個或多個viewcontroller。但是,當你使用故事板的時候,所有的UI 都放到了一個故事板中, MainWindow.xib 不再使用。
????????那么,在沒有 MainWindow.xib 文件的情況下,故事板是怎樣被加載到app中的呢?
????????打開 AppDelegate.h,你將看到這幾句:
#import
@interface?AppDelegate?: UIResponder
@property?(strong, nonatomic)?UIWindow?*window;
@end
????????當使用故事板的時候,應(yīng)用程序委托必須從 UIResponder 開始繼承(原先則直接從NSObject繼承),同時還有一個 UIWindow 屬性(不同的是,它不是一個IBOutlet)。
????????在 AppDelegate.m 中,它實際上什么也沒做,所有的方法都是空的。甚至application:didFinishLaunchingWithOptions:也只是簡單地返回 Yes。如果是過去,要么要在這里加入主viewcontroller的 view 到 window,要么設(shè)置window 的 rootViewController 屬性。但現(xiàn)在什么都沒有。
????????秘密都位于 Info.plist 文件。打開Ratings-Info.plist(在Supporting Files 文件組),在 nib-based 的項目中,Info.plist 文件中有一個名為NSMainNibFile 或者 Main nib file base name 的鍵,它會導(dǎo)致UIApplication去加載 MainWindow.xib 并將之連接到 app 中?,F(xiàn)在,Info.plist中不再有這個設(shè)置。
????????與之對應(yīng)的是,故事板應(yīng)用程序使用 UIMainStoryboardFile或者“Main storyboard file base name”鍵。它要求應(yīng)用程序在啟動時需要加載的故事板文件名。當這個鍵缺失時,UIApplication將默認加載MainStoryboard.storyboard 文件并自動將故事板中第一個viewcontroller 初始化并放到一個新創(chuàng)建的UIWindow 對象中。這一切不再需要手工編寫代碼。
????????你可以查看 Target 的 Summary 窗口:

????????新增的 iPhone/iPodDeployment Info 小節(jié)下面,可以讓你選擇是從故事板文件啟動還是從nib文件啟動。
????????為了更清楚一點,可以打開 main.m 查看:
#import <UIKit/UIKit.h>
#import "AppDelegate.h"
int?main(int?argc,?char?*argv[])?{
??????? @autoreleasepool?{
???????????????return?UIApplicationMain(argc, argv,?nil, NSStringFromClass([AppDelegate class]));
?????}
}
????????原先的 UIApplicationMain() 的最后一個參數(shù)為nil,但現(xiàn)在是 NSStringFromClass([AppDelegate
class])。
????????與使用 MainWindow.xib 的時候不同,故事板中不會包含應(yīng)用程序委托。由于無法從nib中加載應(yīng)用程序委托,也無法從故事板文件中加載應(yīng)用程序委托,我們必須告訴UIApplicationMain 應(yīng)用程序委托類的名稱,否則它根本無法找到應(yīng)用程序委托類。
2 使用
2.1 結(jié)合xib使用
????思路:
? ? ? ? APP跳轉(zhuǎn)流程在故事板中呈現(xiàn),但是VC的事件處理與詳細視圖設(shè)計放在xib文件中。
????具體實現(xiàn)方法:
? ? ? ? ?例如,在故事板中Level1VC中添加一個按鈕,然后將level2VC拖入故事板,按住cmd鍵,鼠標點擊按鈕并拖入level2VC,在彈出的菜單中選擇show事件,即完成了從Level1VC跳轉(zhuǎn)到Level2的操作。
2.2 使用TabBar
2.2.1 TabBarController添加
????????Ratings 程序有一個 Tabbar,包含了兩個viewcontroller。使用故事板創(chuàng)建Tabbar 是小事一碟。
????????切換到MainStoryboard.storyboard,拖一個 TabBarController 到畫布中。你可能得將Xcode窗口最大化,因為TabBarController跟兩個viewcontroller 聯(lián)系在一起,你可能需要更多的空間才能靈活操作。
????????新的 TabBarController 已經(jīng)事先配置了兩個ViewController,每個Tab 按鈕一個。UITabBarController 是一種ViewController 的容器,它包含了多個viewcontroller。其它類似的容器還有NavigationController 和 SplitViewController(后面都會介紹)。iOS5有個更酷的特性是你可以寫自己的 ViewController 容器——在本書后續(xù)教程中介紹。
????????TabBarController和所包含的ViewController的包容關(guān)系用一個箭頭(中間有一個小圓圖標)表示。
? ??????注意:如果要把TabBarController與其包含的ViewController一起移動,用Cmd+左鍵將它們?nèi)窟x中然后移動(選中的場景會有一個淺藍色的方框框住)。
????????在第一個 ViewController 中放入一個Label 然后輸入文本“FirstTab”。在第2個ViewController中放入一個 Label 并輸入文本“Second Tab”。這樣我們就能在切換Tab 時區(qū)分兩個ViewController。
? ??????注意:你不能在編輯器的縮放模式下向場景拖放東西,必須首先恢復(fù)到普通模式下。
????????選中 TabBarController并打開屬性面板。勾選“ IsInitial View Controller”選項。

????????在畫布中,原來指向最初的 ViewController 的箭頭,現(xiàn)在指向了TabBarController。也就是說,程序運行時,UIApplication 會將TabBarController作為應(yīng)用程序的第一個ViewController。故事板總是以一個 ViewController 作為“initialview controller”,即故事板的入口。
????????運行程序?,F(xiàn)在的程序,可以通過TabBarController 在兩個ViewController 中切換。
????????Xcode 其實有一個專門用于Tabbar 應(yīng)用程序的模板(叫做 Tabbed Application 模板),當然 我們也可以使用這個模板。但在某些時候我們必須通過手動創(chuàng)建TabbarController,這樣就必須知道在不使用模板時應(yīng)該如何去做。
????????現(xiàn)在你可以刪除項目模板原來創(chuàng)建的那個viewcontroller,我們不再需要它。這樣故事板中只會有一個TabbarController和它的兩個 viewcontroller。
????????以這種方式,你可以創(chuàng)建超過 5 個的ViewController 給TabBarController,它將自動在Tabbar 上顯示 More... 按鈕。
2.2.2 添加TableView Controller
????????被連接到 TabBarController 的兩個場景只是一般的UIViewController?,F(xiàn)在我們要其中的第一個替換為UITableViewController。
????????選中第一個 ViewController,刪除它。拖一個TablViewController到畫布中。選中 TablViewController,選擇菜單“Editor\EmbedIn\Navigation”。
????????這將導(dǎo)致增加一個 ViewController 到畫布中:

????????當然你也可以直接從 Object Library 中拖一個NavigationController,但 Embed In 命令更簡單一些。
????????由于 NavigationController 也是一種ViewController容器,它和 TableViewController 之間也有一個箭頭表示二者關(guān)系。在文檔樹中這些關(guān)系顯示如圖中所示:

????????注意 TableViewController 上被加入了一個navigationBar。這是故事板編輯器自動放入的,因為這個場景現(xiàn)在將在NavigationController 的 frame 內(nèi)顯示。當然,這并不是真正的UINavigationBar對象,而只是一個模擬的“假”的導(dǎo)航條。
????????打開 TableViewcontroller 的屬性面板,我們可以看到頂部有一個Simulated metrics 小節(jié)。
????????故事板默認使用“Inferred”(依靠推斷)設(shè)置,意思是該場景如果在NavigationController中顯示則會顯示導(dǎo)航條,如果在 TabBarController 中顯示則會顯示TabBar,等等。如果你需要的話也可以改變這些設(shè)置,但請明白,這些設(shè)置僅僅是幫助你設(shè)計你的屏幕,Simulated Metrics 并不會用于運行時,它們僅僅是幫助你進行可視化設(shè)計的。
????????現(xiàn)在將新場景連接到TabBarController 。ctrl+左鍵,從TabBarController拖一條線到NavigationController.
????????拖完后將顯示彈出菜單,請選擇Relationship-viewControllers。這將在這兩者間創(chuàng)建新的關(guān)系:

????????TabBarController 現(xiàn)在有兩個關(guān)系,一個Tab 一個。NavigationController自己有一個關(guān)系,連接的是TableViewController。此外還有另外一種箭頭,“segue”,我們在后面講。
????????創(chuàng)建新連接時,新的 Tab 也同時被加到TabBarController上,名字叫做 “Item”。我想將新的場景放在第一個Tab上,可以用拖拽 Tab 的方式改變它們的順序。
????????運行程序,現(xiàn)在第一個 Tab 已經(jīng)變成了NavigationController。
????????在我們將實際的功能加入 app 之前,讓我們整理一下我們的故事板。我想將第一個tab命名為 Players,第2個 tab 命名為Gestures。你不需要去改變 TabBarController,而是要改變與tab 對應(yīng)的ViewController。
????????當你將一個 ViewController 連接到TabBarController時,會在 ViewController 上創(chuàng)建一個TabBarItem 對象。通過 TabBarItem 對象,你可以設(shè)置Tab的 Title 和圖片。
????????選擇 NavigationController 上的TabBarItem對象,在屬性面板,設(shè)置它的 Title 為Players。將第二個 ViewController 的TabBarItem 重命名為Gestures。
????????我們還可以在 Tab 上放入圖片。在本教程源代碼中有一個文件夾Images。將該文件夾添加到項目中去。在TabBarItem “Guestures”的屬性面板,將 Players.png 設(shè)為它的image。將TabBarItem “Players”的 image 設(shè)置為Players.png。
????????與之相仿,在 NavigationController 所包含的ViewController上,有一個 NavigationItem 對象,可用于設(shè)置導(dǎo)航欄。選擇TableViewController 上的NavigationItem,在屬性面板中將title 修改為 Players。
????????當然,你也可以通過簡單地雙擊 NavigationBar 來修改title(注意:你應(yīng)該雙擊TableViewController 上的“假”導(dǎo)航條,而不是雙擊NavigationController 上的真導(dǎo)航條。
????????運行程序,不需要你編寫一行代碼,我們定制的 Tab 欄就顯示出來了。
2.3 模板cells
2.3.1 模板cells使用
????????注意到當你加入 tableViewController 后,Xcode會發(fā)出警告了嗎?
? ? ? ? “UnsupportedConfiguration: Prototype table cells must have reuse identifiers”,當加入一個TableViewController到故事板后,Xcode 默認會使用一種 prototype cells 的單元格(模板cells)。但我們并沒有配置它,因此會有這個警告。
????????模板 cells 是一種很酷的故事板特性。它遠勝于原來的nib 文件。在以前,如果你要定制表視圖單元格,你要么在代碼中向cell對象添加自己的 subviews ,要么新建一個 nib 然后從nib 中加載你自己的 cell。但模板 cells 的出現(xiàn)簡化了這一切,現(xiàn)在你可以直接在故事板編輯器中設(shè)計你自己的表視圖單元格。
2.3.2 新建PlayerCell
????????TableViewController 上自帶有一個空白的模板cell。點擊這個cell,你可以在屬性面板中設(shè)置它的樣式為Subtitle。這會使 cell 變成包含有兩個label 的 cell。如果你曾經(jīng)自己手動創(chuàng)建過TableViewCell,你應(yīng)該知道這就是UITableViewCellStyleSubtitle樣式。通過模板 cells,你可以創(chuàng)建內(nèi)置樣式的cell,也可以創(chuàng)建完全定制的cell(我們馬上就會提到)。

????????將 Accessory 屬性改為Disclosure Indicator 然后將 Reuse Identifier (復(fù)用ID) 設(shè)置為“PlayerCell”。這樣 Xcode 會立即消除警告。所有的模板 cells 仍然是普通的 UITableViewCell 對象,仍然會帶有一個復(fù)用 ID,Xcode僅僅是提示我們別忘了設(shè)置它(至少會讓我們注意到這個警告)。
????????運行程序,什么都沒有改變。不要奇怪,我們還沒有提供數(shù)據(jù)源,因此表視圖中不會顯示任何行。
2.3.3 創(chuàng)建PlayersViewController
????????加一個新的 File 到項目中。選擇UIViewController subclass 模板。將類命名為PlayersViewController ,確保它繼承于UITableViewController。不要選擇“WithXib...”選項,因為我們在故事版中已經(jīng)為這個類設(shè)計了一個UI。我們不再需要nib!
????????回到故事版編輯器,選擇 TableViewController。在Identity 面板,將它的 Class 設(shè)置為PlayersViewController。這一步很重要,因為這會將位于故事版中的一個場景與你自己的 ViewController子類關(guān)聯(lián)起來。千萬記得這個步驟,否則你創(chuàng)建類將完全沒有用處!
????????從現(xiàn)在開始,運行程序后故事板中的tableViewController 將變成我們的PlayersViewController 類的一個實例。
????????在 PlayersViewController.h 文件中加入一個可變數(shù)組屬性:
#import <UIKit/UIKit.h>
@interface?PlayersViewController?: UITableViewController
????@property?(nonatomic, strong)?NSMutableArray?*players;
@end
????????這個數(shù)組將存儲應(yīng)用程序中的模型數(shù)據(jù),即Player (玩家)對象。
2.3.4 創(chuàng)建數(shù)據(jù)模型Player類
????????現(xiàn)在創(chuàng)建Player 類。創(chuàng)建一個新的File,使用 Objective-C class 模板。命名為 Player,繼承NSObject。
Player.h 文件:
@interface?Player?:?NSObject
????@property?(nonatomic, copy)?NSString?*name;
????@property?(nonatomic, copy)?NSString?*game;
????@property?(nonatomic, assign)?int rating;
@end
Player.m 文件
#import "Player.h"
@implementation Player
????@synthesize name;
????@synthesize game;
????@synthesize rating;
@end
????????這些都毫無出奇之處。Player 是一個簡單對象,擁有3個屬性:玩家姓名、玩家所玩的游戲、以及等級(1-5星)。
2.3.5 構(gòu)建測試數(shù)據(jù)源
????????我們將在 AppDelegate 中放入一個數(shù)組,并在數(shù)組中放入一些Player對象進行測試。這個數(shù)組將被賦值給 PlayerViewController 的players 屬性。
????????在 AppDelegate.m,加入Player類和PlayersViewController類的導(dǎo)入語句,加入一個實例變量叫做players:
#import "AppDelegate.h"
#import "Player.h"
#import "PlayersViewController.h"
@implementation?AppDelegate?{
????NSMutableArray?*players;
}
......
修改didFinishLaunchingWithOptions 方法:
-?(BOOL) application: (UIApplication?*)application didFinishLaunchingWithOptions:(NSDictionary*)launchOptions?{
??????? players?=?[NSMutableArray?arrayWithCapacity: 20];
??????? Player?*player?=?[[Player alloc]?init];
??????? player.name?=?@"Bill Evans";
??????? player.game?=?@"Tic-Tac-Toe";
??????? player.rating?=?4;
????????[players addObject: player];
??????? player?=?[[Player alloc]?init];
??????? player.name?=?@"Oscar Peterson";
??????? player.game?=?@"Spin the Bottle";
??????? player.rating?=?5;
????????[players addObject: player];
??????? player?=?[[Player alloc]?init];
??????? player.name?=?@"Dave Brubeck";
??????? player.game?=?@"Texas Hold’em Poker";
??????? player.rating?=?2;
????????[players addObject: player];
? ? ? ? UITabBarController?*tabBarController?= (UITabBarController?*) self.window.rootViewController;
? ? ? ? UINavigationController?*navigationController?= [[tabBarController viewControllers]?objectAtIndex: 0];????????
????????PlayersViewController?*playersViewController?= [[navigationController viewControllers]?objectAtIndex: 0];
????????playersViewController.players?= players;
???? ???return?YES;
}
????????首先創(chuàng)建了一些 Player 對象并加到 players 數(shù)組里。然后:
UITabBarController?*tabBarController?=?(UITabBarController?*)self.window.rootViewController;
UINavigationController?*navigationController?= [[tabBarController viewControllers] objectAtIndex: 0];
PlayersViewController?*playersViewController?= [[navigationController viewControllers]?objectAtIndex: 0];
playersViewController.players?= players;
????????呀,這是什么?我們想將 players 數(shù)組賦給PlayersViewController的 players 屬性,以便作為TabeViewController 的數(shù)據(jù)源。但是應(yīng)用程序委托不知道PlayersViewController在哪里,因此我們不得不把它從故事板中找出來。這是使用故事板的一個令我煩心不已的不足。如果是使用IB ,在 MainWindow.xib中會有應(yīng)用程序委托的一個引用,同時你可以將頂層的ViewController 連接到應(yīng)用程序委托的IBOutlet 屬性。但現(xiàn)在使用故事板就不可能了。在頂層ViewController 中不能再引用應(yīng)用程序委托。這真是個不幸,我們只能通過代碼方式獲得引用。
UITabBarController?*tabBarController?=?(UITabBarController?*)self.window.rootViewController;
????????我們知道故事板的 initial view controller 是一個TabBarController,所以我們可以從 window 對象的 rootViewController 獲得它的一個引用并進行類型轉(zhuǎn)換。
????????PlayersViewController 位于第一個tab 的NavigationController 容器中,因此我們先獲得UINavigationController 對象:
UINavigationController?*navigationController?=?[[tabBarController viewControllers]?objectAtIndex: 0];
????????然后在 NavigationController 的rootViewController,可以獲得PlayersViewController :
PlayersViewController?*playersViewController?=????[[navigationController viewControllers] objectAtIndex: 0];
????????但是,UINavigationController 沒有 rootViewController屬性。因此我們必須從viewControllers 數(shù)組中檢索。(它有一個 topViewController 屬性,但那個是位于viewControllers棧頂?shù)?view controller。而我們要的是棧低的 view controller。雖然在程序剛啟動的時候,棧頂和棧底實際上是一個,你也可以使用topViewController,但這不是那么安全)
????????現(xiàn)在我們有了 Player 數(shù)組,可以回到PlayersViewController中創(chuàng)建我們的數(shù)據(jù)源了。
2.3.6 填充table view視圖數(shù)據(jù)
????????打開PlayersViewController.m,修改table view 的數(shù)據(jù)源方法:
-?(NSInteger) numberOfSectionsInTableView: (UITableView?*)tableView?{
????????return?1;
}
-?(NSInteger) tableView: (UITableView?*)tableView numberOfRowsInSection: (NSInteger)section?{
????????return?[self.players count];
}
????????重要的是cellForRowAtIndexPath方法。Xcode 創(chuàng)建的模板代碼是這樣的:
-?(UITableViewCell?*) tableView: (UITableView?*)tableView cellForRowAtIndexPath:(NSIndexPath*)? indexPath {
? ? staticNSString*CellIdentifier?=?@"Cell";
? ? UITableViewCell?*cell?=?[tableView dequeueReusableCellWithIdentifier: CellIdentifier];
?????if?(cell?==?nil)?{
???????? cell?=?[[UITableViewCell alloc] initWithStyle: UITableViewCellStyleDefault? reuseIdentifier: CellIdentifier];
?????}
?????// Configure the?cell...
?????return cell;
}
????????毫無疑問,你曾經(jīng)無數(shù)次地在這個地方編寫自己的 table view 代碼。但現(xiàn)在不同了。將代碼修改為:
-?(UITableViewCell?*) tableView: (UITableView?*)tableView cellForRowAtIndexPath:(NSIndexPath*)indexPath?{
? ? UITableViewCell?*cell?=?[tableView dequeueReusableCellWithIdentifier:?@"PlayerCell"];
? ? Player?*player?=?[self.players objectAtIndex: indexPath.row];
? ? cell.textLabel.text?=player.name;
? ? cell.detailTextLabel.text?=player.game;
?????return cell;
}
????????代碼變得更簡單了! 其實你只需要從這里獲得新的cell :
UITableViewCell?*cell?=?[tableView dequeueReusableCellWithIdentifier:?@"PlayerCell"];
????????不再需要復(fù)用單元格了,它會自動從模板 cell 獲得一份拷貝給你使用!你只需要提供復(fù)用的ID(你曾經(jīng)在故事版編輯器中為模板cell設(shè)置過的,在本例中,即“PlayerCell”)。記得設(shè)置這個ID,否則模板cell 不會生效。
????????由于 PlayersViewController 不認識Player 類,你還需要導(dǎo)入Player 類的頭文件:
#import "Player.h"
????????此外還要合成 players 屬性:
@synthesize players;
????????運行程序,如下圖所示:

? ??????注意:在本例中,我們只用了一種模板 cell,如果你需要顯示多種cell,你可以加入更多的模板cell。你可以復(fù)制已有的模板cell為新的cell,也可以增加TableView的 Prototype Cells 屬性值。注意,確保每個模板 cell 都有自己的復(fù)用ID。
????????使用神奇的模板cell只需一行代碼,這是件了不起的事情!
2.3.7 設(shè)計完全自定義的模板cell
????????對于大部分 app,使用標準的cell 樣式就足矣。但我想在單元格右邊加一張圖片以顯示玩家級別(以星級的形式)。UITableViewCell的標準樣式中不包含可以在單元格中放入一個ImageView,因此我只能選擇定制設(shè)計。
????????回到MainStoryboard.storyboard,選擇模板cell,將Style屬性設(shè)置為 Custom。默認的 label 將消失。
????????首先增加 cell 的高度為55 像素。拖拽它下端的拉柄可以改變它的高度,也可以修改Size 面板中的Row height 值。????
????????拖兩個 Label 到Cell 中,將它們放置到大致等于原先所在的位置。隨意修改它們的字體和顏色。將兩個label的高亮色為白色。這樣當用戶點擊 cell 時看起來會好一些,因為此時cell的背景為藍色。
????????拖一個 ImageView 到cell 右端,緊靠著右箭頭。調(diào)整它寬度為81,高度無所謂。設(shè)置它的Mode 為 Center(在屬性面板的 View 下面)以便當我們將圖片放入時它不會被拉伸。
????????我將倆個 label 的寬度設(shè)置為210,這樣不會遮住ImageView。最終設(shè)計完成是這個樣子:

????????由于是定制單元格,我們不再使用cell 的 textLabel 和detailTextLabel 屬性來顯示文本。這兩個標簽的屬性在我們的cell 中也不再存在。
????????我們將通過 tag 檢索我們想要的 Label。對于 Name 標簽,tag設(shè)置為100,對于 Game 標簽,tag設(shè)置為102。你可以在屬性面板中設(shè)置tag。
????????打開PlayersViewController.m ,將cellForRowAtIndexPath方法修改為:
-?(UITableViewCell?*)tableView:(UITableView?*)tableView cellForRowAtIndexPath:(NSIndexPath*)indexPath?{
? ? UITableViewCell?*cell?=?[tableView dequeueReusableCellWithIdentifier:?@"PlayerCell"];
????Player?*player?=?[self.players objectAtIndex: indexPath.row]; ??????? ????UILabel?*nameLabel?=?(UILabel?*)[cell viewWithTag:100];
? ? nameLabel.text?=player.name;
? ? UILabel?*gameLabel?=?(UILabel?*)[cell viewWithTag:101];
? ? gameLabel.text?=player.name;
? ? UIImageView?*?ratingImageView?=?(UIImageView?*)[cell viewWithTag: 102];
? ? ratingImageView.image?=?[self imageForRating: player.rating];
?????return cell;
}
????????這里調(diào)用了一個新方法imageForRating,這個方法實現(xiàn)如下:
-?(UIImage?*)imageForRating: (int)rating?{
????????switch?(rating)????????{
???????????????case?1:?return?[UIImage imageNamed:?@"1StarSmall.png"];
???????????????case?2:?return?[UIImage imageNamed:?@"2StarsSmall.png"];
???????????????case?3:?return?[UIImage imageNamed:?@"3StarsSmall.png"];
???????????????case?4:?return?[UIImage imageNamed:?@"4StarsSmall.png"];
???????????????case?5:?return?[UIImage imageNamed:?@"5StarsSmall.png"];
????????}
????????return?nil;
}
????????再次運行程序。

????????啊哈,看起來有點不太對勁。我們修改了模板cell 的高度,但tableView 并不知道。有兩個辦法:改變table view 的 Row Height 屬性,或者修改?heightForRowAtIndexPath 方法。前者更為簡單,因此我使用了前者。
? ??????注意:如果你事先無法確定 cell 高度,或者你有不同高度的幾種 cell,你應(yīng)該使用heightForRowAtIndexPath。
????????返回MainStoryboard.storyboard,在TableView的 Size 面板中,將 Row Height 設(shè)置為55。
????????如果你用拖拽而不是直接鍵入的方式改變cell 的高度,tableview 的 Row Height 屬性也會自動隨之改變。
????????再次運行程序,這次看起來就好多了。
2.3.8 子類化模板Cell
????????我們的 Table? View 看起來不錯吧!但我并不喜歡用tag 去訪問 UILabel 和其他 cell 的 subview。如果這些Label 能連接到IBOutlet 屬性豈不是更好?
????????在項目中添加新的 File,使用Objective-C class 模板。類名為PlayerCell ,繼承自UITableViewCell。
修改 PlayerCell.h 為:
@interface?PlayerCell?:UITableViewCell
????@property?(nonatomic, strong)?IBOutlet UILabel?*nameLabel;
????@property?(nonatomic, strong)?IBOutlet UILabel?*gameLabel;
????@property?(nonatomic, strong)?IBOutlet UIImageView????*ratingImageView;
@end
修改 PlayerCell.m 為:
#import "PlayerCell.h"
@implementationPlayerCell
????@synthesize nameLabel;
????@synthesize gameLabel;
????@synthesize ratingImageView;
@end
????????內(nèi)容不多,僅僅是加了幾個屬性,nameLabel, gameLabel 以及 ratingImageView。
????????回到MainStoryboard.storyboard,選擇模板cell ,在 Identity 面板改變其 Class 為“PlayerCell”。這樣當你用dequeueReusableCellWithIdentifier 方法獲得一個 cell時,它實際上返回一個PlayerCell給你。
????????注意,我將類的名字和重用 ID 取成了一樣——都叫做 PlayerCell——這僅僅是因為我喜歡這樣。其實二者毫無干系,你完全讓它們不一樣。
????????選擇,你可以將 label 和ImageView 連接到IBOutlet。選中Label 然后從它的連接面板拖一條線到TableViewCell,或者用 Ctrl+左鍵從TableViewCell? 拖到 Label 上。

? ??????重點:你可以在控件和 TableViewCell 間建立連接,而不僅僅是在控件和 ViewController 間建立連接!如你所見,當你的數(shù)據(jù)源用 dequeueReusableCellWithIdentifier向 Table View 請求新的單元格時,TableView并不真正把模板 cell 給你,它只是給你一份模板 cell 的拷貝(也可能是一個已經(jīng)存在的cell——在復(fù)用的情況下)。也就是說任何時候都存在多個 PlayerCell 實例。如果你連接 cell 上的一個Label 到ViewController 的 IBOutlet上,那么會有多個Label 在試圖使用相同的 IBOutlet。那就麻煩了。(順便說一句,如果在你的Cell上有一個 Custom Button 或者其他控件,你可以將模板cell 連接到 ViewController 的action 上。
????????現(xiàn)在,我們已經(jīng)連接好這些屬性。我們的代碼可以變得更加簡潔:
-?(UITableViewCell?*)tableView: (UITableView?*)tableView cellForRowAtIndexPath:(NSIndexPath*)indexPath?{
? ? PlayerCell?*cell?=?(PlayerCell?*)[tableView dequeueReusableCellWithIdentifier:?@"PlayerCell"];
? ? Player?*player?=?[self.players objectAtIndex: indexPath.row];
? ? cell.nameLabel.text?=player.name;
? ? cell.gameLabel.text?=player.game;
??? cell.ratingImageView.image=?[self imageForRating: player.rating];
?????return cell;
}
????????這樣還差不多。我們將dequeueReusableCellWithIdentifier返回的結(jié)果轉(zhuǎn)換為PlayerCell,然后用它的屬性去訪問Label 和 UIImageView。我真的喜歡使用模板cell,它使我的TableView 代碼看起來整潔多了。
????????當然,你仍然需要導(dǎo)入 PlayerCell 類:
#import "PlayerCell.h"
????????運行程序,跟前面一模一樣,但在表格中使用的是我們自己的TableViewCell 子類。
????????還有一些設(shè)計技巧。在設(shè)計自己的TableViewCell 時,你需要注意一些地方。首先,你應(yīng)當設(shè)置Label 的 Highlighted Color(高亮色) ,以便用戶在點擊表格行時感覺更好。
????????其次,你應(yīng)當確保添加的內(nèi)容能自動適應(yīng)單元格尺寸的變化。例如,當你需要表格行能夠被刪除或移動時 ,Cell 尺寸會發(fā)生改變。
????????添加下列方法到 PlayerViewController.m:
-?(void)tableView: (UITableView?*)tableView commitEditingStyle: (UITableViewCellEditingStyle)editingStyle forRowAtIndexPath: (NSIndexPath*)indexPath?{
????????if?(editingStyle?==?UITableViewCellEditingStyleDelete)???????{
???????????????[self.players removeObjectAtIndex: indexPath.row];
???????????????[tableView deleteRowsAtIndexPaths:[NSArray?arrayWithObject: indexPath] withRowAnimation: UITableViewRowAnimationFade];
????????}
}
????????實現(xiàn)這個方法后,表格的“輕掃以刪除”功能被開啟。運行程序,在某行上進行輕掃手勢,看看會發(fā)生什么。

????????刪除按鈕出現(xiàn)在 cell 上,但它同時也遮住了等級圖片。實際上是因為刪除按鈕占據(jù)了部分cell空間,而 cell 大小隨之改變,ImageView 卻沒有改變。
????????要解決這個問題,打開 MainStoryBoard.storyboard,選擇 ImageView ,在 Size 面板中修改 Autosizing 以便它始終位于 superview 的右端:

????????Label 的 Autosizing 設(shè)置如下,因此當 cell 尺寸改變時,Label 的尺寸也隨之變化:

????????經(jīng)過這些調(diào)整,刪除按鈕的出現(xiàn)會將星級圖標擠到左邊:

????????你也可以在刪除按鈕出現(xiàn)時讓星星們消失,這就留給讀者們自己去實現(xiàn)了。重要的是,你應(yīng)該在設(shè)計TableViewCell 時對這些細節(jié)性的東西一清二楚。
3 設(shè)計原則
3.1 布局設(shè)計原則
3.1.1 一個控件的布局盡量只采用一種方式,要么是Storyboard(XIB)要么是代碼
????????因為視圖在刷新時,會直接從Storyboard中加載控件的大小,而不是代碼?。?!
3.1.2 不能同時設(shè)置一個控件橫向或縱向的相對間距后,又去設(shè)置絕對尺寸,否則會導(dǎo)致控件不能顯示,也不會報錯!

3.1.3 在故事板中進行布局設(shè)計時,如果有導(dǎo)航欄、Tab欄,必須也要把高度預(yù)留出來
iPhone iPad各種控件默認高度
http://blog.csdn.net/chengyakun11/article/details/7565690
3.1.4 一個VC繼承自另一個VC,對于父類的View,如果子類初始化時想不一樣大小,如何在故事板中處理
4 開發(fā)技巧
4.1 View分辨率
4.1.1 wAny和hAny——為什么Xcode6的故事板分辨率是480x480
????????那只是設(shè)計時提供給你的一個默認平臺,并且可以適應(yīng)各種不同大小分辨率。你也可以通過下面的 wAny 和hAny 調(diào)整它的大小,也可以通過調(diào)整 Attributes inspector 指定為具體某種屏幕的大小。通過 AutoLayout 可以實現(xiàn)運行時根據(jù)設(shè)備實際屏幕大小調(diào)整控件位置和大小。
4.2 代碼實例化故事板中的VC
如何裝載Storyboard中的ViewController?
http://blog.csdn.net/ztp800201/article/details/8987005
UIStoryboard* storyboard = [UIStoryboard storyboardWithName: @"Main" bundle: nil];
HJSoreMapViewController* mapVC? = (HJSoreMapViewController*)[storyboard instantiateViewControllerWithIdentifier:@"HJSoreMapViewController"];
Objc代碼
//?從storyboard創(chuàng)建MainViewController
UIStoryboard*?storyboard?=?[UIStoryboard?storyboardWithName: @"nailshop" bundle:[NSBundle?mainBundle]];
YLSMainViewController?*mainViewController?=?(YLSMainViewController*)[storyboard?instantiateViewControllerWithIdentifier: @"mainViewController"];
[self?presentViewController: mainViewController?animated:YES?completion: nil];
????????在調(diào)用之前,需要在storyboard里,給目標ViewController設(shè)置identifier。

5 參考鏈接
IOS編程教程(十):使用StoryBoard來建立導(dǎo)航控制器和表視圖
http://www.cnblogs.com/haichao/archive/2012/11/23/2784144.html
iOS 5故事板入門(1)
http://blog.csdn.net/kmyhy/article/details/11472777
iOS Storyboard全解析
http://www.cnblogs.com/jy578154186/archive/2013/02/27/2934853.html
使用storyboard實現(xiàn)頁面跳轉(zhuǎn),簡單的數(shù)據(jù)傳遞
http://blog.csdn.net/mad1989/article/details/7919504
如何裝載Storyboard中的ViewController?
http://blog.csdn.net/ztp800201/article/details/8987005
用代碼創(chuàng)建并實例化在storyboard中聲明的ViewController