动画边界更改时具有 CALayer 有线效果的自定义视图

Posted

技术标签:

【中文标题】动画边界更改时具有 CALayer 有线效果的自定义视图【英文标题】:Custom view with CALayer wired effect when animating bounds change 【发布时间】:2016-03-09 03:45:45 【问题描述】:

我已经实现了一个自定义视图,其中添加了CALayer 作为UIView 的子层。当我使用以下内容为视图设置动画时:UIView.animateWithDuration(2.0) self.slider.bounds.size *= 2.0,缩放动画有点错误。 CALayer 以缩放大小从错误位置开始并移动到最终位置,而不是随视图缩放。

CustomeView 代码:

import UIKit

class GridMaskView: UIView 
    private let cornerLayer: CAShapeLayer
    private let borderLayer: CAShapeLayer
    private let gridLayer: CAShapeLayer

    private let gridSize: (horizontal: UInt, vertical: UInt) = (3, 3)
    private let cornerThickness: CGFloat = 3.0
    private let cornerLength: CGFloat = 20.0
    private let borderThickness: CGFloat = 2.0
    private let gridThickness: CGFloat = 1.0
    private let lineColor: UIColor = UIColor(r: 120, g: 179, b: 193, a: 1)

    var showGridLines: Bool = true 
        didSet 
            gridLayer.hidden = !showGridLines
        
    

    required init?(coder aDecoder: NSCoder) 
        fatalError("init(coder:) has not been implemented")
    

    override init(frame: CGRect) 
        cornerLayer = CAShapeLayer()
        cornerLayer.fillColor = lineColor.CGColor

        borderLayer = CAShapeLayer()
        borderLayer.fillColor = UIColor.clearColor().CGColor
        borderLayer.strokeColor = lineColor.CGColor
        borderLayer.lineWidth = borderThickness

        gridLayer = CAShapeLayer()
        gridLayer.strokeColor = lineColor.CGColor
        gridLayer.lineWidth = gridThickness

        super.init(frame: frame)
        layer.addSublayer(cornerLayer)
        layer.addSublayer(borderLayer)
        layer.addSublayer(gridLayer)
    

    override func layoutSubviews() 
        super.layoutSubviews()
        layoutLayers()
    

    private func layoutLayers() 
        drawCorner()
        drawBorder()
        drawGrid()
    

    private func drawCorner() 
        cornerLayer.frame = bounds.insetBy(dx: -cornerThickness, dy: -cornerThickness)
        cornerLayer.path = cornerPath(forBounds: cornerLayer.bounds)
    

    private func cornerPath(forBounds bounds: CGRect) -> CGPathRef 
        let horizontalSize = CGSize(width: cornerLength, height: cornerThickness)
        let verticalSize = CGSize(width: cornerThickness, height: cornerLength)

        let corners: [(CGRectEdge, CGRectEdge)] = [(.MinXEdge, .MinYEdge), (.MinXEdge, .MaxYEdge), (.MaxXEdge, .MinYEdge), (.MaxXEdge, .MaxYEdge)]

        var cornerRects = [CGRect]()
        for corner in corners 
            cornerRects.append(bounds.align(horizontalSize, corner: corner.0, corner.1))
            cornerRects.append(bounds.align(verticalSize, corner: corner.0, corner.1))
        

        let cornerPath = CGPathCreateMutable()
        CGPathAddRects(cornerPath, nil, cornerRects, cornerRects.count)

        return cornerPath
    

    private func drawBorder() 
        borderLayer.frame = bounds
        borderLayer.path = borderPath(forBounds: borderLayer.bounds)
    

    private func borderPath(forBounds bounds: CGRect) -> CGPathRef 
        let borderPath = CGPathCreateMutable()
        let borderCornerPoints = [bounds.topLeft, bounds.topRight, bounds.bottomRight, bounds.bottomLeft, bounds.topLeft]
        CGPathAddLines(borderPath, nil, borderCornerPoints, borderCornerPoints.count)

        return borderPath
    

    private func drawGrid() 
        gridLayer.frame = bounds
        gridLayer.path = gridPath(forBounds: gridLayer.bounds)
    

    private func gridPath(forBounds bounds: CGRect) -> CGPathRef 
        let stepSize = bounds.size / (CGFloat(gridSize.horizontal), CGFloat(gridSize.vertical))

        let gridPath = CGPathCreateMutable()
        for i in (1...gridSize.vertical) 
            let x = CGFloat(i) * stepSize.width
            CGPathMoveToPoint(gridPath, nil, x, 0)
            CGPathAddLineToPoint(gridPath, nil, x, bounds.size.height)
        

        for i in (1...gridSize.horizontal) 
            let y = CGFloat(i) * stepSize.height
            CGPathMoveToPoint(gridPath, nil, 0, y)
            CGPathAddLineToPoint(gridPath, nil, bounds.size.width, y)
        
        return gridPath
    

    override func intrinsicContentSize() -> CGSize 
        return CGSize(width: cornerLength * 2, height: cornerLength * 2)
    

有人知道怎么装吗?

【问题讨论】:

【参考方案1】:

问题在于,当您查看动画时,您不会获得任何子图层的自动动画。您最好使用原始 UIView 的子视图,因为视图动画会根据其自动布局约束将其与原始视图一起制作动画。

【讨论】:

我正在实现一个自定义网格视图。似乎对每一行都使用 UIView 似乎有点奇怪。还有其他方法吗? 你已经隐瞒了你实际在做什么以及问题究竟是什么。您已经展示了五行完全没有信息的代码。你想要更好的帮助吗?显露,不隐瞒。更好的信息,更好的问题,更好的答案。现在你很幸运有人和你说话。 好吧,我坚持我所说的。您可以在单个视图的主层中进行所有绘制,使其成为另一个视图的子视图,并获得自动子视图动画的好处。否则,当您为视图设置动画时,您将不得不自己为所有这些图层设置动画。 所以我创建了一个自定义 CALayer 类并创建了一个 UIView 并将其 layerClass 覆盖到我的 CALayer 类中? 是的,或者甚至只是使用普通的 UIView 并直接在其中绘制。

以上是关于动画边界更改时具有 CALayer 有线效果的自定义视图的主要内容,如果未能解决你的问题,请参考以下文章

如何在更改其边界的动画期间强制重绘 CALayer

如何为 CALayer 的角半径设置动画

在 NSView 边界更改中托管的 CALayer

CALayer 不透明度动画有效,但边界动画无效

如何在动画时更改 CALayer 的颜色

禁用 CALayer 蒙版帧更改动画