How to convert a View (not UIView) to an image?

前端 未结 4 583
醉梦人生
醉梦人生 2020-12-08 22:34

Similar to this thread

I would like to convert a SwiftUI View rather than a UIView to an image.

4条回答
  •  天命终不由人
    2020-12-08 23:25

    Solution

    Here is a possible solution that uses a UIHostingController that is inserted in the background of the rootViewController:

    func convertViewToData(view: V, size: CGSize, completion: @escaping (Data?) -> Void) where V: View {
        guard let rootVC = UIApplication.shared.windows.first?.rootViewController else {
            completion(nil)
            return
        }
        let imageVC = UIHostingController(rootView: view.edgesIgnoringSafeArea(.all))
        imageVC.view.frame = CGRect(origin: .zero, size: size)
        DispatchQueue.main.async {
            rootVC.view.insertSubview(imageVC.view, at: 0)
            let uiImage = imageVC.view.asImage(size: size)
            imageVC.view.removeFromSuperview()
            completion(uiImage.pngData())
        }
    }
    

    You also need a modified version of the asImage extension proposed here by kontiki (setting UIGraphicsImageRendererFormat is necessary as new devices can have 2x or 3x scale):

    extension UIView {
        func asImage(size: CGSize) -> UIImage {
            let format = UIGraphicsImageRendererFormat()
            format.scale = 1
            return UIGraphicsImageRenderer(size: size, format: format).image { context in
                layer.render(in: context.cgContext)
            }
        }
    }
    

    Usage

    Assuming you have some test view:

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

    you can convert this View to Data which can be used to return an Image (or UIImage):

    convertViewToData(view: testView, size: CGSize(width: 300, height: 300)) {
        guard let imageData = $0, let uiImage = UIImage(data: imageData) else { return }
        return Image(uiImage: uiImage)
    }
    

    The Data object can also be saved to file, shared...


    Demo

    struct ContentView: View {
        @State var imageData: Data?
    
        var body: some View {
            VStack {
                testView
                    .frame(width: 50, height: 50)
                if let imageData = imageData, let uiImage = UIImage(data: imageData) {
                    Image(uiImage: uiImage)
                }
            }
            .onAppear {
                convertViewToData(view: testView, size: .init(width: 300, height: 300)) {
                    imageData = $0
                }
            }
        }
    
        var testView: some View {
            ZStack {
                Color.blue
                Circle()
                    .fill(Color.red)
            }
        }
    }
    

提交回复
热议问题