部分切断了SwiftUI视图的快照

人气:770 发布:2022-10-16 标签: ios swiftui swift ios15 uiimage

问题描述

我尝试使用HWS: How to convert a SwiftUI view to an image中的代码从SwiftUI视图(快照)创建UIImage

我得到以下结果,很明显是不正确的,因为图片被截断了。

编码:

struct ContentView: View {
    @State private var savedImage: UIImage?

    var textView: some View {
        Text("Hello, SwiftUI")
            .padding()
            .background(Color.blue)
            .foregroundColor(.white)
            .clipShape(Capsule())
    }

    var body: some View {
        ZStack {
            VStack(spacing: 100) {
                textView

                Button("Save to image") {
                    savedImage = textView.snapshot()
                }
            }

            if let savedImage = savedImage {
                Image(uiImage: savedImage)
                    .border(Color.red)
            }
        }
    }
}
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)
        }
    }
}

看起来快照的原始视图比应有的位置低,但我不确定。我如何修复此问题?

编辑

我们发现这个问题并不发生在iOS 14上,而只发生在iOS 15上。所以问题是.如何针对iOS 15修复此问题?

推荐答案

我最近也注意到了这个问题。我在不同的模拟器(例如,iPhone8和iPhone13Pro)上进行了测试,发现偏移量似乎总是状态栏高度的一半。因此,我怀疑当您在内部调用drawHierarchy(in:afterScreenUpdates:)时,SwiftUI总是将安全区域插入考虑在内。

因此,我使用edgesIgnoringSafeArea(_:)视图修饰符修改了View扩展中的snapshot()函数,它起作用了:

extension View {
    func snapshot() -> UIImage {
        let controller = UIHostingController(rootView: self.edgesIgnoringSafeArea(.all))
        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)
        }
    }
}

892