如何使CALayer及其所有子图层按比例缩放?

时间:2014-09-08 17:02:53

标签: swift core-animation calayer zooming nsscrollview

我正在尝试创建一个显示文档页面的NSScrollView。我添加了一个支持CALayer的NSView作为NSScrollView的documentView,然后我在documentView中添加了一个CALayer子图层。当我缩放NSScollview时,documentView正确放大和缩小。但是,documentView的子图层不会按其包含的documentView按比例缩放。如果我没有在documentView图层的子图层上设置autoresizingMask,则子图层会在缩放documentView时从屏幕上飞出。如果我使用LayerWidthSizable / LayerHeightSizable选项,子图层会比引用documentView超级图层时更大或更小。这是我到目前为止的代码:

这是NSScrollview:

class LayerScrollView: NSScrollView {

    var containerLayer: ContainerLayer!

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

这是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)
    }
}

最后,这是DocumentLayer(DocumentLayer中包含的子层):

class DocumentLayer: CALayer {

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

这是一张图片来说明问题和我想要的结果:

蓝色矩形是ContainerLayer,红色矩形是DocumentLayer。

我查看了大量的教程和文档,然后空了。看起来这应该是非常容易的。我在这里缺少什么?

1 个答案:

答案 0 :(得分:1)

更新:已解决此问题

以下是解决方案,经过更长时间的挖掘文档和其他内容后发现(同样,感谢@mahaltertin建议我使用borderWidth来帮助调试):

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")
    }
}

和DocumentLayer:

class DocumentLayer: CALayer {

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

解决方案是使用约束。 DocumentLayer中的actionForKey方法的覆盖是为了防止DocumentLayer在缩放时进行动画处理。 ContainerLayer中定义的约束指定DocumentLayer应该是ContainerLayer的宽度/高度的一半,并且两个层的中间应该相同。缩放现在可以保持比例正确。