【IOS開發(fā)基礎(chǔ)系列】Storyboard專題

1 簡介

1.1 故事板簡介

????????如果你的 app 有大量的窗口帚呼,故事板能幫你減少許多用于從一個(gè)窗口轉(zhuǎn)到另一個(gè)窗口的導(dǎo)航代碼酒觅。與每個(gè)viewcontroller一個(gè)單獨(dú)的 nib 文件不同谜悟,你的 app 只需用一個(gè)故事板文件(其中可以包含所有的viewcontroller 以及它們之間的關(guān)系)即可则吟。

????????與傳統(tǒng)的 nib 文件不同,故事板文件有以下優(yōu)點(diǎn):

? ? 1皇忿、通過一個(gè)故事板文件碉怔,你能對所有的窗口以及它們之間的關(guān)系一目了然。因?yàn)樗械拇翱谠O(shè)計(jì)在一個(gè)故事板文件中禁添,你會(huì)更容易把握每次改動(dòng)帶給每個(gè)窗口的變化撮胧。

? ? 2、在故事板中可以描述各個(gè)窗口之間的轉(zhuǎn)換老翘。這種轉(zhuǎn)換叫做 segue(連接)芹啥。創(chuàng)建 segue 只需用? ? ctrl+ 拖拽從一個(gè) viewcontroller 拖到另一個(gè) viewcontroller 即可。這將減少窗體導(dǎo)航的代碼铺峭。

? ? 3墓怀、哪怕在 tableview 上故事板仍然有用,例如定制? ? tableviewcell卫键。你完全可以在故事板編輯器中設(shè)計(jì)自己的 tableview,這也節(jié)省了不少代碼傀履。


????????點(diǎn)擊 MainStoryboard.storyboard 文件,將打開故事板編輯器:

????????故事板編輯器從外表上看很像是IB莉炉。你可以從 Object Library中拖控件(右下角)到viewcontroller 中钓账,并修改它的布局。不同的是絮宁,故事板中不僅僅包含一個(gè)viewcontroller,而是包含 app 中的所有viewcontroller梆暮。

????????故事板有一個(gè)專門的術(shù)語“場景”,一個(gè)“場景”用于表示一個(gè)viewcontroller绍昂。你以前每個(gè)場景/viewcontroller就要用一個(gè)單獨(dú)的 nib 文件啦粹,但現(xiàn)在所有的東西都集中到了一個(gè)故事板中。

????????對于 iPhone 應(yīng)用窘游,一次可以看一個(gè)場景唠椭,但iPad 應(yīng)用可以一次顯示多個(gè)場景,例如使用splitview 的“主-細(xì)”窗口忍饰,或者用popovercontroller 彈出內(nèi)容贪嫂。


1.2 程序加載

????????如果你以前創(chuàng)建過基于 nib 的app(譯者注:Xcode 3.x),你可能知道MainWindow.xib 文件喘批。這個(gè)nib 文件中包含了一個(gè)頂層的連接到 App Delegate 的UIWindow 對象撩荣,以及一個(gè)或多個(gè)viewcontroller铣揉。但是饶深,當(dāng)你使用故事板的時(shí)候餐曹,所有的UI 都放到了一個(gè)故事板中, MainWindow.xib 不再使用敌厘。

????????那么台猴,在沒有 MainWindow.xib 文件的情況下,故事板是怎樣被加載到app中的呢?

????????打開 AppDelegate.h俱两,你將看到這幾句:

#import

@interface?AppDelegate?: UIResponder

@property?(strong, nonatomic)?UIWindow?*window;

@end

????????當(dāng)使用故事板的時(shí)候饱狂,應(yīng)用程序委托必須從 UIResponder 開始繼承(原先則直接從NSObject繼承),同時(shí)還有一個(gè) UIWindow 屬性(不同的是宪彩,它不是一個(gè)IBOutlet)休讳。

????????在 AppDelegate.m 中,它實(shí)際上什么也沒做尿孔,所有的方法都是空的俊柔。甚至application:didFinishLaunchingWithOptions:也只是簡單地返回 Yes。如果是過去活合,要么要在這里加入主viewcontroller的 view 到 window雏婶,要么設(shè)置window 的 rootViewController 屬性。但現(xiàn)在什么都沒有白指。

????????秘密都位于 Info.plist 文件留晚。打開Ratings-Info.plist(在Supporting Files 文件組),在 nib-based 的項(xiàng)目中告嘲,Info.plist 文件中有一個(gè)名為NSMainNibFile 或者 Main nib file base name 的鍵错维,它會(huì)導(dǎo)致UIApplication去加載 MainWindow.xib 并將之連接到 app 中。現(xiàn)在橄唬,Info.plist中不再有這個(gè)設(shè)置需五。

