在UISplitViewController和其他视图控制器之间切换的最佳方法是什么?

occ*_*lus 28 iphone user-interface appstore-approval ipad uisplitviewcontroller

我正在创作一个iPad应用程序.应用程序中的一个屏幕非常适合使用UISplitViewController.但是,应用程序的顶级是一个主菜单,我不想使用UISplitViewController.这提出了一个问题,因为Apple说:

  1. UISplitViewController 应该是应用程序中的顶级视图控制器,即它的视图应该作为子视图添加 UIWindow

  2. 如果使用,UISplitViewController应该在应用程序的生命周期中存在 - 即不要从UIWindow中移除其视图并将其他视图放在适当位置,反之亦然

阅读和实验后,似乎只有满足Apple要求的可行选择,而我们自己的选择是使用模态对话框.所以我们的应用程序在根级别有一个UISplitViewController(即它的视图被添加为UIWindow的子视图),为了显示我们的主菜单,我们将它作为全屏模式对话框推送到UISplitViewController上.然后通过关闭主菜单视图控制器模式对话框,我们可以实际显示我们的拆分视图.

这个策略似乎运作正常.但它引出了一些问题:

1)有没有更好的方法来构建这个,没有模态,也满足提到的所有要求?由于被推送为模态对话框,主UI出现似乎有点奇怪.(模态应该用于重点用户任务.)

2)由于我的做法,我是否有遭受应用商店拒绝的风险?根据Apple的人机界面指南,这种模态策略可能是"误用"模态对话框.但是他们给了我什么其他选择呢?无论如何,他们会知道我这样做吗?

tad*_*ija 19

我真的不相信在UISplitViewController (例如登录表单)之前显示一些UIViewController的概念变得如此复杂,直到我不得不创建那种视图层次结构.

我的例子是基于iOS 8和XCode 6.0(Swift),所以我不确定此问题是否以同样的方式存在,或者是由于iOS 8引入了一些新的错误,但是从所有类似的问题我发现,我没有看到完全'不是非常hacky'这个问题的解决方案.

在我最终得到一个解决方案之前,我将引导您完成我尝试过的一些事情(在本文末尾).每个示例都基于在未启用CoreData的情况下从Master-Detail模板创建新项目.


首先尝试(模态segue到UISplitViewController):

  1. 创建新的UIViewController子类(例如LoginViewController)
  2. 在storyboard中添加新的视图控制器,将其设置为初始视图控制器(而不是UISplitViewController)并将其连接到LoginViewController
  3. 将UIButton添加到LoginViewController并从该按钮创建模态segue到UISplitViewController
  4. 将UISplitViewController的样板设置代码从AppDelegate移动didFinishLaunchingWithOptions到LoginViewControllerprepareForSegue

这几乎奏效了.我差点说,因为在使用LoginViewController启动应用程序并点击按钮并切换到UISplitViewController之后,会出现一个奇怪的错误:显示和隐藏主视图控制器的方向更改不再是动画.

经过一段时间努力解决这个问题,没有真正的解决方案,我认为它与UISplitViewController必须是rootViewController(在这种情况下它不是,LoginViewController)这个奇怪的规则有某种联系,所以我放弃了这个不那么完美的解决方案.


第二次尝试(来自UISplitViewController的模态segue):

  1. 创建新的UIViewController子类(例如LoginViewController)
  2. 在storyboard中添加新的视图控制器,并将其连接到LoginViewController(但这次将UISplitViewController保留为初始视图控制器)
  3. 从UISplitViewController到LoginViewController创建模态segue
  4. 将UIButton添加到LoginViewController并从该按钮创建展开segue

最后,didFinishLaunchingWithOptions在设置UISplitViewController的样板代码之后,将此代码添加到AppDelegate :

window?.makeKeyAndVisible()
splitViewController.performSegueWithIdentifier("segueToLogin", sender: self)
return true
Run Code Online (Sandbox Code Playgroud)

或尝试使用此代码:

window?.makeKeyAndVisible()
let loginViewController = splitViewController.storyboard?.instantiateViewControllerWithIdentifier("LoginVC") as LoginViewController
splitViewController.presentViewController(loginViewController, animated: false, completion: nil)
return true
Run Code Online (Sandbox Code Playgroud)

这两个例子都产生了同样的坏事:

  1. 控制台输出: Unbalanced calls to begin/end appearance transitions for <UISplitViewController: 0x7fc8e872fc00>
  2. 在LoginViewController被模态化之前必须首先显示UISplitViewController(我宁愿只提供登录表单,因此用户在登录前看不到UISplitViewController)
  3. 放松segue不会被调用(这完全是其他错误,我现在不会进入那个故事)

解决方案(更新rootViewController)

我发现哪种方法正常工作的唯一方法是你动态改变窗口的rootViewController:

  1. 为LoginViewController和UISplitViewController定义Storyboard ID,并向AppDelegate添加某种loggedIn属性.
  2. 基于此属性,实例化相应的视图控制器,然后将其设置为rootViewController.
  3. 在没有动画的情况下执行此操作,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
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

这是来自LoginViewController的示例代码:

@IBAction func login(sender: UIButton) {
    let delegate = UIApplication.sharedApplication().delegate as AppDelegate
    delegate.loggedIn = true
    delegate.setupRootViewController(true)
}
Run Code Online (Sandbox Code Playgroud)

我还想听听是否有更好/更清洁的方式在iOS 8中正常工作.


Bou*_*rne 6

德勤!进入相同的问题并使用模态以相同的方式解决它.在我的例子中,它是一个登录视图,然后主菜单也显示在splitview之前.我使用了你想到的相同策略.我(以及我采访过的其他几位知识渊博的iOS人员)找不到更好的出路.对我来说很好.用户无论如何都不会注意到模态.这样介绍他们.是的,我也可以告诉你,有很多应用程序在App商店的引擎技巧下做同样的事情.:)另一方面,请告诉我,如果你想某个时候某种方式更好的出路:)