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