简体   繁体   中英

How to center CALayers inside parent CALayer

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM