我真的不相信在 UISplitViewController (例如登录表单)之前显示一些 UIViewController 的概念变得如此复杂,直到我不得不创建那种视图层次结构。
我的示例基于 iOS 8 和 XCode 6.0 (Swift),所以我不确定这个问题之前是否以相同的方式存在,或者它是由于 iOS 8 引入的一些新错误,但从所有类似的问题我发现,我没有看到这个问题的完整“不是很老套”的解决方案。
在我最终找到解决方案之前,我将指导您完成一些我尝试过的事情(在这篇文章的末尾)。每个示例都基于在未启用 CoreData 的情况下从 Master-Detail 模板创建新项目。
第一次尝试(模态到 UISplitViewController):
- 创建新的 UIViewController 子类(例如 LoginViewController)
- 在情节提要中添加新的视图控制器,将其设置为初始视图控制器(而不是 UISplitViewController)并将其连接到 LoginViewController
- 将 UIButton 添加到 LoginViewController 并从该按钮创建模态 segue 到 UISplitViewController
- 将 UISplitViewController 的样板设置代码从 AppDelegate 移动
didFinishLaunchingWithOptions
到 LoginViewControllerprepareForSegue
这几乎奏效了。我说差不多了,因为在使用 LoginViewController 启动应用程序并点击按钮并转到 UISplitViewController 后,会出现一个奇怪的错误:在方向更改时显示和隐藏主视图控制器不再动画。
经过一段时间努力解决这个问题并且没有真正的解决方案,我认为它与 UISplitViewController 必须是 rootViewController 的奇怪规则有某种联系(在这种情况下它不是,LoginViewController 是)所以我放弃了这个不太完美的解决方案.
第二次尝试(来自 UISplitViewController 的模态 segue):
- 创建新的 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 添加某种类型的 loggedIn 属性。
- 基于此属性,实例化适当的视图控制器,然后将其设置为 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 中正常工作。