ios – Why does my snapshot() in SwiftUI not seize downloaded photographs from WebImage (SDWebImageSwiftUI)?

ios – Why does my snapshot() in SwiftUI not seize downloaded photographs from WebImage (SDWebImageSwiftUI)?


You say you “waited for a very long time” for the picture to be downloaded, however when you did do this accurately, you wouldn’t have this drawback within the first place.

Should you simply have a UIHostingController and simply name drawHierarchy, you aren’t ready in any respect. drawHierarchy will trigger SwiftUI will solely run its lifecycle for a really brief time, simply sufficient that one thing is drawn.

To really anticipate the picture to be downloaded, you have to add the UIHostingController to a UIWindow, and solely then can the SwiftUI lifecycle run for an prolonged time frame. Should you do a Process.sleep throughout this time, you may anticipate the picture to be downloaded.

Right here is a few code that does this. That is modified from ViewHosting.swift in ViewInspector. You in all probability can additional simplify this relying in your wants.

@MainActor
public enum ViewHosting { }

public extension ViewHosting {
    
    struct ViewId: Hashable, Sendable {
        let operate: String
        var key: String { operate }
    }

    @MainActor
    static func host<V, R>(_ view: V,
                        operate: String = #operate,
                        whileHosted: @MainActor (UIViewController) async throws -> R
    ) async rethrows -> R the place V: View {
        let viewId = ViewId(operate: operate)
        let vc = host(view: view, viewId: viewId)
        let outcome = strive await whileHosted(vc)
        expel(viewId: viewId)
        return outcome
    }

    @MainActor
    non-public static func host<V>(view: V, viewId: ViewId) -> UIViewController the place V: View {
        let parentVC = rootViewController
        let childVC = hostVC(view)
        retailer(Hosted(viewController: childVC), viewId: viewId)
        childVC.view.translatesAutoresizingMaskIntoConstraints = false
        childVC.view.body = parentVC.view.body
        willMove(childVC, to: parentVC)
        parentVC.addChild(childVC)
        parentVC.view.addSubview(childVC.view)
        NSLayoutConstraint.activate([
            childVC.view.leadingAnchor.constraint(equalTo: parentVC.view.leadingAnchor),
            childVC.view.topAnchor.constraint(equalTo: parentVC.view.topAnchor),
        ])
        didMove(childVC, to: parentVC)
        window.layoutIfNeeded()
        return childVC
    }

    static func expel(operate: String = #operate) {
        let viewId = ViewId(operate: operate)
        MainActor.assumeIsolated {
            expel(viewId: viewId)
        }
    }

    @MainActor
    non-public static func expel(viewId: ViewId) {
        guard let hosted = expelHosted(viewId: viewId) else { return }
        let childVC = hosted.viewController
        willMove(childVC, to: nil)
        childVC.view.removeFromSuperview()
        childVC.removeFromParent()
        didMove(childVC, to: nil)
    }
}

@MainActor
non-public extension ViewHosting {
    
    struct Hosted {
        let viewController: UIViewController
    }
    non-public static var hosted: [ViewId: Hosted] = [:]
    static let window: UIWindow = makeWindow()
    static func makeWindow() -> UIWindow {
        let body = UIScreen.most important.bounds
        let window = UIWindow(body: body)
        installRootViewController(window)
        window.makeKeyAndVisible()
        window.layoutIfNeeded()
        return window
    }
    @discardableResult
    static func installRootViewController(_ window: UIWindow) -> UIViewController {
        let vc = UIViewController()
        window.rootViewController = vc
        vc.view.translatesAutoresizingMaskIntoConstraints = false
        return vc
    }
    
    static var rootViewController: UIViewController {
        window.rootViewController ?? installRootViewController(window)
    }
    static func hostVC<V>(_ view: V) -> UIHostingController<V> the place V: View {
        UIHostingController(rootView: view)
    }
    
    // MARK: - WillMove & DidMove
    
    static func willMove(_ youngster: UIViewController, to father or mother: UIViewController?) {
        youngster.willMove(toParent: father or mother)
    }
    static func didMove(_ youngster: UIViewController, to father or mother: UIViewController?) {
        youngster.didMove(toParent: father or mother)
    }
    
    // MARK: - ViewController identification
    
    static func retailer(_ hosted: Hosted, viewId: ViewId) {
        self.hosted[viewId] = hosted
    }
    
    static func expelHosted(viewId: ViewId) -> Hosted? {
        return hosted.removeValue(forKey: viewId)
    }
}

non-public extension NSLayoutConstraint {
    func precedence(_ worth: UILayoutPriority) -> NSLayoutConstraint {
        precedence = worth
        return self
    }
}

Right here is an instance utilization:

struct ContentView: View {
    @State non-public var img: UIImage?
    var physique: some View {
        Group {
            if let img {
                Picture(uiImage: img)
            } else {
                Textual content("Ready...")
            }
        }.job {
            strive? await Process.sleep(for: .seconds(1))
            print("Start snapshot")
            img = await snapshot(of: WebImage(url: URL(string: "https://picsum.photographs/200/300"), content material: .self) {
                ProgressView()
            })
        }
    }
    
    func snapshot(of view: some View) async -> UIImage {
        await ViewHosting.host(view) { vc in
            strive? await Process.sleep(for: .seconds(2)) // anticipate the picture to obtain
            vc.view.sizeToFit() // resize the view to be an applicable measurement
            let renderer = UIGraphicsImageRenderer(measurement: vc.view.bounds.measurement)
            return renderer.picture { _ in
                vc.view.drawHierarchy(in: vc.view.bounds, afterScreenUpdates: true)
            }
        }
    }
}

author avatar
roosho Senior Engineer (Technical Services)
I am Rakib Raihan RooSho, Jack of all IT Trades. You got it right. Good for nothing. I try a lot of things and fail more than that. That's how I learn. Whenever I succeed, I note that in my cookbook. Eventually, that became my blog. 
rooshohttps://www.roosho.com
I am Rakib Raihan RooSho, Jack of all IT Trades. You got it right. Good for nothing. I try a lot of things and fail more than that. That's how I learn. Whenever I succeed, I note that in my cookbook. Eventually, that became my blog. 

Related Articles

LEAVE A REPLY

Please enter your comment!
Please enter your name here


Latest Articles

author avatar
roosho Senior Engineer (Technical Services)
I am Rakib Raihan RooSho, Jack of all IT Trades. You got it right. Good for nothing. I try a lot of things and fail more than that. That's how I learn. Whenever I succeed, I note that in my cookbook. Eventually, that became my blog.