I have an array of UIBezierPaths that I am looping over to create a CALayer. The code to generate the CALayer looks like this:
let layer = CAShapeLayer()
layer.path = path.cgPath
layer.strokeColor = UIColor.gray.cgColor
layer.fillColor = UIColor.clear.cgColor
layer.lineWidth = 3
layer.lineJoin = .bevel
return layer
As each layer is returned, I am then adding it to a bigger layer (shapeLayer
) which then gets added to a UIView
layer. (Hopefully that makes sense).
The trouble I am having is centering the sublayers (inside the finalShape) that got added while looping. They obviously have a specific path set using path.cgPath
and I don't know how I can achieve this whilst maintaining their position. I really (obviously) don't know enough about CALayers to solve this, any help would be greatly appreciated.
Thank you.
-- Might be more helpful if I show you the whole code
private let shapeLayer = CALayer()
func bind(shape: Shape) {
let data = NSKeyedUnarchiver.unarchiveObject(with: shape.strokeData)
guard let paths = data as? [UIBezierPath] else { return }
for path in paths {
let guideLine = drawPathLayer(for: path)
shapeLayer.addSublayer(guideLine)
}
layer.addSublayer(shapeLayer)
}
private func drawPathLayer(for path: UIBezierPath) -> CALayer {
let layer = CAShapeLayer()
layer.path = path.cgPath
layer.strokeColor = UIColor.gray.cgColor
layer.fillColor = UIColor.clear.cgColor
layer.lineWidth = 3
layer.lineJoin = .bevel
return layer
}
If you want to position layers (or views), the point in time at which you do this is crucial. For example, if you do this in viewDidLoad
, it is too early because the parent layer does not yet have its final size. Also, the size of the parent layer can change, so it is better to center the sublayers whenever the frame of the parent layer has changed. You can do this with a subclass of CALayer
and overwriting layoutSublayers
.
It'll look something like this (not a running example):
class CenteringLayer: CALayer {
override func layoutSublayers() {
super.layoutSublayers()
if let theSublayers = sublayers {
for s in theSublayers {
var theFrame = s.frame
theFrame.origin.x = (bounds.width - theFrame.width) / 2.0
theFrame.origin.y = (bounds.height - theFrame.height) / 2.0
s.frame = theFrame
}
}
}
}
Note: Your layer
has also to layout shapeLayer
by
shapeLayer.frame = layer.bounds
to make this work. You can do this by either creating another subclass of CALayer
for layer
and overriding layoutSublayers()
, or (if layer
is the layer of a view) overriding layoutSubviews()
in your view class. A third option might be overriding layoutSublayers(of:)
in the view class, because a UIView is always the delegate of its layer.
But these ways are a bit inelegant, though, so you'd better think about reducing the number of layers in your code. E. g. You should try to avoid shapeLayer
.
If all of the paths have the same properties, you might use only one CAShapeLayer
with the concatenated path, because layers might be very resource intensive.