如何将不在根层次结构中的 SwiftUI 视图呈现为 UIImage? [英] How do I render a SwiftUI View that is not at the root hierarchy as a UIImage?

查看:24
本文介绍了如何将不在根层次结构中的 SwiftUI 视图呈现为 UIImage?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

假设我有一个简单的 SwiftUI 视图,而不是像这样的 ContentView:

Suppose I have a simple SwiftUI View that is not the ContentView such as this:

struct Test: View {        
    var body: some View {
        VStack {
            Text("Test 1")
            Text("Test 2")
        }
    }
}

如何将此视图渲染为 UIImage?

How can I render this view as a UIImage?

我研究了以下解决方案:

I've looked into solutions such as :

extension UIView {
    func asImage() -> UIImage {
        let renderer = UIGraphicsImageRenderer(bounds: bounds)
        return renderer.image { rendererContext in
            layer.render(in: rendererContext.cgContext)
        }
    }
}

但似乎这样的解决方案只适用于 UIView,而不适用于 SwiftUI 视图.

But it seems that solutions like that only work on UIView, not a SwiftUI View.

推荐答案

这是对我有用的方法,因为我需要将图像与其他图像并排放置时的大小完全相同.希望对其他人有所帮助.

Here is the approach that works for me, as I needed to get image exactly sized as it is when placed alongside others. Hope it would be helpful for some else.

Demo:上面的分隔线是 SwiftUI 渲染的,下面是图片(在边框中显示大小)

Demo: above divider is SwiftUI rendered, below is image (in border to show size)

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

        // locate far out of screen
        controller.view.frame = CGRect(x: 0, y: CGFloat(Int.max), width: 1, height: 1)
        UIApplication.shared.windows.first!.rootViewController?.view.addSubview(controller.view)

        let size = controller.sizeThatFits(in: UIScreen.main.bounds.size)
        controller.view.bounds = CGRect(origin: .zero, size: size)
        controller.view.sizeToFit()

        let image = controller.view.asImage()
        controller.view.removeFromSuperview()
        return image
    }
}

extension UIView {
    func asImage() -> UIImage {
        let renderer = UIGraphicsImageRenderer(bounds: bounds)
        return renderer.image { rendererContext in
// [!!] Uncomment to clip resulting image
//             rendererContext.cgContext.addPath(
//                UIBezierPath(roundedRect: bounds, cornerRadius: 20).cgPath)
//            rendererContext.cgContext.clip()

// As commented by @MaxIsom below in some cases might be needed
// to make this asynchronously, so uncomment below DispatchQueue
// if you'd same met crash
//            DispatchQueue.main.async {
                 layer.render(in: rendererContext.cgContext)
//            }
        }
    }
}


// TESTING
struct TestableView: View {
    var body: some View {
        VStack {
            Text("Test 1")
            Text("Test 2")
        }
    }
}

struct TestBackgroundRendering: View {
    var body: some View {
        VStack {
            TestableView()
            Divider()
            Image(uiImage: render())
                .border(Color.black)
        }
    }
    
    private func render() -> UIImage {
        TestableView().asImage()
    }
}

struct TestBackgroundRendering_Previews: PreviewProvider {
    static var previews: some View {
        TestBackgroundRendering()
    }
}

这篇关于如何将不在根层次结构中的 SwiftUI 视图呈现为 UIImage?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