如何在 Swift 中以编程方式更改拆分视图控制器中的详细视图

How to change detail View in splitViewController programatically in Swift

我正在制作一个应用程序,其中我在 NSUserDefaults 中有一个 key/value,它允许应用程序在启动时检测这是否是该应用程序第一次在设备上打开。如果是第一次,我希望 splitVC(也是 rootVC)的详细视图成为我的 pageViewController(教程),否则我希望它直接进入应用程序(另一个视图控制器,我们称之为 todayViewController)。

我目前有一个 class 用于我的 SplitVC (GlobalSplitViewController.swift),但我目前不知道如何以编程方式更改 ViewDidLoad 中的详细视图。

此外,在故事板中,我的 splitVC 的细节转场连接到 todayViewController,它的主转场连接到 menuVC,它运行良好。

提前致谢!

GlobalSplitViewController.swift中的代码:

import UIKit

class GlobalSplitViewController: UISplitViewController, UISplitViewControllerDelegate {

var firstTime: Bool!

override func viewDidLoad() {
    super.viewDidLoad()

    self.delegate = self
    firstTime = loadFistTime()

    if firstTime == true {
    //load tutorials pageVC
    } else {
   //load todayVC
    }

}

func splitViewController(svc: UISplitViewController, shouldHideViewController vc: UIViewController, inOrientation orientation: UIInterfaceOrientation) -> Bool {
    return true
}

例如,在 AppDelegate 中,您可以检查您的 UserDefaults,使用 Switch 或 If/else,您可以更改 splitView。 这是更改 detailViewController 的示例。

let detailViewController = self.storyboard?.instantiateViewControllerWithIdentifier("DetailNavigationViewController") as! UINavigationController
self.splitViewController?.viewControllers[1] = detailViewController

从 iOS 8 开始,您可以使用:

splitViewController?.showDetailViewController(vc, sender: self)

或者如果你想更换主控制器

splitViewController?.show(vc, sender: self)

呼叫

splitViewController.showDetailViewController(vc, sender: self)

似乎是一个很好的解决方案。但是当在 iPhone 上调用时,当 detailViewController 已经显示在 masterViewController 之上时,此调用将显示没有 navigationViewController 和后退按钮的 detailViewController。所以应用卡住了。

弹出旧的 detailViewController 并显示新的 detailViewController 也显示了上述相同的问题。

有效的是弹出旧的,等工作完成再显示新的。

以下代码假定在 masterViewController 中实现:

if let top = navigationController?.topViewController,
   top !== self {
    if let navController = splitViewController.viewControllers[0] as? UINavigationController {
        navController.popViewController(animated: false)
        DispatchQueue.main.async {
            splitViewController.showDetailViewController(vc, sender: self)
        }
        return
    }
}
splitViewController.showDetailViewController(vc, sender: self)

关键部分是DispatchQueue.main.async。这确保在显示新细节之前完成弹出旧细节。

我还有一个提议:

此代码在 iPhone 上运行良好。问题出在 iPad(仅 iPad)

详细视图上的导航栏仍然消失(显然还有后退按钮),但仅在 iPad。

在 iPhone 上完美运行。

import UIKit

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate, UISplitViewControllerDelegate {

var window: UIWindow?

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

window = UIWindow(frame: UIScreen.main.bounds)

let firstVC = FIRST()
let secondVC = SECOND()

let firstNC = UINavigationController(rootViewController: firstVC)
let secondNC = UINavigationController(rootViewController: secondVC)

let splitViewController =  UISplitViewController()
splitViewController.viewControllers = [firstNC, secondNC]
splitViewController.preferredPrimaryColumnWidthFraction = 1/3
splitViewController.delegate = self

// IMPORTANT to show back button
let navigationController = splitViewController.viewControllers[splitViewController.viewControllers.count-1] as! UINavigationController

navigationController.topViewController!.navigationItem.leftBarButtonItem = splitViewController.displayModeButtonItem

window?.rootViewController = splitViewController
window?.makeKeyAndVisible()

return true
}

// MARK: - Split view
func splitViewController(_ splitViewController: UISplitViewController, collapseSecondary secondaryViewController:UIViewController, onto primaryViewController:UIViewController) -> Bool {

guard let secondaryAsNavController = secondaryViewController as? UINavigationController else { return false }

guard let topAsDetailController = secondaryAsNavController.topViewController as? SECOND else { return false }

if topAsDetailController.detailItem == nil {
// Return true to indicate that we have handled the collapse by doing nothing; the secondary controller will be discarded.
return true
}
return false
}

问题是:

在 iPad 的情况下失败了什么? 我再说一遍,iPhone 上的这段代码工作得很好。

(场景是:没有故事板

如果您使用 side bar 样式实现并且 iOS 14+:

splitViewController.setViewController(viewController, for: .secondary)