????????與之對應(yīng)的是,故事板應(yīng)用程序使用 UIMainStoryboardFile或者“Main storyboard file base name”鍵轧坎。它要求應(yīng)用程序在啟動(dòng)時(shí)需要加載的故事板文件名宏邮。當(dāng)這個(gè)鍵缺失時(shí),UIApplication將默認(rèn)加載MainStoryboard.storyboard 文件并自動(dòng)將故事板中第一個(gè)viewcontroller 初始化并放到一個(gè)新創(chuàng)建的UIWindow 對象中缸血。這一切不再需要手工編寫代碼蜜氨。

????????你可以查看 Target 的 Summary 窗口:

????????新增的 iPhone/iPodDeployment Info 小節(jié)下面,可以讓你選擇是從故事板文件啟動(dòng)還是從nib文件啟動(dòng)捎泻。

????????為了更清楚一點(diǎn)飒炎,可以打開 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() 的最后一個(gè)參數(shù)為nil,但現(xiàn)在是 NSStringFromClass([AppDelegate

class])笆豁。

????????與使用 MainWindow.xib 的時(shí)候不同郎汪,故事板中不會(huì)包含應(yīng)用程序委托赤赊。由于無法從nib中加載應(yīng)用程序委托,也無法從故事板文件中加載應(yīng)用程序委托煞赢,我們必須告訴UIApplicationMain 應(yīng)用程序委托類的名稱抛计,否則它根本無法找到應(yīng)用程序委托類。


2 使用

2.1 結(jié)合xib使用

????思路:

? ? ? ? APP跳轉(zhuǎn)流程在故事板中呈現(xiàn)照筑,但是VC的事件處理與詳細(xì)視圖設(shè)計(jì)放在xib文件中吹截。

????具體實(shí)現(xiàn)方法:

? ? ? ? ?例如,在故事板中Level1VC中添加一個(gè)按鈕凝危,然后將level2VC拖入故事板波俄,按住cmd鍵,鼠標(biāo)點(diǎn)擊按鈕并拖入level2VC蛾默,在彈出的菜單中選擇show事件懦铺,即完成了從Level1VC跳轉(zhuǎn)到Level2的操作。


2.2 使用TabBar

2.2.1 TabBarController添加

????????Ratings 程序有一個(gè) Tabbar支鸡,包含了兩個(gè)viewcontroller冬念。使用故事板創(chuàng)建Tabbar 是小事一碟。

????????切換到MainStoryboard.storyboard苍匆,拖一個(gè) TabBarController 到畫布中刘急。你可能得將Xcode窗口最大化,因?yàn)門abBarController跟兩個(gè)viewcontroller 聯(lián)系在一起浸踩,你可能需要更多的空間才能靈活操作叔汁。

????????新的 TabBarController 已經(jīng)事先配置了兩個(gè)ViewController,每個(gè)Tab 按鈕一個(gè)检碗。UITabBarController 是一種ViewController 的容器据块,它包含了多個(gè)viewcontroller。其它類似的容器還有NavigationController 和 SplitViewController(后面都會(huì)介紹)折剃。iOS5有個(gè)更酷的特性是你可以寫自己的 ViewController 容器——在本書后續(xù)教程中介紹另假。

????????TabBarController和所包含的ViewController的包容關(guān)系用一個(gè)箭頭(中間有一個(gè)小圓圖標(biāo))表示。

? ??????注意:如果要把TabBarController與其包含的ViewController一起移動(dòng)怕犁,用Cmd+左鍵將它們?nèi)窟x中然后移動(dòng)(選中的場景會(huì)有一個(gè)淺藍(lán)色的方框框妆呃骸)。

????????在第一個(gè) ViewController 中放入一個(gè)Label 然后輸入文本“FirstTab”奏甫。在第2個(gè)ViewController中放入一個(gè) Label 并輸入文本“Second Tab”戈轿。這樣我們就能在切換Tab 時(shí)區(qū)分兩個(gè)ViewController。

? ??????注意:你不能在編輯器的縮放模式下向場景拖放東西阵子,必須首先恢復(fù)到普通模式下思杯。

????????選中 TabBarController并打開屬性面板。勾選“ IsInitial View Controller”選項(xiàng)挠进。

