How can I make CALayer and all its Zoom sublayers proportional?

I am trying to create an NSScrollView that displays document pages. I added a CALayer controlled NSView as the NSScrollView documentView and then added a CALayer sub-layer to the documentView. When I scale the NSScollview, the DocumentView scales and downscales correctly. However, documentView sublayers do not scale in proportion to their documentView content. If I don't set autoresizingMask on the sublayers of the documentView layer, the sublayers just fly off the screen when the documentView is zoomed in. If I use the LayerWidthSizable / LayerHeightSizable parameters, the sublayers get more or less than they should be relative to the documentView superword. Here is the code I have so far:

Here's an example NSScrollview:

class LayerScrollView: NSScrollView {

    var containerLayer: ContainerLayer!

    override func awakeFromNib() {
        documentView = ContainerLayer(frame: frame)
    }
}

      

Here is the ContainerLayer (documentView CALayer):

class ContainerLayer: NSView {

    let documentLayer: DocumentLayer = DocumentLayer()

    override init(frame frameRect: NSRect) {
        super.init(frame: frameRect)
        autoresizesSubviews = true
        wantsLayer = true
        layer = CATiledLayer()
        layer?.delegate = self
        layer?.backgroundColor = NSColor.blueColor().CGColor
        layer?.masksToBounds = true
        documentLayer.frame = CGRect(x: frame.width / 4.0, y: frame.height / 4.0, width: frame.width / 2.0, height: frame.height / 2.0)
        documentLayer.delegate = documentLayer
        layer?.addSublayer(documentLayer)
        documentLayer.autoresizingMask = CAAutoresizingMask.LayerWidthSizable | CAAutoresizingMask.LayerHeightSizable
        documentLayer.setNeedsDisplay()
    }

    required init(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    override func drawLayer(layer: CALayer!, inContext ctx: CGContext!) {
        CGContextSetFillColorWithColor(ctx, NSColor.redColor().CGColor)
        CGContextFillRect(ctx, layer.bounds)
    }
}

      

And finally, here is the DocumentLayer (sublayers contained in the DocumentLayer):

class DocumentLayer: CALayer {

    override func drawLayer(layer: CALayer!, inContext ctx: CGContext!) {
        CGContextSetFillColorWithColor(ctx, NSColor.redColor().CGColor)
        CGContextFillRect(ctx, layer.bounds)
    }
}

      

Here is an image illustrating the problem and my desired results:

The blue rectangle is the ContainerLayer, the red rectangle is the DocumentLayer.

I went through numerous tutorials and docs and came up with an empty one. It looks like it should be very simple. What am I missing here?

+3


source to share


1 answer


UPDATE: SOLVABLE IT

Here is a solution, found after hours of working through the documentation and other stuff (also thanks to @mahaltertin for suggesting I use borderWidth for debugging):

LayerScrollView:

class LayerScrollView: NSScrollView {

    var containerLayer: ContainerLayer!

    override func awakeFromNib() {
        containerLayer = ContainerLayer(frame: frame)
        documentView = containerLayer
    }
}

      

ContainerLayer:



class ContainerLayer: NSView {

    let documentLayer: DocumentLayer = DocumentLayer()

    override init(frame frameRect: NSRect) {
        super.init(frame: frameRect)
        wantsLayer = true
        layer = CALayer()
        layer?.layoutManager = CAConstraintLayoutManager.layoutManager()
        layer?.delegate = self
        layer?.backgroundColor = NSColor.blueColor().CGColor

        documentLayer.delegate = documentLayer
        documentLayer.name = "documentLayer"
        documentLayer.borderWidth = 1.0
        documentLayer.backgroundColor = NSColor.redColor().CGColor

        documentLayer.addConstraint(CAConstraint(attribute: CAConstraintAttribute.Width, relativeTo: "superlayer", attribute: CAConstraintAttribute.Width, scale: 0.5, offset: 0.0))
        documentLayer.addConstraint(CAConstraint(attribute: CAConstraintAttribute.Height, relativeTo: "superlayer", attribute: CAConstraintAttribute.Height, scale: 0.5, offset: 0.0))
        documentLayer.addConstraint(CAConstraint(attribute: CAConstraintAttribute.MidX, relativeTo: "superlayer", attribute: CAConstraintAttribute.MidX, scale: 1.0, offset: 0.0))
        documentLayer.addConstraint(CAConstraint(attribute: CAConstraintAttribute.MidY, relativeTo: "superlayer", attribute: CAConstraintAttribute.MidY, scale: 1.0, offset: 0.0))

        layer?.addSublayer(documentLayer)

        layer?.setNeedsDisplay()
        documentLayer.setNeedsDisplay()
    }

    required init(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}

      

And the DocumentLayer:

class DocumentLayer: CALayer {

    override func actionForKey(event: String!) -> CAAction! {
        return nil
    }
}

      

The solution is to use constraints. Overriding the actionForKey method in the DocumentLayer is to prevent the DocumentLayer from animating when scaling. The constraints defined in the ContainerLayer dictate that the DocumentLayer should be half the width / height of the ContainerLayer and that the middle of both layers should be the same. Scaling now maintains correct proportions.

+1


source







All Articles