如何在Swift中获得Uistoryboard数组



我发现我的故事板变得非常复杂,决定将其分为不同的故事板。但是,无论我将视图控制器放入何处,我都希望自由实例化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;
}

问题是,我找不到属于诸如.storyboards之类的故事板实例列表的属性/方法。有什么解决方法吗?我知道我可能有一个静态的故事板名称列表,但是这样,扩展名不会动态且与项目无关。

有人可以帮忙吗?谢谢。

编辑:

将接受的答案和答案结合起来,以安全实例化viewController(如果找不到的话,返回零(,这是我的代码:

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文件夹中。

我知道,这不是运行时间的最有效代码。但是就目前而言,很容易被理解,我可以忍受。:)感谢所有帮助的人!

故事板通常设置为本地化,因此您应该在捆绑包资源目录的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中更改。

不要那样做。

相反,必要时将情节板引用到其他故事板上。

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

如其他地方所述,在编译并运行应用程序时,将其编译到不透明的(即二进制,无证件(.storyboardc文件。UIStoryboard API仅允许实例化初始视图控制器或名为ONE(已知(的API,因此没有天真的方法来询问您的应用程序"未知"的故事板元素。实例化未显示的UI元素也可能有副作用。但是...

如果将.storyboard文件放在复制文件构建阶段中,则可以询问XML并恢复例如。uiviewController/uiview标识符,自定义属性等。IBDECOPABLE似乎可以做到合理的工作。在安装方面(可可录(它是仅MACOS的,但是如果您将其嵌入iOS应用程序(安装SWXMLHASH先决条件并克隆IBDECODECODABLE/GIT-SUBMOPMOPULE(,它将愉快地运行。我可能会浏览一些效率和本地化问题,但是对于我的用例(从自定义属性中构建弹出式弹出案件而不明确地构建弹出案(,它可以正常工作。

要更具体地回答提出的问题,询问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 { /* ... */ }
        })

最新更新