????????在畫布中色乾,原來指向最初的 ViewController 的箭頭誊册,現(xiàn)在指向了TabBarController。也就是說暖璧,程序運(yùn)行時(shí)案怯,UIApplication 會(huì)將TabBarController作為應(yīng)用程序的第一個(gè)ViewController。故事板總是以一個(gè) ViewController 作為“initialview controller”漆撞,即故事板的入口殴泰。

????????運(yùn)行程序∮谥妫現(xiàn)在的程序浮驳,可以通過TabBarController 在兩個(gè)ViewController 中切換。

????????Xcode 其實(shí)有一個(gè)專門用于Tabbar 應(yīng)用程序的模板(叫做 Tabbed Application 模板),當(dāng)然 我們也可以使用這個(gè)模板捞魁。但在某些時(shí)候我們必須通過手動(dòng)創(chuàng)建TabbarController至会,這樣就必須知道在不使用模板時(shí)應(yīng)該如何去做。

????????現(xiàn)在你可以刪除項(xiàng)目模板原來創(chuàng)建的那個(gè)viewcontroller谱俭,我們不再需要它奉件。這樣故事板中只會(huì)有一個(gè)TabbarController和它的兩個(gè) viewcontroller。

????????以這種方式昆著,你可以創(chuàng)建超過 5 個(gè)的ViewController 給TabBarController县貌,它將自動(dòng)在Tabbar 上顯示 More... 按鈕。

2.2.2 添加TableView Controller

????????被連接到 TabBarController 的兩個(gè)場景只是一般的UIViewController〈斩現(xiàn)在我們要其中的第一個(gè)替換為UITableViewController煤痕。

????????選中第一個(gè) ViewController,刪除它接谨。拖一個(gè)TablViewController到畫布中摆碉。選中 TablViewController,選擇菜單“Editor\EmbedIn\Navigation”。

????????這將導(dǎo)致增加一個(gè) ViewController 到畫布中:

????????當(dāng)然你也可以直接從 Object Library 中拖一個(gè)NavigationController,但 Embed In 命令更簡單一些脓豪。

????????由于 NavigationController 也是一種ViewController容器巷帝,它和 TableViewController 之間也有一個(gè)箭頭表示二者關(guān)系。在文檔樹中這些關(guān)系顯示如圖中所示:

????????注意 TableViewController 上被加入了一個(gè)navigationBar扫夜。這是故事板編輯器自動(dòng)放入的楞泼,因?yàn)檫@個(gè)場景現(xiàn)在將在NavigationController 的 frame 內(nèi)顯示。當(dāng)然笤闯,這并不是真正的UINavigationBar對象堕阔,而只是一個(gè)模擬的“假”的導(dǎo)航條。

????????打開 TableViewcontroller 的屬性面板望侈,我們可以看到頂部有一個(gè)Simulated metrics 小節(jié)印蔬。

????????故事板默認(rèn)使用“Inferred”(依靠推斷)設(shè)置,意思是該場景如果在NavigationController中顯示則會(huì)顯示導(dǎo)航條脱衙,如果在 TabBarController 中顯示則會(huì)顯示TabBar侥猬,等等例驹。如果你需要的話也可以改變這些設(shè)置,但請明白退唠,這些設(shè)置僅僅是幫助你設(shè)計(jì)你的屏幕鹃锈,Simulated Metrics 并不會(huì)用于運(yùn)行時(shí),它們僅僅是幫助你進(jìn)行可視化設(shè)計(jì)的瞧预。

????????現(xiàn)在將新場景連接到TabBarController 屎债。ctrl+左鍵,從TabBarController拖一條線到NavigationController.

????????拖完后將顯示彈出菜單,請選擇Relationship-viewControllers垢油。這將在這兩者間創(chuàng)建新的關(guān)系:

????????TabBarController 現(xiàn)在有兩個(gè)關(guān)系盆驹,一個(gè)Tab 一個(gè)。NavigationController自己有一個(gè)關(guān)系滩愁,連接的是TableViewController躯喇。此外還有另外一種箭頭,“segue”硝枉,我們在后面講廉丽。

????????創(chuàng)建新連接時(shí),新的 Tab 也同時(shí)被加到TabBarController上妻味,名字叫做 “Item”正压。我想將新的場景放在第一個(gè)Tab上,可以用拖拽 Tab 的方式改變它們的順序责球。

????????運(yùn)行程序焦履,現(xiàn)在第一個(gè) Tab 已經(jīng)變成了NavigationController。

????????在我們將實(shí)際的功能加入 app 之前棕诵,讓我們整理一下我們的故事板裁良。我想將第一個(gè)tab命名為 Players,第2個(gè) tab 命名為Gestures校套。你不需要去改變 TabBarController价脾,而是要改變與tab 對應(yīng)的ViewController。

