SwiftUI - 代码中的扩展定义放置

SwiftUI - Extension definition placement in code

Xcode 最近使用将 SwiftUI 视图转换为 UIImage 的扩展让我很头疼。该项目主要是 SwiftUI 通用应用程序。我需要 UIImage 来定义 ClockKit Complications,我发现以下代码理论上应该可以做到这一点:

extension View {
    func snapshot() -> UIImage {
        let controller = UIHostingController(rootView: self)
        let view = controller.view

        let targetSize = controller.view.intrinsicContentSize
        view?.bounds = CGRect(origin: .zero, size: targetSize)
        view?.backgroundColor = .clear

        let renderer = UIGraphicsImageRenderer(size: targetSize)

        return renderer.image { _ in
            view?.drawHierarchy(in: controller.view.bounds, afterScreenUpdates: true)
        }
    }
}

问题是:

  1. Xcode 只允许将扩展放在主 ContentView 的顶部,watchOS Extension 不会检测到它(因为它有自己的 ContentView 并且不能共享或设置为两者的目标成员).在代码的任何其他地方我得到 the following errors.
  2. 即使扩展没有在主 ContentView 中给出错误,常量声明也不起作用,它给出 a following error。如果将其更改为 let exportimage: UIImage = MoonView.snapshot(MoonView),则只会显示 无法将类型 'MoonView.Type' 的值转换为预期的参数类型 'MoonView'.

如何解决这个问题?扩展可以放置的地方有问题吗? Xcode 版本 12.5,我清理了构建文件夹,但找不到与名称有任何相似之处。在所有代码文件中,SwiftUI 和 UIKit 库都被导入(尽管后者对于编译器来说似乎不是最重要的)。

非常感谢所有贡献。

给定以下扩展名:

extension View {
  func sayHello() {
    print("Hello, world!")
  }
}

为了实现#1,您可以将扩展名放在它自己的文件中,然后将其分配给将要使用它的每个目标。

然后,正如@aheze 在评论中指出的那样,您必须在 View 的实例(而不是 View 类型本身)上调用该函数。所以:

MoonView().sayHello()

但是,在您的情况下,您看到错误的原因(如范围内的 Cannot find UIHostingController)是因为 UIHostingControllerUIGraphicsImageRenderer 在 WatchOS 上不可用(请参阅https://developer.apple.com/documentation/swiftui/uihostingcontroller and https://developer.apple.com/documentation/uikit/uigraphicsimagerenderer),这意味着所写的扩展不会在该平台上编译。