我真的不相信在 UISplitViewController (例如登录表单) 之前显示一些 UIViewController 的概念竟然如此复杂,直到我不得不创建那种视图层次结构。
我的示例基于 iOS 8 和 XCode 6.0 (Swift),所以我不确定这个问题之前是否以相同的方式存在,或者是由于 iOS 8 引入了一些新错误,但来自我发现的所有类似问题,我都没有看到这个问题的完整“不是很hacky”的解决方案。
在我最终找到解决方案之前,我将指导您完成一些我尝试过的事情(在本文结尾处)。每个示例都基于在未启用 CoreData 的情况下从 Master-Detail 模板创建新项目。
第一次尝试(模式 segue 到 UISplitViewController):
- 创建新的 UIViewController 子类(例如 LoginViewController)
- 在情节提要中添加新的视图控制器,将其设置为初始视图控制器(而不是 UISplitViewController)并将其连接到 LoginViewController
- 将 UIButton 添加到 LoginViewController 并从该按钮创建模态 segue 到 UISplitViewController
- 将 UISplitViewController 的样板设置代码从 AppDelegate 的
didFinishLaunchingWithOptions 移动到 LoginViewController 的 prepareForSegue
这几乎奏效了。我说差不多了,因为在使用 LoginViewController 启动应用程序并点击按钮并转到 UISplitViewController 后,会出现一个奇怪的错误:在方向更改时显示和隐藏主视图控制器不再是动画。
在解决这个问题一段时间后没有真正的解决方案,我认为它与 UISplitViewController 必须是 rootViewController 的 奇怪规则 有某种联系(在这种情况下它不是,LoginViewController 是)所以我放弃了这个不太完美的解决方案。
第二次尝试(来自 UISplitViewController 的模态转场):
- 创建新的 UIViewController 子类(例如 LoginViewController)
- 在情节提要中添加新的视图控制器,并将其连接到 LoginViewController(但这次将 UISplitViewController 保留为初始视图控制器)
- 创建从 UISplitViewController 到 LoginViewController 的模态序列
- 将 UIButton 添加到 LoginViewController 并从该按钮创建 unwind segue
最后,将此代码添加到 AppDelegate 的 didFinishLaunchingWithOptions 后,用于设置 UISplitViewController 的样板代码:
window?.makeKeyAndVisible()
splitViewController.performSegueWithIdentifier("segueToLogin", sender: self)
return true
或尝试使用此代码:
window?.makeKeyAndVisible()
let loginViewController = splitViewController.storyboard?.instantiateViewControllerWithIdentifier("LoginVC") as LoginViewController
splitViewController.presentViewController(loginViewController, animated: false, completion: nil)
return true
这两个例子都产生了同样的几个坏事:
- 控制台输出:
Unbalanced calls to begin/end appearance transitions for <UISplitViewController: 0x7fc8e872fc00>
- UISplitViewController 必须在 LoginViewController 以模态方式连接之前首先显示(我宁愿只显示登录表单,这样用户在登录前看不到 UISplitViewController)
- Unwind segue 不会被调用(这完全是另一个错误,我现在不打算讨论这个故事)
解决方案(更新 rootViewController)
我发现唯一可以正常工作的方法是动态更改窗口的 rootViewController:
- 为 LoginViewController 和 UISplitViewController 定义 Storyboard ID,
并向 AppDelegate 添加某种登录属性。
- 基于此属性,实例化适当的视图控制器,然后将其设置为 rootViewController。
- 在
didFinishLaunchingWithOptions 中不使用动画,但在从 UI 调用时动画。
这是来自 AppDelegate 的示例代码:
var loggedIn = false
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
setupRootViewController(false)
return true
}
func setupRootViewController(animated: Bool) {
if let window = self.window {
var newRootViewController: UIViewController? = nil
var transition: UIViewAnimationOptions
// create and setup appropriate rootViewController
if !loggedIn {
let loginViewController = window.rootViewController?.storyboard?.instantiateViewControllerWithIdentifier("LoginVC") as LoginViewController
newRootViewController = loginViewController
transition = .TransitionFlipFromLeft
} else {
let splitViewController = window.rootViewController?.storyboard?.instantiateViewControllerWithIdentifier("SplitVC") as UISplitViewController
let navigationController = splitViewController.viewControllers[splitViewController.viewControllers.count-1] as UINavigationController
navigationController.topViewController.navigationItem.leftBarButtonItem = splitViewController.displayModeButtonItem()
splitViewController.delegate = self
let masterNavigationController = splitViewController.viewControllers[0] as UINavigationController
let controller = masterNavigationController.topViewController as MasterViewController
newRootViewController = splitViewController
transition = .TransitionFlipFromRight
}
// update app's rootViewController
if let rootVC = newRootViewController {
if animated {
UIView.transitionWithView(window, duration: 0.5, options: transition, animations: { () -> Void in
window.rootViewController = rootVC
}, completion: nil)
} else {
window.rootViewController = rootVC
}
}
}
}
这是来自 LoginViewController 的示例代码:
@IBAction func login(sender: UIButton) {
let delegate = UIApplication.sharedApplication().delegate as AppDelegate
delegate.loggedIn = true
delegate.setupRootViewController(true)
}
我也想知道是否有更好/更清洁的方法可以在 iOS 8 中正常工作。