UIGraphicsImageRenderer 在 SwiftUI 中返回空白图像

问题描述 投票:0回答:1

在 SwiftUI 中,我想截取名为 ExtractedView 的子视图的屏幕截图。我正在使用 UIGraphicsImageRenderer 将视图渲染为图像。但是,当 ExtractedView 太长时,我会遇到错误消息。我该如何解决这个问题?

在 SwiftUI 中,我想截取子视图 ExtractedView 的屏幕截图。我使用 UIGraphicsImageRenderer 将视图渲染为图像。当ExtractedView太长时,它会输出一个空白图像。(但我需要一张长图片),并且收到以下错误消息。我该如何解决它

运行日志:

Render server returned error for view (0x102804310, _TtGC7SwiftUI14_UIHostingViewV5Kylin13ExtractedView_).

imageSize : (462.0, 2803.3333333333335)

身体

var body: some View {
        VStack {
            Button(action: {
                renderToImage()
            }, label: {
                Text("Snapshot")
            })
            ExtractedView()
        }
    }

提取视图

struct ExtractedView: View {
    var body: some View {
        ScrollView {
            let contentStr = "About 2000 word long piece of text"
            Text(contentStr)
                .frame(maxWidth: UIScreen.main.bounds.size.width, maxHeight: .infinity)
                .padding()
        }
    }
}

渲染到图像:

@MainActor func renderToImage() -> UIImage? {
        return ExtractedView()
            .saveToImage()
    }

保存到图像

extension View {
    func saveToImage() -> 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)
        let image = renderer.image { render in
            view?.drawHierarchy(in: controller.view.bounds, afterScreenUpdates: true)
        }
        
        return image
    }
}

我尝试使用ImageRenderer进行屏幕捕获,但它只支持有限的组件,并且不支持ScrollView或TextView,所以我放弃了

swiftui screenshot uigraphicsimagerenderer
1个回答
0
投票

使用

ImageRenderer
,我获得了非常整洁的图像。

正如您已经提到的,

ImageRenderer
在很多方面都受到限制(ScrollView、WebView等),我的方法是为您想要在没有
ScrollView
的情况下截图的内容创建一个计算属性,然后将内容传递给
 ImageRenderer

  1. 首先,在
    ExtractedView
struct ExtractedView: View {
    let contentStr = twokwords // I created some at https://ctxt.io/2/AABIE5oBFg
    // 1. create a computed property for the content without the scroll view (text in this case)
    private var myContentView: some View {
        Text(contentStr)
    }
    
    var body: some View {
        ScrollView {
            // 2. Here, you can use the content as you previously did
            myContentView
                .frame(maxWidth: UIScreen.main.bounds.size.width, maxHeight: .infinity)
                .padding()
        }
    }

    // 3. This is just a helper to retrieve the content to screenshot
    // You can remove `private` modifier on `myContentView` property and access it directly
    func getViewToScreenshot() -> some View {
        myContentView
    }
}
  1. 终于
struct ContentView: View {
    @State private var screenshot: UIImage?
    @Environment(\.displayScale) private var displayScale
    
    var body: some View {
        VStack {
            if let screenshot {
                Image(uiImage: screenshot)
                    .resizable()
                    .scaledToFit()
                    .frame(width: UIScreen.main.bounds.width)
                    .frame(maxHeight: .infinity)
                    .onTapGesture {
                        self.screenshot = nil
                    }
            } else {
                Button(action: {
                    if let img = renderToImage() {
                        self.screenshot = img
                    }
                }, label: {
                    Text("Snapshot")
                })
                extractedView
            }
        }
    }
    
    
    private var extractedView: ExtractedView {
        ExtractedView()
    }
    
    @MainActor func renderToImage() -> UIImage? {
        // 1. Get the view you want and add any modifier you want (padding in my case)
        let screenshot = extractedView.getViewToScreenshot().padding()
        let renderer = ImageRenderer(content: screenshot)
        
        // 2. make sure to use the correct display scale for the device
        // Learn more at https://www.hackingwithswift.com/quick-start/swiftui/how-to-convert-a-swiftui-view-to-an-image
        renderer.scale =  displayScale

        // 3. Set the proposedViewSize you want
        var size = ProposedViewSize.infinity
        size.width = 400 // 4. set the width you want for better result
        renderer.proposedSize = size
        
        if let uiImage = renderer.uiImage {
            return uiImage
        }
        return nil
        
    }
}

让我知道你的效果如何!

© www.soinside.com 2019 - 2024. All rights reserved.