????????當(dāng)你將一個(gè) ViewController 連接到TabBarController時(shí)笛匙,會(huì)在 ViewController 上創(chuàng)建一個(gè)TabBarItem 對象侨把。通過 TabBarItem 對象,你可以設(shè)置Tab的 Title 和圖片妹孙。

????????選擇 NavigationController 上的TabBarItem對象秋柄,在屬性面板,設(shè)置它的 Title 為Players蠢正。將第二個(gè) ViewController 的TabBarItem 重命名為Gestures骇笔。

????????我們還可以在 Tab 上放入圖片。在本教程源代碼中有一個(gè)文件夾Images。將該文件夾添加到項(xiàng)目中去笨触。在TabBarItem “Guestures”的屬性面板懦傍,將 Players.png 設(shè)為它的image。將TabBarItem “Players”的 image 設(shè)置為Players.png芦劣。

????????與之相仿粗俱,在 NavigationController 所包含的ViewController上,有一個(gè) NavigationItem 對象,可用于設(shè)置導(dǎo)航欄。選擇TableViewController 上的NavigationItem洁奈,在屬性面板中將title 修改為 Players。

????????當(dāng)然偏塞,你也可以通過簡單地雙擊 NavigationBar 來修改title(注意:你應(yīng)該雙擊TableViewController 上的“假”導(dǎo)航條,而不是雙擊NavigationController 上的真導(dǎo)航條模庐。

????????運(yùn)行程序烛愧,不需要你編寫一行代碼油宜,我們定制的 Tab 欄就顯示出來了掂碱。

2.3 模板cells

2.3.1 模板cells使用

????????注意到當(dāng)你加入 tableViewController 后,Xcode會(huì)發(fā)出警告了嗎慎冤?

? ? ? ? “UnsupportedConfiguration: Prototype table cells must have reuse identifiers”疼燥,當(dāng)加入一個(gè)TableViewController到故事板后,Xcode 默認(rèn)會(huì)使用一種 prototype cells 的單元格(模板cells)蚁堤。但我們并沒有配置它醉者,因此會(huì)有這個(gè)警告。

????????模板 cells 是一種很酷的故事板特性披诗。它遠(yuǎn)勝于原來的nib 文件撬即。在以前,如果你要定制表視圖單元格呈队,你要么在代碼中向cell對象添加自己的 subviews 剥槐,要么新建一個(gè) nib 然后從nib 中加載你自己的 cell。但模板 cells 的出現(xiàn)簡化了這一切宪摧,現(xiàn)在你可以直接在故事板編輯器中設(shè)計(jì)你自己的表視圖單元格粒竖。

2.3.2 新建PlayerCell

????????TableViewController 上自帶有一個(gè)空白的模板cell。點(diǎn)擊這個(gè)cell几于,你可以在屬性面板中設(shè)置它的樣式為Subtitle蕊苗。這會(huì)使 cell 變成包含有兩個(gè)label 的 cell。如果你曾經(jīng)自己手動(dòng)創(chuàng)建過TableViewCell沿彭,你應(yīng)該知道這就是UITableViewCellStyleSubtitle樣式朽砰。通過模板 cells,你可以創(chuàng)建內(nèi)置樣式的cell,也可以創(chuàng)建完全定制的cell(我們馬上就會(huì)提到)瞧柔。

????????將 Accessory 屬性改為Disclosure Indicator 然后將 Reuse Identifier (復(fù)用ID) 設(shè)置為“PlayerCell”熔掺。這樣 Xcode 會(huì)立即消除警告。所有的模板 cells 仍然是普通的 UITableViewCell 對象非剃,仍然會(huì)帶有一個(gè)復(fù)用 ID置逻,Xcode僅僅是提示我們別忘了設(shè)置它(至少會(huì)讓我們注意到這個(gè)警告)。

????????運(yùn)行程序备绽,什么都沒有改變券坞。不要奇怪,我們還沒有提供數(shù)據(jù)源肺素,因此表視圖中不會(huì)顯示任何行恨锚。

2.3.3 創(chuàng)建PlayersViewController

????????加一個(gè)新的 File 到項(xiàng)目中。選擇UIViewController subclass 模板倍靡。將類命名為PlayersViewController ,確保它繼承于UITableViewController猴伶。不要選擇“WithXib...”選項(xiàng),因?yàn)槲覀冊诠适掳嬷幸呀?jīng)為這個(gè)類設(shè)計(jì)了一個(gè)UI塌西。我們不再需要nib!

