繁体   English   中英

将 SwiftUI 视图渲染为 UIImage

[英]Render SwiftUI View as an UIImage

我正在尝试将 SwiftUI 视图呈现为 UIImage,然后让用户选择保存到相机胶卷或通过电子邮件发送给其他人。

例如,我想将 50 行的列表呈现到 UIImage 中。

struct MyList: View {
    var body: some View {
        List {
            ForEach(0 ..< 50, id:\.self) {
                Text("row \($0)")
            }
        }
    }
}

过去几周在互联网上搜索仍然没有运气。 我尝试了 2 种不同的方法。

1. UIHostingController来源在这里

let hosting = UIHostingController(rootView: Text("TEST"))
hosting.view.frame = // Calculate the content size here //
let snapshot = hosting.view.snapshot        // output: an empty snapshot of the size
print(hosting.view.subviews.count)          // output: 0

我试过layoutSubviews()setNeedsLayout()layoutIfNeeded()loadView() ,但所有结果仍然是 0 个子视图。

2. UIWindow.rootViewController来源在这里

var vc = UIApplication.shared.windows[0].rootViewController
vc = vc.visibleController          // loop through the view controller stack to find the top most view controller
let snapshot = vc.view.snapshot    // output: a snapshot of the top most view controller

这几乎产生了我想要的输出。 然而,我得到的快照实际上是一个屏幕截图,即带有导航栏、标签栏和固定大小(与屏幕大小相同)。 我需要捕获的只是没有这些条的视图内容,有时可能比屏幕大(我的视图在这个例子中是一个很长的列表)。

试图查询vc.view.subviews以查找我想要的表视图,但返回了一个无用的[<_TtGC7SwiftUI16PlatformViewHostGVS_42PlatformViewControllerRepresentableAdaptorGVS_16BridgedSplitViewVVS_22_VariadicView_Children7ElementGVS_5GroupGVS_19_ConditionalContentS4_GVS_17_UnaryViewAdaptorVS_9EmptyView______: 0x7fb061cc4770; frame = (0 0; 414 842); anchorPoint = (0, 0); tintColor = UIExtendedSRGBColorSpace 0 0.478431 1 1; layer = <CALayer: 0x600002d8caa0>>] [<_TtGC7SwiftUI16PlatformViewHostGVS_42PlatformViewControllerRepresentableAdaptorGVS_16BridgedSplitViewVVS_22_VariadicView_Children7ElementGVS_5GroupGVS_19_ConditionalContentS4_GVS_17_UnaryViewAdaptorVS_9EmptyView______: 0x7fb061cc4770; frame = (0 0; 414 842); anchorPoint = (0, 0); tintColor = UIExtendedSRGBColorSpace 0 0.478431 1 1; layer = <CALayer: 0x600002d8caa0>>] [<_TtGC7SwiftUI16PlatformViewHostGVS_42PlatformViewControllerRepresentableAdaptorGVS_16BridgedSplitViewVVS_22_VariadicView_Children7ElementGVS_5GroupGVS_19_ConditionalContentS4_GVS_17_UnaryViewAdaptorVS_9EmptyView______: 0x7fb061cc4770; frame = (0 0; 414 842); anchorPoint = (0, 0); tintColor = UIExtendedSRGBColorSpace 0 0.478431 1 1; layer = <CALayer: 0x600002d8caa0>>]

任何帮助深表感谢。

这样的事情对你有用吗?

import SwiftUI

extension UIView {
    func takeScreenshot() -> UIImage {
        // Begin context
        UIGraphicsBeginImageContextWithOptions(self.bounds.size, false, UIScreen.main.scale)
        // Draw view in that context
        drawHierarchy(in: self.bounds, afterScreenUpdates: true)
        // And finally, get image
        let image = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()

        if (image != nil) {
            UIImageWriteToSavedPhotosAlbum(image!, nil, nil, nil);
            return image!
        }

        return UIImage()
    }
}

struct ContentView: UIViewRepresentable {
    func makeUIView(context: Context) -> UIView {
        let someView = UIView(frame: UIScreen.main.bounds)
        _ = someView.takeScreenshot()
        return someView
    }

    func updateUIView(_ view: UIView, context: Context) {

    }
}

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

我想知道为什么人们说你的问题不清楚。 它实际上是完全合理的,这是我一直在寻找自己做的事情之一。

我终于找到了一种按照您希望完成的方式做事的方法,这很复杂,但确实有效。

这个想法是滥用你的第二种可能性( UIWindow.rootViewController )。 那个很有趣,因为你拿起窗户,弄清楚你想画什么,然后画出来。

但问题是这是您的主窗口,您要求绘制主窗口。

我最终让它工作的方法是做一个UIViewControllerRepresentable ,它在我的 SwiftUI 应用程序中为我提供了一个UIViewController 然后,在它里面,我放了一个UIHostingController ,它在 UIView 中给了我一个 SwiftUI 视图。 换句话说,我做了一座桥。

update函数中,我可以调用异步调度到view.layer.render(context)来绘制图像。

有趣的是, UIViewControllerRepresentable实际上是全屏的。 所以我建议做一个.scale(...)以便你的整个列表适合屏幕(渲染命令不会介意它变小)并且它会居中。 因此,如果您事先知道图像的大小,则可以执行HStack { VStack { YourStuff Spacer() } Spacer() }使其呈现在左上角。

祝你好运!

暂无
暂无

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

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