如何在 SwiftUI 中显示整个 CALayer 内容?

How could I display the entire CALayer content in the SwiftUI?

我的代码很简单如下:

import PocketSVG
import SwiftUI

struct SVGImage: UIViewRepresentable {
    var SVGName: String?
    var tintStrokeColor: CGColor?
    var tintFillColor: CGColor?

    func makeUIView(context: Context) -> UIView {
        return UIView()
    }

    func updateUIView(_ view: UIView, context: Context) {
        let svgURL = Bundle.main.url(forResource: "tiger", withExtension: "svg")!
        let paths = SVGBezierPath.pathsFromSVG(at: svgURL)
        let layer = CALayer()
        for path in paths {
            let shapeLayer = CAShapeLayer()
            shapeLayer.path = path.cgPath

            if let any = path.svgAttributes["stroke"] {
                shapeLayer.strokeColor = (any as! CGColor)
            }

            if let any = path.svgAttributes["fill"] {
                shapeLayer.fillColor = (any as! CGColor)
            }

            layer.addSublayer(shapeLayer)
        }

        layer.contentsGravity = CALayerContentsGravity.resizeAspectFill
        layer.contentsScale = UIScreen.main.scale
        view.layer.addSublayer(layer)
    }
}

struct SVGImage_Previews: PreviewProvider {
    static var previews: some View {
        SVGImage()
    }
}

但是在View中并没有显示整个图像,只显示了一部分。

我也设置了CALayerContentsGravity.resizeAspectFillUIScreen.main.scale,但是没有生效

这是一个相对容易的修复:

layer.position = svgView.center

并将图层添加到 UIView,如下所示:

import PocketSVG
import SwiftUI

struct SVGImage: UIViewRepresentable {
    var SVGName: String?
    var tintStrokeColor: CGColor?
    var tintFillColor: CGColor?

    func makeUIView(context: Context) -> UIView {
        let svgView = UIView(frame: UIScreen.main.bounds)
        svgView.contentMode = .scaleAspectFit

        let svgURL = Bundle.main.url(forResource: "tiger", withExtension: "svg")!
        let paths = SVGBezierPath.pathsFromSVG(at: svgURL)
        let layer = CALayer()

        for path in paths {
            let shapeLayer = CAShapeLayer()
            shapeLayer.path = path.cgPath

            if let any = path.svgAttributes["stroke"] {
                shapeLayer.strokeColor = (any as! CGColor)
            }

            if let any = path.svgAttributes["fill"] {
                shapeLayer.fillColor = (any as! CGColor)
            }

            layer.addSublayer(shapeLayer)
        }

        layer.position = svgView.center
        svgView.layer.addSublayer(layer)

        return svgView
    }

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

    }
}

struct SVGImage_Previews: PreviewProvider {
    static var previews: some View {
        SVGImage()
    }
}
import SwiftUI
// It works on iPhone, iPad 13+ and macOS 10.15+, yep with UIView.
// Persistent root view we going to use
// Touches can be also intercepted by subclassing it
let viewRoot: UIView = UIView()

// CALayer : This is "The One"
// Can use any CALayer subclass
let layerOne: CALayer = CALayer()

// Binding to change "The One" geometry
let layerOneBinding: Binding<CGRect> = .init(get: { () -> CGRect in
    CGRect(origin: layerOne.position, size: layerOne.bounds.size)
}, set: { (newValue: CGRect) in
    layerOne.position = newValue.origin
    layerOne.bounds.size = newValue.size
})


// This is UIViewRepresentable for "The One"
struct ViewRootRepresenter: UIViewRepresentable {
    // Think as it's initializer
    func makeUIView(context: Context) -> UIView {
        // All objects better be persistently pre-initialized elsewhere, use only references.
        // Use this method only to pass references, extra adjustments to UIView or CALayer apply outside SwiftUI.
        // ...
        // ...but for testing only:
        // Make UIView red to see it
        viewRoot.backgroundColor = UIColor.red
        // Make "The One" green to see it
        layerOne.backgroundColor = UIColor.green.cgColor
        // ...
        // add LayerOne to view hierarchy so it draws, but better to have it done elsewhere
        viewRoot.layer.addSublayer(layerOne)
        // And just all together as UIView
        return viewRoot
    }
    //
    func updateUIView(_ uiView: UIView, context: UIViewRepresentableContext<ViewRootRepresenter>) {
        print("updateUIView")
        // Protocol required
        // Do whatever you think you can do here
    }
}

struct GeometryGetter: View {
    // "Binding" only to pass geometry changes towards "The One"
    @Binding var rectBinded: CGRect
    // Look carefully what is actually returned...
    var body: some View {
        return GeometryReader { geometry in
            self.makeView(geometry: geometry)
        }
    }

    func makeView(geometry: GeometryProxy) -> some View {
        // Update "Bounding" to resize "The One"
        DispatchQueue.main.async {
            self.rectBinded = geometry.frame(in: .global)
        }
        // Return ViewRootRepresenter here :)
        return ViewRootRepresenter()
        // ...due to use of persistent pre-initialized UIView and CALayer no objects initialize whithin SwiftUI pipeline
    }

}


// ContentView first on pipeline
// ...think as SwiftUI's RootView
struct ContentView: View {
    var body: some View {
        // Use GeometryGetter here, through layerRootBinding it applies geometry to "The One"
        GeometryGetter(rectBinded: layerOneBinding)
    }
}