????????回到故事版編輯器他挎,選擇 TableViewController。在Identity 面板捡需,將它的 Class 設(shè)置為PlayersViewController办桨。這一步很重要,因?yàn)檫@會(huì)將位于故事版中的一個(gè)場景與你自己的 ViewController子類關(guān)聯(lián)起來站辉。千萬記得這個(gè)步驟呢撞,否則你創(chuàng)建類將完全沒有用處!

????????從現(xiàn)在開始饰剥,運(yùn)行程序后故事板中的tableViewController 將變成我們的PlayersViewController 類的一個(gè)實(shí)例殊霞。

????????在 PlayersViewController.h 文件中加入一個(gè)可變數(shù)組屬性:

#import <UIKit/UIKit.h>

@interface?PlayersViewController?: UITableViewController

????@property?(nonatomic, strong)?NSMutableArray?*players;

@end

????????這個(gè)數(shù)組將存儲(chǔ)應(yīng)用程序中的模型數(shù)據(jù),即Player (玩家)對象汰蓉。

2.3.4 創(chuàng)建數(shù)據(jù)模型Player類

????????現(xiàn)在創(chuàng)建Player 類绷蹲。創(chuàng)建一個(gè)新的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 是一個(gè)簡單對象岩齿,擁有3個(gè)屬性:玩家姓名太颤、玩家所玩的游戲、以及等級(1-5星)盹沈。

2.3.5 構(gòu)建測試數(shù)據(jù)源

????????我們將在 AppDelegate 中放入一個(gè)數(shù)組龄章,并在數(shù)組中放入一些Player對象進(jìn)行測試吃谣。這個(gè)數(shù)組將被賦值給 PlayerViewController 的players 屬性。

????????在 AppDelegate.m做裙,加入Player類和PlayersViewController類的導(dǎo)入語句岗憋,加入一個(gè)實(shí)例變量叫做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在哪里监徘,因此我們不得不把它從故事板中找出來。這是使用故事板的一個(gè)令我煩心不已的不足吧碾。如果是使用IB ,在 MainWindow.xib中會(huì)有應(yīng)用程序委托的一個(gè)引用凰盔,同時(shí)你可以將頂層的ViewController 連接到應(yīng)用程序委托的IBOutlet 屬性。但現(xiàn)在使用故事板就不可能了倦春。在頂層ViewController 中不能再引用應(yīng)用程序委托户敬。這真是個(gè)不幸,我們只能通過代碼方式獲得引用睁本。

UITabBarController?*tabBarController?=?(UITabBarController?*)self.window.rootViewController;

????????我們知道故事板的 initial view controller 是一個(gè)TabBarController尿庐,所以我們可以從 window 對象的 rootViewController 獲得它的一個(gè)引用并進(jìn)行類型轉(zhuǎn)換。

????????PlayersViewController 位于第一個(gè)tab 的NavigationController 容器中添履,因此我們先獲得UINavigationController 對象:

UINavigationController?*navigationController?=?[[tabBarController viewControllers]?objectAtIndex: 0];

????????然后在 NavigationController 的rootViewController屁倔,可以獲得PlayersViewController :

PlayersViewController?*playersViewController?=????[[navigationController viewControllers] objectAtIndex: 0];

????????但是,UINavigationController 沒有 rootViewController屬性暮胧。因此我們必須從viewControllers 數(shù)組中檢索。(它有一個(gè) topViewController 屬性问麸,但那個(gè)是位于viewControllers棧頂?shù)?view controller往衷。而我們要的是棧低的 view controller。雖然在程序剛啟動(dòng)的時(shí)候严卖,棧頂和棧底實(shí)際上是一個(gè)席舍,你也可以使用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ù)次地在這個(gè)地方編寫自己的 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;

}

????????代碼變得更簡單了! 其實(shí)你只需要從這里獲得新的cell :

UITableViewCell?*cell?=?[tableView dequeueReusableCellWithIdentifier:?@"PlayerCell"];

????????不再需要復(fù)用單元格了滑黔,它會(huì)自動(dòng)從模板 cell 獲得一份拷貝給你使用!你只需要提供復(fù)用的ID(你曾經(jīng)在故事版編輯器中為模板cell設(shè)置過的,在本例中,即“PlayerCell”)略荡。記得設(shè)置這個(gè)ID庵佣,否則模板cell 不會(huì)生效。

????????由于 PlayersViewController 不認(rèn)識(shí)Player 類汛兜,你還需要導(dǎo)入Player 類的頭文件:

#import "Player.h"

????????此外還要合成 players 屬性:

@synthesize players;

