如何在Swift中获取UIStoryboard数组?

问题描述:

我发现我的故事板变得非常复杂,并决定将它分成不同的故事板。但是我希望能够*地实例化一个UIViewController,无论我把视图控制器放在哪里。这样,我就可以将View Controller从Storyboard移动到Storyboard,而无需记住我把View Controller放在哪里,而且我也不必更新代码,因为它们全都使用相同的代码来实例化具有该名称的相同View Controller,而不管它驻留在何处。如何在Swift中获取UIStoryboard数组?

因此,我想是这样创造的UIViewController的扩展:

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; 
} 

的问题是,我无法找到属性/方法返回故事板的实例列表像.storyboards任何地方。有没有解决这个问题的方法?我知道我可能有一个静态的故事板名称列表,但这样,扩展名将不会是动态的,并且与项目无关。

任何人都可以帮忙吗?谢谢。

编辑:

组合来自hereaccepted answer和答案安全实例化视图 - 控制(并返回零,如果没有找到),这是我的代码:

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; 
    } 
} 

限制所有故事板文件必须位于Base.lproj文件夹中。我知道,这不是运行时间最有效的代码。但现在,这很容易理解,我可以忍受这一点。 :)感谢大家谁帮助!

+0

您可以在此行self.navigationController中获取storyboards viewcontroller的导航堆栈数组 –

+0

。viewControllers你可以得到你的viewcontrollers的导航数组 –

+0

@JitendraModi你的意思是'self.navigationController.viewControllers'?我的意思是我想从UIStoryboard实例化新的viewController,而不是访问导航堆栈数组上已经存在的viewControllers。 –

故事板通常设置为本地化,因此您应该在您的软件包资源目录的Base.lproj子目录中查找它们。故事板被编译成扩展名为.storyboardc的文件,因此您应该查找具有该后缀的文件并将其剥离。

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 }) 
Swift.print(storyboardNames) 

如果已创建特定于设备的故事板(通过添加或~ipad~iphone到故事板的文件名),那么你还需要脱掉这些后缀和消除重复。

请注意,已编译的故事板后缀尤其不是SDK的文档部分,因此它可能会在iOS/Xcode的未来版本中更改。

+0

哦,哇,这太棒了!让我试着将其纳入代码中,并会再次回复您。谢谢! –

+0

“请注意,编译后的故事板后缀尤其不是SDK的文档部分,因此它可能会在iOS/Xcode的未来版本中发生变化。” - >没关系,我可以添加一些解决方法和预防措施,现在我知道'NSBundle'是如何工作的。 –

+1

即使它发生变化,它也不会影响到您,直到您使用较新的iOS部署目标重建您的应用程序。 –

不要这样做。

取而代之的是,根据需要使用故事板参考材料以适应不同的故事板。

+0

这也可以做到。但我有点不喜欢赛格。如果观看控制器已经很丰富(因此为什么我想将它们分离到不同的故事板),Segue会混乱故事板。但它仍然会混乱。我们只是说我想尝试不同的编程方法,以找出最适合我的东西。 :) –

在你的情况下,如果你把故事板放在一起,也许它会更干净。并以编程方式创建所有视图控制器。

+0

这对我来说是个好主意。不适用于继续我的工作的开发人员。 ( –

+0

)教他们如何做到这一点,它并不复杂,一个好的iOS开发者应该能够同时做到这两点。(\ w storyboard and programmatically) – Mikasa

+0

这并不复杂,但不是太高产,特别是在视觉设计可以快速操纵设计的人是首先创建设计的人,其他人仍然需要深入代码并搜索控制该标签的y位置的那一行代码里面的那个单元格里面那个tableview里面的那个容器里面那个页面控件里面的控件... –