在iOS 14+上将SwiftUI视图转换为UIImage

3
我可以使用下面的代码将任何SwiftUI视图转换为高分辨率UIImage。它效果很好......直到我尝试使用大于CGSize(宽度:2730,高度:2730)的图像大小。
如果我将图像大小增加到CGSize(宽度:2731,高度:2731)或更大,则会出现以下问题:
self.drawHierarchy(in: self.layer.bounds, afterScreenUpdates: true)

在 "extension UIView" 中,无法再绘制UIImage。
有没有任何想法是什么原因导致了尺寸的限制?
一个注意事项:我可以通过取消注释在 "extension View" 中的2行代码并替换来克服尺寸限制:
self.drawHierarchy(in: self.layer.bounds, afterScreenUpdates: true)

使用:

layer.render(in: context.cgContext)

在“扩展UIView”中......但是,使用“layer.render”将无法呈现像“blur”、SceneKit子视图或金属等图像效果。因此,必须使用“self.drawHierarchy”。
// 1: Set breakpoint on line: print("done") to inspect the high res image
// 2: Run, then tap the image on screen to inspect the highresImage
// 3: repeat after changing the size to CGSize = CGSize(width: 2731, height: 2731)

import SwiftUI

struct ContentView: View {
    @State var blurRadius: CGFloat = 4.0
    let imageSize: CGSize = CGSize(width: 2730, height: 2730)

    var body: some View {
        testView
            .frame(width: 300, height: 300)
            .onTapGesture {
                // Adjust blur radius based on high res image scale
                blurRadius *= imageSize.width * 0.5/300

                // Capture high res image of swiftUI view
                let highresImage = testView.asImage(size: imageSize)
                // set breakpoint here to inspect the high res image size, quality, etc.
                print("done")
                
                // reset blur radius back to 4
                blurRadius = 4
            }
    }

    var testView: some View {
        ZStack {
            Color.blue
            Circle()
                .fill(Color.red)
        }
        .blur(radius: blurRadius)
    }
}

extension UIView {
    func asImage() -> UIImage {
        let format = UIGraphicsImageRendererFormat()
        format.scale = 1
        return UIGraphicsImageRenderer(size: self.layer.frame.size, format: format).image { context in
            self.drawHierarchy(in: self.layer.bounds, afterScreenUpdates: true)
            //layer.render(in: context.cgContext)

        }
    }
}


extension View {
    func asImage(size: CGSize) -> UIImage {
        let controller = UIHostingController(rootView: self)
        controller.view.bounds = CGRect(origin: .zero, size: size)
        //UIApplication.shared.windows.first!.rootViewController?.view.addSubview(controller.view)
        let image = controller.view.asImage()
        //controller.view.removeFromSuperview()
        return image
    }
}


在iPad Pro上(似乎是2倍),限制为4096。 同样,如果使用8192.0 / UIScreen.main.nativeScale,您仍然可以在3x iPhone上呈现8192x8192的图像。Apple可能硬编码了8192x8192像素的限制以渲染视图。 - Christopher Boyd
1个回答

0

我不知道为什么,但是在将视图原点的y坐标更改为任何非零值后,它对我有效。这可能是UIHostingController中的一个错误。

如果您使用非常小的Int,则看不到差异,例如:

controller.view.bounds = CGRect(origin: CGPoint(x: 0, y: 0.0001), size: size)

1
我刚刚尝试了你的建议,但不行... 仍然无法绘制。我使用的是Xcode 12.2,在iOS 14.2的iPhone 12 Pro模拟器上运行。 - KINGandKONG
我对此有一些后续。看起来我没有正确测试我的解决方案。实际上,我可以让它在1x和2x设备上运行,但由于某种原因,在3x设备上失败了。我仍然找不到解决方案,现在已经切换到UIKit视图。 - milan.elsen

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接