????????運(yùn)行程序巴粪,如下圖所示:

? ??????注意:在本例中,我們只用了一種模板 cell粥谬,如果你需要顯示多種cell验毡,你可以加入更多的模板cell。你可以復(fù)制已有的模板cell為新的cell帝嗡,也可以增加TableView Prototype Cells 屬性值晶通。注意,確保每個(gè)模板 cell 都有自己的復(fù)用ID哟玷。

????????使用神奇的模板cell只需一行代碼狮辽,這是件了不起的事情!

2.3.7 設(shè)計(jì)完全自定義的模板cell

????????對于大部分 app巢寡,使用標(biāo)準(zhǔn)的cell 樣式就足矣喉脖。但我想在單元格右邊加一張圖片以顯示玩家級別(以星級的形式)。UITableViewCell的標(biāo)準(zhǔn)樣式中不包含可以在單元格中放入一個(gè)ImageView抑月,因此我只能選擇定制設(shè)計(jì)树叽。

????????回到MainStoryboard.storyboard,選擇模板cell谦絮,將Style屬性設(shè)置為 Custom题诵。默認(rèn)的 label 將消失。

????????首先增加 cell 的高度為55 像素层皱。拖拽它下端的拉柄可以改變它的高度性锭,也可以修改Size 面板中的Row height 值。????

????????拖兩個(gè) Label 到Cell 中叫胖,將它們放置到大致等于原先所在的位置草冈。隨意修改它們的字體和顏色。將兩個(gè)label的高亮色為白色瓮增。這樣當(dāng)用戶點(diǎn)擊 cell 時(shí)看起來會(huì)好一些怎棱,因?yàn)榇藭r(shí)cell的背景為藍(lán)色。

????????拖一個(gè) ImageView 到cell 右端绷跑,緊靠著右箭頭拳恋。調(diào)整它寬度為81,高度無所謂你踩。設(shè)置它的Mode 為 Center(在屬性面板的 View 下面)以便當(dāng)我們將圖片放入時(shí)它不會(huì)被拉伸诅岩。

????????我將倆個(gè) label 的寬度設(shè)置為210讳苦,這樣不會(huì)遮住ImageView。最終設(shè)計(jì)完成是這個(gè)樣子:

????????由于是定制單元格吩谦,我們不再使用cell 的 textLabel 和detailTextLabel 屬性來顯示文本鸳谜。這兩個(gè)標(biāo)簽的屬性在我們的cell 中也不再存在。

????????我們將通過 tag 檢索我們想要的 Label式廷。對于 Name 標(biāo)簽咐扭,tag設(shè)置為100,對于 Game 標(biāo)簽滑废,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)用了一個(gè)新方法imageForRating薛闪,這個(gè)方法實(shí)現(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;

}

????????再次運(yùn)行程序。

????????啊哈俺陋,看起來有點(diǎn)不太對勁豁延。我們修改了模板cell 的高度,但tableView 并不知道腊状。有兩個(gè)辦法:改變table view 的 Row Height 屬性诱咏,或者修改?heightForRowAtIndexPath 方法。前者更為簡單缴挖,因此我使用了前者袋狞。

? ??????注意:如果你事先無法確定 cell 高度,或者你有不同高度的幾種 cell映屋,你應(yīng)該使用heightForRowAtIndexPath苟鸯。

????????返回MainStoryboard.storyboard,在TableView的 Size 面板中秧荆,將 Row Height 設(shè)置為55倔毙。

????????如果你用拖拽而不是直接鍵入的方式改變cell 的高度,tableview 的 Row Height 屬性也會(huì)自動(dòng)隨之改變乙濒。

????????再次運(yùn)行程序,這次看起來就好多了卵蛉。

2.3.8 子類化模板Cell

????????我們的 Table? View 看起來不錯(cuò)吧颁股!但我并不喜歡用tag 去訪問 UILabel 和其他 cell 的 subview。如果這些Label 能連接到IBOutlet 屬性豈不是更好傻丝?

????????在項(xiàng)目中添加新的 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)容不多亏掀,僅僅是加了幾個(gè)屬性忱反,nameLabel, gameLabel 以及 ratingImageView。

????????回到MainStoryboard.storyboard滤愕,選擇模板cell 温算,在 Identity 面板改變其 Class 為“PlayerCell”。這樣當(dāng)你用dequeueReusableCellWithIdentifier 方法獲得一個(gè) cell時(shí)间影,它實(shí)際上返回一個(gè)PlayerCell給你注竿。

