iPad 應用中,UISplitViewController是最具特色的視圖控制器,用於顯示一個從左右分隔的分欄視圖。
建立View–based Appliation項目:SplitVCDemo。用IB開啟SplitVCDemoViewController.xib,點擊File->CreateiPad Version Using…,save as,檔案名稱仍保持“SplitVCDemoViewController.xib”不變,覆蓋:replace。
從Library視窗拖一個SplitView Controller到Document視窗。
可以看到,Split View Controller已有一個Navigation Controller和一個View Controller對象了。在NavitationController下邊還有一個Table View Controller對象,這是用於顯示Split View Controller左邊的列表的控制器,是整個SplitView Controller 中最核心的部分,我們接下來要實現它。
現在,建立一個Table View Controller 子類RootVC,不要選擇With Xib for user interface,RootVC是用來導航ViewController的,它不需要一個.xib檔案。
我們先開啟RootVC.h檔案,聲明幾個變數:
UISplitViewController *splitViewController;
UIPopoverController *popoverController;
UIBarButtonItem *rootPopoverButtonItem;
同時屬性聲明:
@property (nonatomic, assign) IBOutlet UISplitViewController*splitViewController;
@property (nonatomic, retain) UIPopoverController*popoverController;
@property (nonatomic, retain) UIBarButtonItem *rootPopoverButtonItem;
回到SplitVCDemoViewController.xib中,把TableView Controller對象的類別Identity修改為RootVC。
建立串連,將RootVC中的splitViewController和IB對象SplitView Controller串連起來:
同時,將 Split View Controller 的delegate屬性和RootVC串連起來:
這樣就把 Split View Controller 的一些事件委託給RootVC來實現,這需要RootVC實現協議UISplitViewControllerDelegate。在RootVC.h中聲明協議實現,然後在RootVC.m中進行實現:
#pragma mark -
#pragma mark 旋屏支援及UISplitViewControllerDelegate協議實現
// 支援旋屏
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
return YES;
}
// 當splitViewController旋轉為豎屏時,左側邊欄隱藏,同時顯示一個工具列按鈕,用於一個PopOver Controller彈出左側邊欄
- (void)splitViewController:(UISplitViewController*)svc willHideViewController:(UIViewController *)aViewController withBarButtonItem:(UIBarButtonItem*)barButtonItemforPopoverController:(UIPopoverController*)pc {
// 從參數獲得按鈕和popover controller的引用.
barButtonItem.title = @"Root View Controller";
self.popoverController =pc;
self.rootPopoverButtonItem =barButtonItem;
// 擷取右側邊欄,在右側邊欄中顯示按鈕
UIViewController<SubstitutableDetailViewController> *detailViewController = [splitViewController.viewControllers objectAtIndex:1];
[detailViewController showRootPopoverButtonItem:rootPopoverButtonItem];
}
// 當split View controller旋轉為橫屏,左側邊欄顯示,同時消除popover按鈕
- (void)splitViewController:(UISplitViewController*)svc willShowViewController:(UIViewController *)aViewController invalidatingBarButtonItem:(UIBarButtonItem*)barButtonItem {
UIViewController<SubstitutableDetailViewController> *detailViewController = [splitViewController.viewControllers objectAtIndex:1];
// SubstituableDetailViewController協議規定了兩個方法,分別用於顯示/清除popover按鈕
[detailViewControllerinvalidateRootPopoverButtonItem:rootPopoverButtonItem];
// 釋放
self.popoverController =nil;
self.rootPopoverButtonItem =nil;
}
當螢幕豎屏顯示時,Split View Controller會隱藏左側邊欄而只顯示右側邊欄,為了讓使用者能看到左側邊欄,我們需要在右側邊欄的工具條上顯示一個popover按鈕。這樣,在左側邊欄不顯示的情況下,使用者可以通過點擊這個popover按鈕來彈出顯示左側邊欄的內容。為此,我們在RootVC.h中定義了一個協議SubstitutableDetailViewController,要求在必要的時候右側邊欄能實現popover按鈕的顯示方法和消除方法:
@protocolSubstitutableDetailViewController
- (void)showRootPopoverButtonItem:(UIBarButtonItem *)barButtonItem;
- (void)invalidateRootPopoverButtonItem:(UIBarButtonItem *)barButtonItem;
@end
接下來就是實現UITableView的UITableViewDataSource協議和UITableViewDelegate協議。這都是大家都熟悉的內容了:
#pragma mark -
#pragma mark Tableview data source method
- (NSInteger)tableView:(UITableView *)aTableViewnumberOfRowsInSection:(NSInteger)section {
return 2;
}
- (UITableViewCell *)tableView:(UITableView *)aTableViewcellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = @"RootViewControllerCellIdentifier";
UITableViewCell *cell = [self.tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
}
if (indexPath.row == 0) {
cell.textLabel.text = @"First DetailView Controller";
}
else {
cell.textLabel.text = @"Second DetailView Controller";
}
return cell;
}
#pragma mark -
#pragma mark Tableview delegate method
- (void)tableView:(UITableView *)tableViewdidSelectRowAtIndexPath:(NSIndexPath *)indexPath {
NSUInteger row = indexPath.row;
UIViewController <SubstitutableDetailViewController>*detailViewController = nil;
if (row == 0) {
detailViewController= [[DetailVC alloc] initWithNibName:@"DetailVC" bundle:nil];
[detailViewController.view setBackgroundColor:[UIColor redColor]];
}
if (row == 1) {
detailViewController = [[DetailVC alloc] initWithNibName:@"DetailVC" bundle:nil];
[detailViewController.view setBackgroundColor:[UIColor blueColor]];
}
// 修改 split view controller的viewControllers屬性.
NSArray *viewControllers = [[NSArray alloc] initWithObjects:self.navigationController,detailViewController, nil];
splitViewController.viewControllers =viewControllers;
[viewControllers release];
// 如果popover視窗在彈出中,解散
if (popoverController!= nil) {
[popoverController dismissPopoverAnimated:YES];
}
// 重新設定右側邊欄的popover按鈕
if (rootPopoverButtonItem!= nil) {
[detailViewController showRootPopoverButtonItem:self.rootPopoverButtonItem];
}
[detailViewControllerrelease];
}
接下來我們實現右側邊欄的視圖控制器。你可以實現多個View Controller,但為求簡便。我們只實現一個。在 tableViewv:didSelectRowAtIndexPath:代碼中,你可以看到針對每一行的點擊,我們都new了一個DetailVC對象作為右側邊欄的View Controller。如果你願意,你也可以針對每一行的點擊new不同ViewController類作為右側邊欄。這樣,自然就能通過左邊的列表導航到多個Detail View Controller了。
建立View Controller類DetailVC,這次需要一個.xib檔案。 開啟DetailVC.h,增加變數聲明:
UIToolbar *toolbar;
將變數聲明為出口:
@property (nonatomic, retain) IBOutlet UIToolbar *toolbar;
然後,在DetailVC.m中實現以下方法:
#pragma mark SubstitutableDetailViewController協議實現
- (void)showRootPopoverButtonItem:(UIBarButtonItem *)barButtonItem {
// Add the popover button to the toolbar.
NSMutableArray*itemsArray = [toolbar.items mutableCopy];
[itemsArray insertObject:barButtonItem atIndex:0];
[toolbar setItems:itemsArray animated:NO];
[itemsArray release];
}
- (void)invalidateRootPopoverButtonItem:(UIBarButtonItem*)barButtonItem {
// Remove the popover button from the toolbar.
NSMutableArray *itemsArray= [toolbar.items mutableCopy];
[itemsArray removeObject:barButtonItem];
[toolbar setItems:itemsArray animated:NO];
[itemsArray release];
}
#pragma mark 旋屏支援
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
return YES;
}
開啟DetailVC.xib檔案,將其轉換為iPad版本。然後拖一個Tool Bar在視圖中,並把Tool Bar串連到出口IBOutlettoolbar。
回到SplitVCDemoViewController.xib,把View Controller對象的類別Identity修改為DetailVC:
這樣並不能就顯示出Split View Controller來,因為Split View Controller還沒有把視圖addSubview到視圖控制器的view中。
首先我們要在SplitVCDemoViewController.h中聲明一個出口用於串連Split View Controller對象:
SplitVCDemoViewController *viewController;
⋯⋯
@property (nonatomic, retain) IBOutletSplitVCDemoViewController *viewController;
然後,回到SplitVCDemoViewController.xib,把IB對象Split View Controller串連到出口viewController。
然後在SplitVCDemoViewController.m的viewDidLoad方法中,我們可以載入Split ViewController了:
- (void)viewDidLoad {
// [self.view addSubview:splitVC.view];
//Split View Controller 只能作為window的根視圖控制器
UIWindow* window=[(SplitVCDemoAppDelegate*)[[UIApplication sharedApplication]delegate]window];
window.rootViewController=splitVC;
}
這裡需要說明一點,要把Split View Controller添加到視圖中,你不能使用UIView或者UIWindow的addSubview方法。這個問題說起來很簡單,但網路上並沒有正確的答案,許多解決思路都沒有說在點子上。
蘋果文檔中說明,Split View Controller只能作為應用程式Window的根視圖控制器使用, 很多程式員都在為Split ViewController只能作為根視圖控制器的問題而煩擾,實際上解決的辦法是如此簡單:
我們直接擷取到應用程式委託的window對象,把它的rootViewController屬性設定為Split View Controller執行個體就可以了(原來是這個屬性被設定為SplitVCDemoViewController執行個體對象)。
現在,我們可以運行程式了。模擬器開始總是豎屏啟動的,這時沒有左邊的列表欄。但工具列上有一個popover按鈕,點擊它列表欄以彈出方式顯示: