2

在此处输入图像描述

我有一个 SplitViewVC,它有一个 MasterNavVC,它的根是 TableViewVC。SplitViewVC 还有一个以 WhiteVC 作为根的 DetailNavVC。我想从 TableViewVC 中获得其他几个视图控制器:RedVC、GreenVC、BlueVC 和 PinkVC。我不想使用所有这些 IB segue 连接,所以我想以编程方式推送它们。TableView 的单元格有一个推动 DetailNavVC 的 segue,因此所有其他 vcs 都必须通过它。我将其用于 iPad 和 iPhone 的适应性。

问题出在 TableView 的 didSelect 方法中,当我尝试推送到任何颜色的 vcs 时,WhiteVC 总是显示向前推进并在向后退时弹出:

例如。

Push-TableView -> WhiteVC -> RedVC

Pop- RedVC -> WhiteVC -> TableView

我想

Push-TableView -> RedVC

Pop- RedVC -> TableView

我试图删除 WhiteVC,但我不断收到异常:

无法显示带有零视图控制器的嵌套 UINavigationController

因此,我添加了 WhiteVC 以消除该错误,但以下方法均无效。

  func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {

  switch indexPath.row{
        case 0:
            //this shows the WhiteVC while pushing and popping
            let redVC = storyboard?.instantiateViewController(withIdentifier: "RedVC") as! RedVC
            navigationController?.pushViewController(redVC, animated: true)
        break
        case 1:
           //this shows the WhiteVC while pushing but removes the backButton from the GreenVC
           let greenVC = storyboard?.instantiateViewController(withIdentifier: GreenVC") as! GreenVC
           navigationController?.setViewControllers([greenVC], animated: true)
        break
        case 2:
           //this has the same effect as case 1
           let blueVC = storyboard?.instantiateViewController(withIdentifier: BlueVC") as! BlueVC
           let root = detailNavController(rootViewController: blueVC)
           navigationController?.pushViewController(root, animated: true)
        break
        case 3:
            //this shows the WhiteVC pushing but doesn't show it popping
            let masterNav = splitViewController?.viewControllers.last as! MasterNavVC
            let detailNav = masterNav.viewControllers.last as! DetailNavVC
            let pinkVC = storyboard?.instantiateViewController(withIdentifier: "PinkVC") as! PinkVC
            detailNav.setViewControllers([pinkVC], animated: true)
        break

案例 3 进入壁橱,因为 WhiteVC 在推动时显示但它没有显示弹出(它正确弹出到根目录):

Push-TableView -> WhiteVC -> PinkVC

Pop- PinkVC- > TableView

我想以编程方式推送到其他颜色的 vcs(当然是在点击他们选择的单元格之后)而不显示 WhiteVC。我怎么做?

4

1 回答 1

0

首先我要说的是,Apple 建议 SpiltVC 以 root 身份开始。我遇到的问题是,如果使用 TabBarVC 作为 root,您必须将其放在 containerView 中,然后将 containerView 作为 root 到作为 SplitVC 的根的 NavVC。

SplitVC > NavVC > ContainerVC > TabBarVC //this wasn't working out

我决定使用 TabBarVC 作为 root 并为每个 Tab 添加一个单独的 SplitVC。如果您查看下图的左侧,这就是 Apple 的 MasterDetailApp 启动时的外观。场景的右侧是我使用的布局。

在此处输入图像描述

在图像的右侧,我有一个 TabBar 作为根,每个选项卡都有一个 SplitVC,每个 SplitVc 都有它自己的 NavVC,它本身有它自己的 TableVC 作为它的根:

           ____SplitVC -- NavVC -- TableVC  //this would be tab 0 and where we will focus
          /
         /
TabBarVC                                    //all the other color vcs I want to get to from the TableVC in tab 0
         \
          \____SplitVC -- NavVC -- TableVC

请注意,我没有使用的图像右侧包含一个 DetailNavigationController,就像 MasterDetailApp 中包含的一样。

我将只专注于从 TabBar 的第一个选项卡推送 vcs,因为您将对 TabBar 的第二个选项卡使用相同的方法。

要从 appDelegate 的 didFinishLaunching 开始,您只需将要首先登陆的选项卡添加为 selectedIndex:

AppDelegate:

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {

        //I subclassed the TabBarVC with the name TabBarController

        let mainStoryboard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
        let tabBarVC: TabBarController = mainStoryboard.instantiateViewController(withIdentifier: "TabBarController") as! TabBarController
        tabBarController.selectedIndex = 0
        window?.rootViewController = tabBarController

        window?.makeKeyAndVisible()
        return true
}

仅供参考,这里是选项卡 0 的 vcs 的流程和名称:

TabBarContoller > TabZeroSplitVC > TabZeroNavVC > SettingsVC

以这种方式启动将为我提供 SettingsVC,它在 iPad 上将在分屏模式下位于左侧(主侧)。您还必须符合 UISplitViewControllerDelegate 并在 viewDidLoad 中创建它,以便它显示分屏,左侧是主视图,右侧是细节。

SettingsVC: UIViewController, UISplitViewControllerDelegate{

@IBOutlet weak fileprivate var tableView: UITableView!

var colors = ["RedVC", "GreenVC", "BlueVC", "PinkVC"]

override func viewDidLoad() {
    super.viewDidLoad()

    splitViewController?.delegate = self

    //this keeps it in splitScreen mode
    splitViewController?.preferredDisplayMode = UISplitViewControllerDisplayMode.allVisible
}

func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return self.colors.count
}

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "ColorsCell", for: indexPath) as! ColorsCell

        cell.titleLabel?.text = self.colors[indexPath.row]
        return cell
 }

由于我没有在 Storyboard 中包含 DetailNavigationController,因此屏幕右侧(详细信息侧)将为空白。因此,在启动时,您会看到如下所示的屏幕:

在此处输入图像描述

这个想法是首先以编程方式添加 NavVC,然后以编程方式添加 WhiteVC 作为它的根。这样,WhiteVC 最初将显示在屏幕的右侧。关键是使用 splitVCshowDetailViewController(vc: UIViewController, sender: Any?)以编程方式显示它。顺便说一句,将 nav 添加为类变量很重要,因为我们将使用它来显示其他颜色 vcs。

SettingsVC: UIViewController, UISplitViewControllerDelegate{

@IBOutlet weak fileprivate var tableView: UITableView!

var colors = ["RedVC", "GreenVC", "BlueVC", "PinkVC"]

var whiteVC: WhiteController? //the one from the storyboard

var nav: UINavigationController? //this will represent the DetailNavigationController from Apple's MasterDetailApp

override func viewDidLoad() {
    super.viewDidLoad()

    splitViewController?.delegate = self
    splitViewController?.preferredDisplayMode = UISplitViewControllerDisplayMode.allVisible

    //1st instantiate the WhiteVC that was created in storyboard
    self.whiteVC = storyboard?.instantiateViewController(withIdentifier: "WhiteController") as? WhiteController

    //2nd add it to the programmatic navigationController as it's root
    self.nav = UINavigationController(rootViewController: whiteVC!)

    //3rd use the splitVC method to show the nav on the right side of scene 
    splitViewController?.showDetailViewController(self.nav!, sender: self
}

现在启动时,场景将如下所示:

在此处输入图像描述

现在回答如何在不包括 WhiteVC 的情况下推动任何颜色 vcs 的问题。您所要做的就是将任何颜色的 vc 作为根添加到作为类变量创建的编程导航中。在 tableView 里面didSelectRow是你添加它并显示的地方

func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {

switch indexPath.row{

        case 0:
            let redVC = storyboard?.instantiateViewController(withIdentifier: "RedController") as! RedController
            self.nav = UINavigationController(rootViewController: redVC)
            splitViewController?.showDetailViewController(self.nav! , sender: self)
            break

        case 1:
            let greenVC = storyboard?.instantiateViewController(withIdentifier: "GreenController") as! GreenController
            self.nav = UINavigationController(rootViewController: greenVC)
            splitViewController?.showDetailViewController(self.nav! , sender: self)
            break

        case 2:
            let blueVC = storyboard?.instantiateViewController(withIdentifier: "BlueController") as! BlueController
            self.nav = UINavigationController(rootViewController: blueVC)
            splitViewController?.showDetailViewController(self.nav! , sender: self)
            break

        case 3:
            let pinkVC = storyboard?.instantiateViewController(withIdentifier: "PinkController") as! PinkController
            self.nav = UINavigationController(rootViewController: PinkVC)
            splitViewController?.showDetailViewController(self.nav! , sender: self)
            break

}

现在,如果您选择了标记为 RedVC 的单元格,您会得到这个(RedVC 顶部应该有一个导航栏,但我忘了在 Photoshop 中添加它):

在此处输入图像描述

如果您查看 didSelectRow 内部,您将看到导航现在有一个新根,即 redVC(它最初在 viewDidLoad 中使用 WhiteVC)。由于您更改了根,WhiteVC 不再在层次结构中。对于任何其他颜色,同样的事情也会发生。如果你选择 PinkVC,你会得到(PinkVC 顶部应该有一个导航栏,但我忘了在 Photoshop 中添加它):

在此处输入图像描述

在任何情况下,您所要做的就是为导航设置一个新的根。如果您想添加将视图向外扩展的双展开箭头

在此处输入图像描述

您还可以将其添加到 didSelectRow

       case 0:
            let redVC = storyboard?.instantiateViewController(withIdentifier: "RedController") as! RedController
            self.nav = UINavigationController(rootViewController: redVC)

            //these 2 lines of code are what adds the double expand arrow
            self.nav?.topViewController?.navigationItem.leftBarButtonItem = splitViewController?.displayModeButtonItem
            self.nav?.topViewController?.navigationItem.leftItemsSupplementBackButton = true

            splitViewController?.showDetailViewController(self.nav! , sender: self)
            break

            //add the same exact 2 lines for every other case

最后一件事。这是我遇到的一个大问题,我相信其他人可能会遇到这个问题,因为这个 SplitVC 不是 root。假设在启动时的选项卡零上,您想显示另一个 vc(即 OrangeVC)而不是 SettingsVC。问题是设置是:

TabBarContoller > TabZeroSplitVC > TabZeroNavVC > SettingsVC

由于故事板将 SettingsVC 作为 TabZeroNavVC 的根目录,因此您必须在 appDelegate 的 didFinishLaunching(或您的登录屏幕等)中更改它。

使用它的代码是:

AppDelegate:

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {

        //I subclassed the TabBarVC with the name TabBarController

        let mainStoryboard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
        let tabBarVC: TabBarController = mainStoryboard.instantiateViewController(withIdentifier: "TabBarController") as! TabBarController
        tabBarController.selectedIndex = 0

        //first you have to get to the splitVC on the first tab
        let tabZeroSplitVC = tabBarController.viewControllers![0] as! TabZeroSplitController

        //second you have to get to the navVC that's connected to the splitVC
        let tabZeroNavVC = tabZeroSplitVC.childViewControllers[0] as! TabZeroNavController

        //third instantiate the vc that you want to appear upon launch
        let orangeVC = mainStoryboard.instantiateViewController(withIdentifier: "OrangeController") as! OrangeController

        //the navVC has a method to set a new array of vcs. Just add the orangeVC in here (make sure to put it in array brackets)
        tabZeroNavVC.setViewControllers( [orangeVC], animated: true)

        window?.rootViewController = tabBarController

        window?.makeKeyAndVisible()
        return true
}

一旦你启动你的 OrangeVC 就会显示出来。由于 OrangeVC 不是 tableView,您可能希望显示全屏。请务必添加 UISplitViewControllerDelegate 并在 viewDidLoad 添加:

OrangeVC: UIViewController, UISplitViewControllerDelegate{ 

override func viewDidLoad() {
        super.viewDidLoad()

        splitViewController?.delegate = self

        //this will hide splitScreen and will only show fullScreen
        splitViewController?.preferredDisplayMode = UISplitViewControllerDisplayMode.primaryHidden
}

启动时您将拥有一个橙色的全屏,而不是拆分屏幕。

尽管这些链接使用 SplitVC 作为 root,但这些是一些关于配置 SplitVC 的非常好的博客文章:

拆分VC-1

拆分VC-2

拆分VC-3

拆分VC-4

拆分VC-5

拆分VC-7

于 2017-07-31T20:41:20.247 回答