Swift 包管理器 - 故事板包

Ace*_*een 12 bundle storyboard uistoryboard swift-package-manager

我正在尝试在您的具有故事板的项目之一中添加对 SPM 的支持。

目前我们抓住了它,UIStoryboard(name: String, bundle: String?)但这似乎不适用于 SPM,因为实际上没有捆绑。即使打印所有捆绑包也不会显示我们包裹的捆绑包。

我们可以通过任何方式支持故事板还是 SPM 只是文件?

尝试:

UIStoryboard(name: "GiftCards", bundle: Bundle(for: self))
UIStoryboard(name: "GiftCards", bundle: Bundle(for: type(of: self)))
UIStoryboard(name: "GiftCards", bundle: Bundle(identifier: "com.x.x"))
Run Code Online (Sandbox Code Playgroud)

mm2*_*001 8

从 Xcode 12.0 开始,这种工作是可行的,但需要一些额外的步骤才能完成。

设想:

  • 一个应用程序,显示来自名为的包的嵌入式故事板 BadgeKit
  • 一个BadgeKitPackage.swift头文件// swift-tools-version:5.3或更高版本命名的 Swift 包
  • BadgeKit 中的故事板称为 BadgeKit.storyboard

目标:

  • 在应用程序故事板中添加故事板引用并使其在应用程序中工作

脚步:

将故事板引用添加到应用程序故事板并配置如下:

具有 Storyboard 值“BadgeKit”和 Bundle 标识符“BadgeKit-BadgeKit-resources”的 Storyboard Reference 属性面板

带有 Storyboard 值BadgeKit和 Bundle identifier 的Storyboard Reference 属性面板BadgeKit-BadgeKit-resources

Xcode 会自动生成一个包(及其标识符),供您使用以下格式保存在 SPM 包中找到的资源:[package name]-[package target name]-resources. 在我们的例子中,包名和目标名是相同的 ( BadgeKit)。

虽然 SPM 资源包总是在构建过程中创建并包含在应用程序中,但它们在运行时不会在包自动可用。如果您不在代码中的任何位置导入和使用包的目标,Xcode 会尝试通过不加载该包的资源包来进行优化(这可能是 Apple 的疏忽,仅故事板引用不足以触发此操作)。因此,如果您仅在故事板中使用其资源,则需要一种解决方法来诱使 Xcode 使 SPM 包的包可用。

将此代码添加到应用程序的AppDelegate.swift文件中作为解决方法:

@UIApplicationMain final class AppDelegate: UIResponder {

    […]

    override init() {
        super.init()
        
        // WORKAROUND: Storyboards do not trigger the loading of resource bundles in Swift Packages.
        let bundleNames = ["BadgeKit_BadgeKit"]
        bundleNames.forEach { (bundleName) in
            guard
                let bundleURL = Bundle.main.url(forResource: bundleName, withExtension: "bundle"),
                let bundle = Bundle(url: bundleURL) else {
                preconditionFailure()
            }
            bundle.load()
        }

        […]
    }

    […]

}
Run Code Online (Sandbox Code Playgroud)

在我们的示例中,该数组bundleNames包含一个字符串,该字符串对应于我们的包将在构建过程中为其资源创建的包的预期文件名。Xcode 自动命名这些包文件如下:[package name]_[package target name].bundle. 请注意,bundle 的文件名与其identifier 不同

如果您对在运行时加载和可用的包(及其相应的标识符)感到好奇,您可以使用以下代码进行故障排除:

let bundles = Bundle.allBundles
bundles.forEach { (bundle) in
    print("Bundle identifier loaded: \(bundle.bundleIdentifier)") }
}
Run Code Online (Sandbox Code Playgroud)

在 SPM BadgeKit 包中配置故事板:

  • 使用 SPM 包目标名称(“BadgeKit”)填写“模块”
  • 取消选中“从目标继承模块”

带有 Module 值 BadgeKit 的 Storyboard 属性面板


ful*_*oon 7

关键是在实例化故事板时使用 Bundle.module

1- 将此视图控制器扩展添加到 swift 包中:

 public extension UIViewController{
        
        public static func getStoryboardVC() -> UIViewController { 
            let storyboard = UIStoryboard(name: String(describing: self), bundle: Bundle.module) // Use Bundle.module
            return storyboard.instantiateInitialViewController()!
        }
 }
Run Code Online (Sandbox Code Playgroud)

Bundle.module 表示包含的包。

2- 在应用程序中,就我而言,swift 包称为 MySwiftPackage。我在 swift 包中调用该扩展方法来实例化我想要的视图控制器并呈现它:

   @IBAction func openCard(){
        let vc = MySwiftPackage.MyViewController.getStoryboardVC() as! MySwiftPackage.MyViewController
        vc.personNo = "11111"
        vc.personId = "8888888"
        present(vc, animated: true, completion: nil)
    }
Run Code Online (Sandbox Code Playgroud)