我怎样才能得到UIStoryboard数组中斯威夫特?

问题描述 投票:1回答:4

我发现我的故事板已经变得非常复杂,并决定将其分割成不同的故事板。但我想有自由来实例化一个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故事板实例的列表。是否有此任何解决方法吗?我知道,我也许可以有故事板的名称的静态列表,但这样一来,扩展不会是动态的,独立的项目。

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

编辑:

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

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文件夹的限制。

这不是在运行时间方面最有效的代码,我知道了。但现在,它很容易被理解,我可以用这个活。 :)感谢大家谁帮助!

ios swift xcode uistoryboard
4个回答
3
投票

故事板通常设置的是局部的,所以你应该看看他们在你的bundle的资源目录的子目录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中的未来版本中改变。


1
投票

在您的情况,也许会,如果你放弃故事板一起被清洁。和编程创建所有的视图控制器。


0
投票

不要那样做。

相反,用故事板引用Segue公司以不同的故事板是必要的。


0
投票

如别处所述,当应用程序被编译和运行故事板被编译为不透明(即二进制的,未记录).storyboardc文件。该UIStoryboard API只允许实例化初始视图控制器,或(已知)命名一个,所以有来询问关于“未知”故事板元素的应用程序没有幼稚的方式。也可能有副作用,以实例化不被显示的UI元素。然而...

如果你把你的.storyboard文件在复制文件生成阶段,你可以询问XML和恢复例如的UIViewController / UIView的标识,自定义属性等IBDecodable似乎做的这一个合理的工作。安装明智(的CocoaPods)它的MacOS只,但如果你在iOS应用中嵌入它会高兴地运行(安装SWXMLHash前提和克隆IBDecodable /混帐子模块它,等等)。有可能我克扣效率和本地化的问题,但为我用的情况下(从构建自定义属性上岗popovers没有被明确它)它的工作确定。

要回答这个问题提出更具体,询问为ID的故事板将允许应用程序找到(通过字典或类似),并创建一个视图控制器,无论它在视觉上存储。

例如:

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 { /* ... */ }
        })
© www.soinside.com 2019 - 2024. All rights reserved.