Reimond Hill
Reimond Hill

Reputation: 4760

Destination ViewController from UIStoryboardSegueTemplate

I am trying to programatically get all the segues attached to UIViewController using UIStoryboardSegueTemplate and my aim is to get the destination view controller to the segue associated.

So far I have done:

func initViewControllers(){

    guard let array = self.value(forKey: "storyboardSegueTemplates") as? [AnyObject] else {
        //No segues
        return
    }

    for item in array{
        print("\(logClassName): initViewControllers -> Self = \(String(describing: item.self))")
        print("\(logClassName): initViewControllers -> Segue Identifier = \(String(describing: item.value(forKey: "identifier")))")
        print("\(logClassName): initViewControllers -> Class = \(String(describing: item.value(forKey: "segueClassName")))")
        print("\(logClassName): initViewControllers -> Destination = \(String(describing: item.value(forKey: "destinationViewControllerIdentifier")))")


        let segueClassName:String = (item.value(forKey: "segueClassName") as? String) ?? ""
        if segueClassName.contains("CustomTabSegue"){
            print("\(logClassName): appending tabItem VC")
            let segueContainer:String = (item.value(forKey: "identifier") as? String) ?? ""
            performSegue(withIdentifier: segueContainer, sender: self)
        }

   }
}

and getting the Destination using override func prepare(for segue: UIStoryboardSegue, sender: Any?)

The way I select the segues I want to catch is by a custom class called CustomTabSegue

The question is: Is there a way to get the destination View controller without performing the segue?

According to this link it seems unlikely but maybe there is something I am missing.

Upvotes: 0

Views: 102

Answers (2)

Reimond Hill
Reimond Hill

Reputation: 4760

Following the answer of Agent Smith, I have been able to do:

func initViewControllers(){

    guard let array = self.value(forKey: "storyboardSegueTemplates") as? [AnyObject] else {
        return
    }

    for item in array{

        if let segueClassName = (item.value(forKey: "segueClassName") as? String){

            if segueClassName.contains("CustomTabSegue"){
                print("\(logClassName): CustomTabSegue")

                if let destinationViewController = (item.value(forKey: "destinationViewControllerIdentifier") as? String)?.components(separatedBy: "-").first{
                    print("\(logClassName): DestinationVC = \(destinationViewController)")

                    if let addedVC = destinationViewController.getViewController(){
                        print("\(logClassName): Adding View Controller \(addedVC.logClassName)")
                        viewControllers.append(addedVC)
                    }

                }
            }
        }
}

And I have created the following extension

import Foundation
import UIKit

extension String{

func getViewController() -> UIViewController? {

    if var appName = Bundle.main.infoDictionary?["CFBundleName"] as? String {
        print("CFBundleName - \(appName)")
        appName = appName.replacingOccurrences(of: " ", with: "_", options: .literal)

        if let viewControllerType = NSClassFromString("\(appName).\(self)") as? UIViewController.Type {
            print("String: as UIViewController")
            return viewControllerType.init()
        }

    }

    return nil
}

It works as expected but the only problem a now is that if the ViewController in embedded in a UINavigationController it won't detect it

Upvotes: 0

Agent Smith
Agent Smith

Reputation: 2913

If you created those segues yourself, What I did was, I gave the identifier to the segue as :-

ViewControllerName1 + "To" + ViewControllerName2

After getting the list of segues from storyboardSegueTemplates I separated them with To and got the name of both source and destination ViewControllers. Then, Converting them to Class type by

NSClassFromString(ClassName: String)

This way you can get the type of classes which are associated with segues but it's impossible to get the instances of destinationViewControllers because they are not initialized until you do :

 performSegue(withIdentifier: String, sender: Any?)

Hope it helps!!

Upvotes: 1

Related Questions