????????注意,我將類的名字和重用 ID 取成了一樣——都叫做 PlayerCell——這僅僅是因?yàn)槲蚁矚g這樣魂贬。其實(shí)二者毫無干系巩割,你完全讓它們不一樣。

????????選擇付燥,你可以將 label 和ImageView 連接到IBOutlet宣谈。選中Label 然后從它的連接面板拖一條線到TableViewCell,或者用 Ctrl+左鍵從TableViewCell? 拖到 Label 上。

? ??????重點(diǎn):你可以在控件和 TableViewCell 間建立連接键科,而不僅僅是在控件和 ViewController 間建立連接闻丑!如你所見,當(dāng)你的數(shù)據(jù)源用 dequeueReusableCellWithIdentifier Table View 請求新的單元格時(shí)萝嘁,TableView并不真正把模板 cell 給你梆掸,它只是給你一份模板 cell 的拷貝(也可能是一個(gè)已經(jīng)存在的cell——在復(fù)用的情況下)。也就是說任何時(shí)候都存在多個(gè) PlayerCell 實(shí)例牙言。如果你連接 cell 上的一個(gè)Label ViewController IBOutlet上酸钦,那么會(huì)有多個(gè)Label 在試圖使用相同的 IBOutlet。那就麻煩了咱枉。(順便說一句卑硫,如果在你的Cell上有一個(gè) 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āng)然,你仍然需要導(dǎo)入 PlayerCell 類:

#import "PlayerCell.h"

????????運(yùn)行程序聊训,跟前面一模一樣抱究,但在表格中使用的是我們自己的TableViewCell 子類。

????????還有一些設(shè)計(jì)技巧带斑。在設(shè)計(jì)自己的TableViewCell 時(shí)鼓寺,你需要注意一些地方勋拟。首先,你應(yīng)當(dāng)設(shè)置Label 的 Highlighted Color(高亮色) 妈候,以便用戶在點(diǎn)擊表格行時(shí)感覺更好敢靡。

????????其次,你應(yīng)當(dāng)確保添加的內(nèi)容能自動(dòng)適應(yīng)單元格尺寸的變化州丹。例如醋安,當(dāng)你需要表格行能夠被刪除或移動(dòng)時(shí) ,Cell 尺寸會(huì)發(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];

????????}

}

????????實(shí)現(xiàn)這個(gè)方法后吓揪,表格的“輕掃以刪除”功能被開啟。運(yùn)行程序所计,在某行上進(jìn)行輕掃手勢柠辞,看看會(huì)發(fā)生什么。

????????刪除按鈕出現(xiàn)在 cell 上主胧,但它同時(shí)也遮住了等級圖片叭首。實(shí)際上是因?yàn)閯h除按鈕占據(jù)了部分cell空間,而 cell 大小隨之改變踪栋,ImageView 卻沒有改變焙格。

????????要解決這個(gè)問題,打開 MainStoryBoard.storyboard夷都,選擇 ImageView 眷唉,在 Size 面板中修改 Autosizing 以便它始終位于 superview 的右端:

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

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

????????你也可以在刪除按鈕出現(xiàn)時(shí)讓星星們消失,這就留給讀者們自己去實(shí)現(xiàn)了党饮。重要的是肝陪,你應(yīng)該在設(shè)計(jì)TableViewCell 時(shí)對這些細(xì)節(jié)性的東西一清二楚。

3 設(shè)計(jì)原則

3.1 布局設(shè)計(jì)原則

3.1.1 一個(gè)控件的布局盡量只采用一種方式刑顺,要么是Storyboard(XIB)要么是代碼

????????因?yàn)橐晥D在刷新時(shí)氯窍,會(huì)直接從Storyboard中加載控件的大小,而不是代碼6滋谩\衤俊!

3.1.2 不能同時(shí)設(shè)置一個(gè)控件橫向或縱向的相對間距后贯城,又去設(shè)置絕對尺寸,否則會(huì)導(dǎo)致控件不能顯示霹娄,也不會(huì)報(bào)錯(cuò)能犯!

3.1.3 在故事板中進(jìn)行布局設(shè)計(jì)時(shí)鲫骗,如果有導(dǎo)航欄、Tab欄踩晶,必須也要把高度預(yù)留出來

iPhone iPad各種控件默認(rèn)高度

http://blog.csdn.net/chengyakun11/article/details/7565690


3.1.4 一個(gè)VC繼承自另一個(gè)VC执泰,對于父類的View,如果子類初始化時(shí)想不一樣大小渡蜻,如何在故事板中處理


4 開發(fā)技巧

4.1 View分辨率

4.1.1 wAny和hAny——為什么Xcode6的故事板分辨率是480x480

