У меня есть то, что кажется очень распространенной настройкой в моем универсальном приложении, с корнем UISplitViewController, использующим UITabBarController в качестве masterViewController, и затем я хочу:

  • либо поместите контроллер детального просмотра в стек, если я нахожусь на вертикальном iPhone
  • показать подробный контроллер в detailViewController UISplitViewController на альбомном iPhone 6+ и других больших экранах, таких как iPad и т. д.

Для этого у меня точно такая же настройка, как и во всех тех обсуждениях, в которых упоминается похожая проблема:

Но ни одно из решений, упомянутых в этих вопросах, не работает. Некоторые из них создают бесконечный рекурсивный цикл и EXC_BAD_ACCESS. И последний, который я попробовал, просто продолжает представлять контроллер детального представления модально, а не помещать его в стек на iPhone. Что я сделал, так это создал собственный подкласс UISplitViewController:

    class RootSplitViewController: UISplitViewController {
        override func viewDidLoad() {
            super.viewDidLoad()
            self.delegate = self
        }
    }

    extension RootSplitViewController: UISplitViewControllerDelegate {
        func splitViewController(_ splitViewController: UISplitViewController, showDetail vc: UIViewController, sender: Any?) -> Bool {
            if let tabController = splitViewController.viewControllers[0] as? UITabBarController {
                if(splitViewController.traitCollection.horizontalSizeClass == .compact) {
                    tabController.selectedViewController?.show(vc, sender: sender)
                } else {
                    splitViewController.viewControllers = [tabController, vc]
                }
            }

            return true
        }

        func splitViewController(_ splitViewController: UISplitViewController, separateSecondaryFrom primaryViewController: UIViewController) -> UIViewController? {
            if let tabController = splitViewController.viewControllers[0] as? UITabBarController {
                if let navController = tabController.selectedViewController as? UINavigationController {
                    return navController.popViewController(animated: false)
                } else {
                    return nil
                }
            } else {
                return nil
            }
        }
    }

И вот код в главном контроллере представления, чтобы показать детализированный контроллер представления:

self.performSegue(withIdentifier: "showReference", sender: ["tags": tags, "reference": reference])

Где tags и reference где загружены из Firebase. И, конечно, sege showReference имеет вид «Show Detail (например, Replace)».

Первый метод делегата вызывается правильно, о чем свидетельствует точка останова, которая попадает туда, когда я щелкаю элемент в списке внутри UITabBarController. И все же контроллер подробного представления все еще представлен модально на iPhone. Впрочем, на iPad проблем нет: контроллер детального просмотра отображается справа, как и ожидалось.

Большинство упомянутых выше ответов довольно старые, и некоторые решения реализованы в Objective-C, поэтому, возможно, я что-то не так сделал в преобразовании, или что-то изменилось в реализации UISplitViewController с тех пор.

Есть ли у кого-нибудь предложения?

3
Sebastien 19 Апр 2019 в 10:25

2 ответа

Лучший ответ

Я понял. Фактически, это было связано с целевым контроллером представления, который я пытался показать. Из 2 методов, которые я переопределил в UISplitViewControllerDelegate, был вызван только первый:

func splitViewController(_ splitViewController: UISplitViewController, showDetail vc: UIViewController, sender: Any?) -> Bool {
    if let tabController = splitViewController.viewControllers[0] as? UITabBarController {
        if(splitViewController.traitCollection.horizontalSizeClass == .compact) {
            tabController.selectedViewController?.show(vc, sender: sender)
        } else {
            splitViewController.viewControllers = [tabController, vc]
        }
    }

    return true
}

Но контроллер представления, который я показывал в первой ветви теста, уже был встроен в UINavigationController, поэтому я, по сути, показывал UINavigationController в другой, и в этом случае модальность имела больше смысла. Поэтому в этом случае мне нужно было показать контроллер вида сверху UINavigationController, который, как я полагаю, был целью второго метода, который я переопределяю в делегате, но он никогда не вызывался. Так что я сделал это прямо со следующей реализацией:

extension RootSplitViewController: UISplitViewControllerDelegate {
    func splitViewController(_ splitViewController: UISplitViewController, showDetail vc: UIViewController, sender: Any?) -> Bool {
        if let tabController = splitViewController.viewControllers[0] as? UITabBarController {
            if(splitViewController.traitCollection.horizontalSizeClass == .compact) {
                if let navController = vc as? UINavigationController, let actualVc = navController.topViewController {
                    tabController.selectedViewController?.show(actualVc, sender: sender)
                    navController.popViewController(animated: false)
                } else {
                    tabController.selectedViewController?.show(vc, sender: sender)
                }
            } else {
                splitViewController.viewControllers = [tabController, vc]
            }
        }

        return true
    }
}

И это, кажется, работает отлично, как на iPhone, так и на iPad.

3
Sebastien 23 Апр 2019 в 10:06

После того, как вы попробовали метод ShowDetailViewController, чтобы изменить контроллер подробного представления в контроллере разделенного представления.

splitViewController.showDetailViewController(vc, sender: self)

Если в случае, если ваш контроллер представления не содержит контроллер навигации, вы также можете встроить его в контроллер навигации.

 let nav = UINavigationController.init(rootViewController: vc)
 splitViewController.showDetailViewController(nav, sender: self)
0
pooja 23 Апр 2019 в 03:00