Search code examples
swiftcashapelayer

How to properly copy CAShapeLayer in swift?


I'm trying to create copies of CAShapeLayer in swift but I'm getting a crash

Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[CAShapeLayer copyWithZone:]: unrecognized selector sent to instance 0x282e87e60'

which extra steps should I take to allow CALayer .copy() work without crashing the app? Even if I don't cast the result of .copy() it stills fails exactly at that copy() line...

private var drawLines: [CAShapeLayer]
func getCopiedLayers() -> [CAShapeLayer] {
    return drawLines.compactMap { layer -> CAShapeLayer? in
        return layer.copy() as? CAShapeLayer
    }
}

what I'm doing wrong here? Thanks in advance for the answers


Solution

  • CALayer does not conform to NSCopying from API, but it conforms to NSSecureCoding, so it is possible to add copying capability as below

    Tested with Xcode 11.2 / iOS 13.2 (with CAShapeLayer & CAGradientLayer)

    extension CALayer : NSCopying {
        public func copy(with zone: NSZone? = nil) -> Any {
            if let data = try? NSKeyedArchiver.archivedData(withRootObject: self, requiringSecureCoding: false) {
                if let newInstance = try? NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(data) {
                    return newInstance
                }
            }
            fatalError() // << should never got here
        }
    }
    

    Now, it is possible to call layer.copy() to any layer (theoretically) without exception.