????????那只是設(shè)計(jì)時(shí)提供給你的一個(gè)默認(rèn)平臺(tái)术吝,并且可以適應(yīng)各種不同大小分辨率。你也可以通過下面的 wAny 和hAny 調(diào)整它的大小茸苇,也可以通過調(diào)整 Attributes inspector 指定為具體某種屏幕的大小排苍。通過 AutoLayout 可以實(shí)現(xiàn)運(yùn)行時(shí)根據(jù)設(shè)備實(shí)際屏幕大小調(diào)整控件位置和大小。

4.2 代碼實(shí)例化故事板中的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里,給目標(biāo)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實(shí)現(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)建并實(shí)例化在storyboard中聲明的ViewController

http://kyfxbl.iteye.com/blog/1997502

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末哭靖,一起剝皮案震驚了整個(gè)濱河市具垫,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌试幽,老刑警劉巖筝蚕,帶你破解...
    沈念sama閱讀 221,635評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異抡草,居然都是意外死亡饰及,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,543評論 3 399
  • 文/潘曉璐 我一進(jìn)店門康震,熙熙樓的掌柜王于貴愁眉苦臉地迎上來燎含,“玉大人,你說我怎么就攤上這事腿短∑凉浚” “怎么了?”我有些...
    開封第一講書人閱讀 168,083評論 0 360
  • 文/不壞的土叔 我叫張陵橘忱,是天一觀的道長赴魁。 經(jīng)常有香客問我,道長钝诚,這世上最難降的妖魔是什么颖御? 我笑而不...
    開封第一講書人閱讀 59,640評論 1 296
  • 正文 為了忘掉前任,我火速辦了婚禮凝颇,結(jié)果婚禮上潘拱,老公的妹妹穿的比我還像新娘疹鳄。我一直安慰自己,他們只是感情好芦岂,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,640評論 6 397
  • 文/花漫 我一把揭開白布瘪弓。 她就那樣靜靜地躺著,像睡著了一般禽最。 火紅的嫁衣襯著肌膚如雪腺怯。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,262評論 1 308
  • 那天川无,我揣著相機(jī)與錄音呛占,去河邊找鬼。 笑死舀透,一個(gè)胖子當(dāng)著我的面吹牛栓票,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播愕够,決...
    沈念sama閱讀 40,833評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼走贪,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了惑芭?” 一聲冷哼從身側(cè)響起坠狡,我...
    開封第一講書人閱讀 39,736評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎遂跟,沒想到半個(gè)月后逃沿,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,280評論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡幻锁,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,369評論 3 340
  • 正文 我和宋清朗相戀三年凯亮,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片哄尔。...
    茶點(diǎn)故事閱讀 40,503評論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡假消,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出岭接,到底是詐尸還是另有隱情富拗,我是刑警寧澤,帶...
    沈念sama閱讀 36,185評論 5 350
  • 正文 年R本政府宣布鸣戴,位于F島的核電站啃沪,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏窄锅。R本人自食惡果不足惜创千,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,870評論 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧签餐,春花似錦寓涨、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,340評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽体捏。三九已至冠摄,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間几缭,已是汗流浹背河泳。 一陣腳步聲響...
    開封第一講書人閱讀 33,460評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留年栓,地道東北人拆挥。 一個(gè)月前我還...
    沈念sama閱讀 48,909評論 3 376
  • 正文 我出身青樓,卻偏偏與公主長得像某抓,于是被迫代替她去往敵國和親纸兔。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,512評論 2 359

推薦閱讀更多精彩內(nèi)容

  • 這是一篇翻譯作品,水平有限,希望各位指正否副。各位同學(xué)最好去看原文:Storyboards Tutorial in i...
    Eddy_0閱讀 1,081評論 0 0
  • 各位考生請注意: 快坐好汉矿! 考試就要開始了。 請拿出2B鉛筆和橡皮擦 手機(jī)關(guān)機(jī)备禀,并交到講臺(tái)上 考試期間手機(jī)若響起來...
    黃展鋒閱讀 327評論 3 4
  • 昨天聽了項(xiàng)目老師介紹她所支教的山區(qū)學(xué)校洲拇。出乎我意料,跟我想象中完全不一樣曲尸。 從學(xué)校來看赋续,我想象中的是破破舊舊的,墻...
    小小小grow閱讀 173評論 0 0
  • 我飲良茶 伴君千里外 等若思 不及紅塵一曲 你最愛 交錯(cuò)開 終是我欽慕 你不愛
    老瘦雜閱讀 177評論 0 0