前面已介绍过UINavigationController和UITabBarController。和这些视图控制器类似,当初始化UISplitViewController时,也需要传入一组视图控制器。不同的是,UISplitViewController对象只能包含两个视图控制器:一个主视图控制器和一个从视图控制器。某个视图控制器是主还是从,是由该对象在数组中的位置决定的。数组中的第一项是主视图控制器,第二项是从视图控制器。
用Xcode打开Nerdfeed.xcodeproj,然后在项目导航面板中选中BNRAppDelegate.m。
更新application:didFinishLaunchingWithOptions:,检查当前运行的设备是否是iPad,是就创建UISplitViewController对象。iPhone不支持UISplitViewController,如果当前的设备是iPhone,创建UISplitViewController对象则会导致应用抛出异常。代码如下:
- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window
= [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
BNRCoursesViewController *lvc = [[BNRCoursesViewController alloc]
initWithStyle:UITableViewStylePlain];
UINavigationController *masterNav =
[[UINavigationController alloc] initWithRootViewController:lvc];
BNRWebViewController *wvc = [[BNRWebViewController alloc] init];
lvc.webViewController = wvc;
self.window.rootViewController = masterNav;
// 检查当前设备是否是iPad
if ([[UIDevice currentDevice] userInterfaceIdiom] ==
UIUserInterfaceIdiomPad) {
// 必须将webViewController包含在导航视图控制器中,稍后会解释原因
UINavigationController *detailNav =
[[UINavigationController alloc] initWithRootViewController:wvc];
UISplitViewController *svc = [[UISplitViewController alloc] init];
// 将从视图控制器设置为UISplitViewController对象的委托对象
// 稍后会用到(暂时忽略Xcode对这行代码发出的警告信息)
svc.delegate = wvc;
svc.viewControllers = @[masterNav, detailNav];
// 将UISplitViewController对象设置为UIWindow对象的根视图控制器
self.window.rootViewController = svc;
} else {
// 对于非iPad设备,仍然使用导航视图控制器
self.window.rootViewController = masterNav;
}
self.window.backgroundColor = [UIColor whiteColor];
[self.window makeKeyAndVisible];
return YES;
}
这段代码将创建UISplitViewController对象的代码放入了if子句,这是为将Nerdfeed修改为通用应用做准备。基于同样的原因,读者现在应该明白为什么要在UIApplication的委托中创建BNRWebViewController对象,而不是使用常见的模式(由UINavigationController对象的当前视图控制器创建下一个需要压入UINavigationController栈的对象)。创建UISplitViewController对象时,必需设置主、从视图控制器,缺一不可。图22-2显示的是Nerdfeed的UISplitViewController对象图。
图22-2 UISplitViewController对象图
构建并运行应用,如果设备处于竖排模式,应用界面将没有任何内容;将设备旋转至横排模式,就可以在屏幕上看到两个视图控制器。下一节就会解决这个问题,让Nerdfeed在任何方向都可以正确显示。
如果现在按下UITableView对象中的某个表格行,Nerdfeed并不会在右侧面板(从面板)处显示BNRWebViewController对象,而是会将BNRWebViewController对象压入位于左侧面板(主面板)的UINavigationController栈,以替换掉BNRCoursesViewController对象。要解决该问题,需要在用户按下某个表格行时,检查BNRCoursesViewController对象是否属于某个UISplitViewController对象。如果是,就要执行不同的逻辑。
向某个UIViewController对象发送splitViewController消息,可以得到一个指针,并指向该对象所属的UISplitViewController对象。如果UIViewController对象不属于任何UISplitViewController对象,那么splitViewController方法会返回nil(见图22-3)。实际情况要稍微复杂一点,如果某个视图控制器A是某个UISplitViewController对象B的主/从对象之一,对象A的splitViewController方法就会返回对象B。如果某个视图控制器C不是对象B的主/从对象之一,但是对象A包含对象C,那么对象C的splitViewController方法也会返回对象B(本章的BNRCoursesViewController和BNRWebViewController都属于后一种情况)。
图22-3 UIViewController的splitViewController属性
更新BNRCoursesViewController.m中的tableView:didSelectRowAtIndexPath:,在该方法顶部,在将BNRWebViewController对象压入UINavigationController栈前,检查当前的BNRCoursesViewController对象是否属于某个UISplitViewController对象。代码如下:
- (void)tableView:(UITableView *)tableView
didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
NSDictionary *course = self.courses[indexPath.row];
NSURL *URL = [NSURL URLWithString:course[@“url”]];
self.webViewController.title = course[@“title”];
self.webViewController.URL = URL;
[self.navigationController pushViewController:self.webViewController
animated:YES];
if (!self.splitViewController) {
[self.navigationController pushViewController:self.webViewController
animated:YES];
}
}
如果BNRCoursesViewController对象不属于任何UISplitViewController对象,就可以认为当前的设备不是iPad,应该将BNRWebViewController对象压入UINavigation- Controller栈。如果BNRCoursesViewController对象属于某个UISplitViewController对象,就应该由这个UISplitViewController对象负责显示BNRWebViewController对象。
构建并运行应用,转动设备至横屏模式,按下UITableView对象中的某个表格行,Nerdfeed应该会在右侧面板载入相应的课程页面。