简体   繁体   English

将 SwiftUI 视图转换为图像时,它无法按预期工作

[英]When converting SwiftUI view to image, it doesn't work as expected

My goal is to convert SwiftUI view to image.我的目标是将 SwiftUI 视图转换为图像。 I am currently building it for iOS 15 here is the code that converts the view to image我目前正在为 iOS 15 构建它,这是将视图转换为图像的代码

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)
        }
    }
}

Expected result: https://share.cleanshot.com/5Pvzb7预期结果: https://share.cleanshot.com/5Pvzb7

Actual result: https://share.cleanshot.com/O4GKUF实际结果: https://share.cleanshot.com/O4GKUF

Below is the body of the view that has a button and image view下面是具有按钮和图像视图的视图主体

var body: some View {
        VStack {
            
            imageView
            
            Spacer(minLength: 200)
            
            Button {
                UIImageWriteToSavedPhotosAlbum(imageView.snapshot(), nil, nil, nil)
                
            } label: {
                Label("Save to Photos", systemImage: "photo")
            }
        }
    }

Imageview is hardcoded with data and image is loaded from the internet Imageview 是用数据硬编码的,图像是从互联网加载的

    var imageView: some View {
        VStack {
            ZStack(alignment: .top) {
                
                Color.red
                
                VStack {
                    VStack {
                        HStack(alignment: .top) {
                            Rectangle.init().frame(width: 56, height: 56).cornerRadius(28)
                            
                            VStack {
                                HStack {
                                    Text.init("Yura Filin").font(.system(size: 16))
                                    Spacer()
                                }.padding(.bottom, 1)
                                
                                HStack {
                                    Text.init("qweqwewqeqqweqeasd asd asd aosidhsa doaid adoaid adiad hiu uh i hiu ih uhuih iuhiu ih asdi ").lineLimit(90).multilineTextAlignment(.leading).font(.system(size: 16))
                                    Spacer()
                                }.padding(.bottom, 2)
                                
                                HStack {
                                    Image(uiImage: image)
                                        .resizable()
                                        .aspectRatio(contentMode: .fit)
                                        .frame(width: (286 - 64 - 12))
                                        .onReceive(imageLoader.didChange) { data in
                                            self.image = UIImage(data: data) ?? UIImage() }.cornerRadius(14)
                                    Spacer()
                                }
                                Spacer()
                            }.padding(.leading, 12).readSize { size in
                                print(size)
                            }
                        }
                    }.padding(16).background(.green).cornerRadius(12)
                }.padding(64)
            }
        }.ignoresSafeArea()
    }

Any help is appreciated.任何帮助表示赞赏。 Thanks!谢谢!

Unlike when a usual view is rendered and the sizing of that view is constrained by the device, that doesn't happen here.与渲染普通视图并且该视图的大小受设备限制时不同,此处不会发生这种情况。 So you need to apply a frame with defined width (and possibly height) to the view you're capturing.因此,您需要将具有定义宽度(可能还有高度)的框架应用于您正在捕获的视图。

When I needed to do this, I created another view that isn't rendered to the user and used it solely for rendering the image.当我需要这样做时,我创建了另一个不呈现给用户的视图,并将其仅用于呈现图像。 That way, any sizing you have to define in the frame doesn't mess up what the user sees.这样,您必须在框架中定义的任何尺寸都不会弄乱用户看到的内容。

It might take you a while to get it right, but try adding a frame modifier with desired width to the view you're rendering.您可能需要一段时间才能正确处理,但请尝试将具有所需宽度的帧修改器添加到您正在渲染的视图中。

Here is the code as shown below which solves your problem and in order to call UIImageWriteToSavedPhotosAlbum() you must add the NSPhotoLibraryAddUsageDescription key to your Info.plist下面是解决您的问题的代码,为了调用UIImageWriteToSavedPhotosAlbum()您必须将NSPhotoLibraryAddUsageDescription键添加到Info.plist

import SwiftUI

struct ContentView: View {
    var imageView: some View {
        VStack {
            ZStack(alignment: .top) {
                
                Color.red
                
                VStack {
                    VStack {
                        HStack(alignment: .top) {
                            Rectangle.init().frame(width: 56, height: 56).cornerRadius(28)
                            
                            VStack {
                                HStack {
                                    Text.init("Yura Filin").font(.system(size: 16))
                                    Spacer()
                                }.padding(.bottom, 1)
                                
                                HStack {
                                    Text.init("qweqwewqeqqweqeasd asd asd aosidhsa doaid adoaid adiad hiu uh i hiu ih uhuih iuhiu ih asdi ").lineLimit(90).multilineTextAlignment(.leading).font(.system(size: 16))
                                    Spacer()
                                }.padding(.bottom, 2)
                                
                                HStack {
                                    Image("image")
                                        .resizable()
                                        .aspectRatio(contentMode: .fit)
                                        .frame(width: (286 - 64 - 12))
                                        .cornerRadius(14)
                                    Spacer()
                                }
                                Spacer()
                            }.padding(.leading, 12)
                        }
                    }.padding(16).background(.green).cornerRadius(12)
                }.padding(64)
            }
        }.ignoresSafeArea()
    }
    
    var body: some View {
        VStack {
            Button("Save to Photos") {
                let image = imageView.snapshot()
                UIImageWriteToSavedPhotosAlbum(image, nil, nil, nil)
            }
            imageView
            
            Spacer(minLength: 200)
            
            Button("Save to Photos") {
                let image = imageView.snapshot()
                UIImageWriteToSavedPhotosAlbum(image, nil, nil, nil)
            }
        }
    }
    
}


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)
        }
    }
}


struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

Output: Output:

在此处输入图像描述

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM