mchoe / SwiftSVG

A simple, performant, and lightweight SVG parser
Other
1.94k stars 229 forks source link

Question: Can the underlying data resource be freed from a view instance for later re-use? #106

Open chrislconover opened 6 years ago

chrislconover commented 6 years ago

I would like to render an image as part of collection / table view cells. For a regular UIImageView, I can clear the image property. How would I do this with SwiftSvg? I checked UIView+SVG.swift and don't see anything immediately obvious.v

Thanks!

chrislconover commented 6 years ago

And is there anyway to bind it later, or do you have to create and re-create a view every time?

chrislconover commented 6 years ago

I coded a work-around for the above - to implement a view class that clobbers any previous layer and renders new SVG data. Also, and perhaps more importantly, I noticed that the images were not scaling properly, so I added support for that. And finally, I broke out data parsing into a failable factory method.

extension Data {

    static func from(SVGNamed: String) -> Data? {
        if #available(iOS 9.0, OSX 10.11, *) {

            #if os(iOS)
            if let asset = NSDataAsset(name: SVGNamed) {
                return asset.data
            }
            #elseif os(OSX)
            if let asset = NSDataAsset(name: NSDataAsset.Name(SVGNamed)) {
                return asset.data
            }
            #endif

            if let svgURL = Bundle.main.url(forResource: SVGNamed, withExtension: "svg") {
                return try? Data(contentsOf: svgURL)
            }

        } else if let svgURL = Bundle.main.url(forResource: SVGNamed, withExtension: "svg") {
            return try? Data(contentsOf: svgURL)
        }

        return nil
    }
}

class ScalingSvgView: UIView {

    override func layoutSublayers(of layer: CALayer) {
        if layer === self.layer {
            print("SvgView.layoutSublayers: resizing svgLayer to main layer")
            if let _ = svgLayer {
                print("detected svg layer, updating...")
            }
            svgLayer?.resizeToFit(bounds)
        }
    }

    func render(_ SVGData: Data, parser: SVGParser? = nil, completion: (() -> ())? = nil) {

        CALayer(SVGData: SVGData, parser: parser) { [weak self] (svgLayer) in
            DispatchQueue.main.safeAsync { [weak self] in
                self?.svgLayer?.removeFromSuperlayer()
                self?.layer.addSublayer(svgLayer)
            }
            completion?()
        }
    }

    var svgLayer: SVGLayer? {
        return layer.sublayers?.first as? SVGLayer
    }
}

Usage:

if let data = Data.from(SVGNamed: "foo") {
    let svgView = ScalingSvgView(SVGData: data)
    // add view to existing hierarchy, set constraints ...

    // later update data source
    svgView.render(newData)
}