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按钮,点击它列表栏以弹出方式显示: