我发现我的故事板已经变得非常复杂,并决定将其分割成不同的故事板。但我想有自由来实例化一个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
文件夹的限制。
这不是在运行时间方面最有效的代码,我知道了。但现在,它很容易被理解,我可以用这个活。 :)感谢大家谁帮助!
故事板通常设置的是局部的,所以你应该看看他们在你的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中的未来版本中改变。
在您的情况,也许会,如果你放弃故事板一起被清洁。和编程创建所有的视图控制器。
不要那样做。
相反,用故事板引用Segue公司以不同的故事板是必要的。
如别处所述,当应用程序被编译和运行故事板被编译为不透明(即二进制的,未记录).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 { /* ... */ }
})