I find that my Storyboard has become very complex and decided to split it into different Storyboards. But I want to have the freedom to instantiate a UIViewController no matter where I put the view controller in. That way, I can move around View Controller from Storyboard to Storyboard without the need to remember where did I put that View Controller, and I also don't have to update the code at all because they all use the same code to instantiate the same View Controller with that name, no matter where it resides.
Therefore, I want to create an extension of UIViewController like this:
extension UIViewController {
func instantiate (named: String?, fromStoryboard: String? = nil) -> UIViewController? {
guard let named = named else { return nil; }
if let sbName = fromStoryboard {
let sb = UIStoryboard(name: sbName, bundle: nil);
return sb.instantiateViewController(withIdentifier: named);
}
else {
for sb in UIStoryboard.storyboards {
if let vc = sb.instantiateViewController(withIdentifier: named) {
return vc;
}
}
}
return nil;
}
The problem is, I cannot find the property / method to return the list of storyboard instances like .storyboards
anywhere. Is there any workaround on this? I know that I can probably have a static list of storyboard names, but that way, the extension won't be dynamic and independent of the projects.
Can anybody help? Thanks.
EDIT:
Combining the accepted answer and answer from here to safely instantiate viewcontroller (and return nil if not found), this is my code:
UIStoryboard+Storyboards.swift:
extension UIStoryboard {
static var storyboards : [UIStoryboard] {
let directory = Bundle.main.resourcePath! + "/Base.lproj"
let allResources = try! FileManager.default.contentsOfDirectory(atPath: directory)
let storyboardFileNames = allResources.filter({ $0.hasSuffix(".storyboardc" )})
let storyboardNames = storyboardFileNames.map({ ($0 as NSString).deletingPathExtension as String })
let storyboardArray = storyboardNames.map({ UIStoryboard(name: $0, bundle: Bundle.main )})
return storyboardArray;
}
func instantiateViewControllerSafe(withIdentifier identifier: String) -> UIViewController? {
if let availableIdentifiers = self.value(forKey: "identifierToNibNameMap") as? [String: Any] {
if availableIdentifiers[identifier] != nil {
return self.instantiateViewController(withIdentifier: identifier)
}
}
return nil
}
}
UIViewController+Instantiate.swift:
extension UIViewController {
static func instantiate (named: String?, fromStoryboard: String? = nil) -> UIViewController? {
guard let named = named else { return nil; }
if let sbName = fromStoryboard {
let sb = UIStoryboard(name: sbName, bundle: nil);
return sb.instantiateViewControllerSafe(withIdentifier: named);
}
else {
for sb in UIStoryboard.storyboards {
if let vc = sb.instantiateViewControllerSafe(withIdentifier: named) {
return vc;
}
}
}
return nil;
}
}
With the restriction that all the storyboard files must be located on Base.lproj
folder.
It's not the most efficient code in terms of running time, I know. But for now, it's easy enough to be understood and I can live with this. :) Thanks for everybody who helps!