簡體   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