Я серьезно не верил, что эта концепция наличия некоторого UIViewController для отображения перед UISplitViewController (форма входа в систему) оказывается настолько сложной, пока мне не пришлось создавать такого родаview hiearchy.
Мой пример основан на iOS 8 и XCode 6.0 (Swift), поэтому я не уверен, существовала ли ранее подобная проблема подобным образом, или это из-за какой-тоновые ошибки, появившиеся в iOS 8, но из всех похожих вопросов, которые я нашел, я не увидел полного «не очень хакерского» решения этой проблемы.
Я покажу вамчерез некоторые вещи, которые я пробовал, прежде чем я получил решение (в конце этого поста).Каждый пример основан на создании нового проекта из шаблона Master-Detail без включенного CoreData.
Первая попытка (модальный переход к UISplitViewController):
- создать новый UIViewControllerподкласс (например, LoginViewController)
- добавить новый контроллер представления в раскадровку, установить его в качестве начального контроллера представления (вместо UISplitViewController) и подключить его к LoginViewController
- добавить UIButton к LoginViewController и создать модальный переход отэта кнопка для UISplitViewController
- переместить код установки шаблонов для UISplitViewController из
didFinishLaunchingWithOptions
AppDelegate в LoginViewController prepareForSegue
Это почти сработало.Я говорю почти, потому что после того, как приложение запущено с LoginViewController и вы нажимаете кнопку и переходите к UISplitViewController, происходит странная ошибка: отображение и скрытие главного контроллера вида при изменении ориентации больше не анимируется.
Через некоторое время, пытаясь справиться с этой проблемой и без реального решения, я подумал, что это как-то связано с этим странным правилом , что UISplitViewController должен быть rootViewController (а в данном случае это не так, LoginViewController) поэтому я отказался от этого не очень идеального решения.
Вторая попытка (модальный переход от UISplitViewController):
- создать новый подкласс UIViewController (например, LoginViewController)
- добавить новый контроллер представления в раскадровку и подключить его к LoginViewController (но на этот раз оставить UISplitViewController в качестве начального контроллера представления)
- создать модальный переход из UISplitViewController в LoginViewController
- добавить UIButton to LoginViewController и создайте функцию раскрутки с помощью этой кнопки
Наконец, добавьте этот код в didFinishLaunchingWithOptions
AppDelegate после стандартного кода для настройки 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 и добавление некоторого свойства loggedIn в AppDelegate.
- На основе этого свойства создайте экземпляр соответствующего контроллера представления и после этого установите его как rootViewController.
- Сделайте это без анимации в
didFinishLaunchingWithOptions
, но анимируйте при вызове из пользовательского интерфейса.
Вот пример кода от 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.