Ios 如何在Swift中获取UIStoryboard数组?

Ios 如何在Swift中获取UIStoryboard数组?,ios,swift,xcode,uistoryboard,Ios,Swift,Xcode,Uistoryboard,我发现我的故事板变得非常复杂,决定把它分成不同的故事板。但我希望无论将视图控制器放在何处,都可以自由地实例化UIViewController。这样,我就可以在一个故事板到另一个故事板之间移动视图控制器,而不需要记住我把视图控制器放在哪里了,而且我也不需要更新代码,因为它们都使用相同的代码来实例化具有该名称的同一视图控制器,无论它位于何处 因此,我想创建UIViewController的扩展,如下所示: extension UIViewController { func instanti

我发现我的故事板变得非常复杂,决定把它分成不同的故事板。但我希望无论将视图控制器放在何处,都可以自由地实例化UIViewController。这样,我就可以在一个故事板到另一个故事板之间移动视图控制器,而不需要记住我把视图控制器放在哪里了,而且我也不需要更新代码,因为它们都使用相同的代码来实例化具有该名称的同一视图控制器,无论它位于何处

因此,我想创建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;
}
问题是,我在任何地方都找不到返回脚本实例列表(如
.storyboard
)的属性/方法。这方面有什么解决办法吗?我知道我可能有一个静态的故事板名称列表,但是那样的话,扩展就不会是动态的并且独立于项目

有人能帮忙吗?谢谢

编辑:

将和的答案结合起来以安全地实例化viewcontroller(如果未找到,则返回nil),这是我的代码:

UIStoryboard+故事板.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
文件夹中


我知道,就运行时间而言,它不是最有效的代码。但就目前而言,这很容易被理解,我可以接受谢谢大家的帮助

故事板通常设置为本地化,因此您应该在捆绑包资源目录的
Base.lproj
子目录中查找它们。故事板被编译成一个扩展名为
.storyboard c
的文件,因此您应该查找带有该后缀的文件并将其删除

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中更改。

不要这样做


相反,根据需要使用故事板引用来切换到不同的故事板。

在您的情况下,如果您一起丢弃故事板,可能会更干净。并以编程方式创建所有视图控制器

如其他地方所述,在编译和运行应用程序时,故事板被编译为不透明(即二进制、未记录的)
。故事板C
文件。
UIStoryboard
API只允许实例化初始视图控制器或(已知)命名的控制器,因此没有简单的方法来询问应用程序中的“未知”情节提要元素。实例化未显示的UI元素也可能有副作用。然而

如果将
.storyboard
文件置于复制文件构建阶段,则可以查询XML并恢复,例如UIViewController/UIView标识符、自定义属性等。这似乎是一项合理的工作。安装方面(cocoapods)它只是MacOS,但是如果你将它嵌入到你的iOS应用程序中,它会运行得很好(安装必备组件并克隆IBDecodable/git子模块,不管什么)。可能有效率和本地化的问题,我略谈了一下,但对于我的用例(从自定义属性构建板载弹出窗口,但没有明确说明),它工作正常

为了更具体地回答提出的问题,询问情节串连板上的ID将允许应用程序查找(通过dict或类似方式),并实例化视图控制器,无论它在何处可视化存储

例如:

Bundle.main.urls(forResourcesWithExtension: "storyboard", subdirectory: nil)?
    .forEach({ (url) in
        do {
            let file = try StoryboardFile(url: url)
            if !file.document.launchScreen {
                file.document.scenes?.forEach({ (scene) in
                    scene.viewController?.viewController.rootView?.subviews?
                        .forEach({ (view) in
                            // Do stuff...
                        })
                    })
                }
            } catch { /* ... */ }
        })

您可以在这一行self.navigationController.viewControllers中获取故事板viewcontroller的导航堆栈阵列您可以获取viewcontrollers@JitendraModi你是说
self.navigationController.viewControllers
?我的意思是我想从UIStoryboard实例化新的viewController,而不是访问导航堆栈数组上已经存在的viewController。你想做什么?你想通过传递数组在新的viewcontroller中导航?如果你的viewcontroller知道他的故事板?,这可能是你需要的解决方案?哦,哇,这太棒了!让我试着把它合并到代码中,然后再给你回复。谢谢“请注意,特别是已编译的故事板后缀不是SDK的文档部分,因此它可能会在未来版本的iOS/Xcode中更改。”-->没关系,我可能可以添加一些解决方法和预防措施,现在我知道了
NSBundle
的工作原理。即使它发生了更改,除非你用新的iOS部署目标重新构建你的应用程序,否则它不会影响你。这是个好主意。。。为了我。不适用于继续我工作的开发人员:(教他们怎么做。没那么复杂。一个好的iOS开发人员应该能够同时做到这两个方面。(\w情节提要和编程)这并不复杂,但也不太有效,尤其是在视觉设计布局方面。能够快速操纵设计的人就是首先创建设计的人。其他人仍然需要深入到代码中,搜索控制单元格中标签y位置的一行代码将tableview放在容器中,页面控件放在容器中,也可以这样做。但我有点不喜欢segue。如果ViewController已经足够多,segue会使情节提要变得杂乱无章(因此我想将它们分离到不同的情节提要)。但它仍然会很混乱。我想试试不同的编程方法,找出最适合我的方法。:)