1
votes

I have a XUIView class as below. When I run animation, it's no effect for folding.

Who can explain me ?

class ViewController: UIViewController {

    // Width constraint of XUIView instance
    @IBOutlet weak var vwWrapperWidth: NSLayoutConstraint! {
        didSet{
            self.vwWrapperWidth.constant = UIScreen.main.bounds.width
        }
    }

    @IBAction func btnToggleTouchUp(_ sender: UIButton) {
        if(self.vwWrapperWidth.constant == 55) {
            // animation effect is OK when expanding
            self.vwWrapperWidth.constant = UIScreen.main.bounds.width

            UIView.animate(withDuration: 0.5, animations: {
                self.view.layoutIfNeeded()
            })
        }
        else {
            // animation effect is not OK when folding
            self.vwWrapperWidth.constant = 55
            UIView.animate(withDuration: 0.5, animations: {
                self.view.layoutIfNeeded()
            })
        }
    }
    //.....
}

@IBDesignable
class XUIView: UIView {

    @IBInspectable
    var roundTopLeftCorner: Bool = false

    @IBInspectable
    var roundBottomLeftCorner: Bool = false

    @IBInspectable
    var roundTopRightCorner: Bool = false

    @IBInspectable
    var roundBottomRightCorner: Bool = false

    @IBInspectable
    var cornerRadius: CGFloat = 0.0

    @IBInspectable
    var borderWidth: CGFloat = 0.0

    @IBInspectable
    var borderColor: UIColor?

    fileprivate var borderLayer: CAShapeLayer? {
        didSet{
            self.layer.addSublayer(self.borderLayer!)
        }
    }

    func roundCorners(_ corners: UIRectCorner) {
        if(self.borderLayer == nil) { self.borderLayer = CAShapeLayer() }

        let bounds = self.bounds

        let maskPath = UIBezierPath(roundedRect: bounds, byRoundingCorners: corners, cornerRadii: CGSize(width: self.cornerRadius, height: self.cornerRadius))

        let maskLayer = CAShapeLayer()
        maskLayer.frame = bounds
        maskLayer.path = maskPath.cgPath

        self.layer.mask = maskLayer

        self.borderLayer?.frame = bounds
        self.borderLayer?.path = maskPath.cgPath
        self.borderLayer?.strokeColor = self.borderColor?.cgColor
        self.borderLayer?.lineWidth = self.borderWidth
        self.borderLayer?.fillColor = nil

    }

    override func layoutSubviews() {
        super.layoutSubviews()
        var roundedCorners: UIRectCorner = []
        if(roundTopLeftCorner) { roundedCorners.insert(.topLeft) }
        if(roundTopRightCorner) { roundedCorners.insert(.topRight) }
        if(roundBottomLeftCorner) { roundedCorners.insert(.bottomLeft) }
        if(roundBottomRightCorner) { roundedCorners.insert(.bottomRight) }
        roundCorners(roundedCorners)
    }


}

source code : http://www.mediafire.com/file/n6svp1mk44fc0uf/TestXUIView.zip/file

2
Try removing didSet in vwWrapperWidthelbert rivas
@elbertrivas, didSet is called one time when initializing so it's not reason.alphaplus

2 Answers

2
votes

You are experiencing no animation on the "folding" animation because CALayer's path property is not implicitly animatable. It is mentioned here.

Unlike most animatable properties, path (as with all CGPath animatable properties) does not support implicit animation.

To make matters worst, CALayer's frame property is not implicitly animatable either. It is mentioned here

Note: The frame property is not directly animatable. Instead you should animate the appropriate combination of the bounds, anchorPoint and position properties to achieve the desired result.

This simply means the following lines won't work, just because it is being called inside a UIView.animate block.

let maskLayer = CAShapeLayer()
maskLayer.frame = bounds // This line won't animate!
maskLayer.path = maskPath.cgPath // This line won't animate!

self.layer.mask = maskLayer

Since it is not being animated, the mask is applied as soon as the layoutSubviews method in XUIView is called. View is actually animating, but you just cannot see it because the mask is being set before the animation completes.

This is also what happens in the expand animation. First the mask expands, then the animation occurs.

To prevent this, you might need to find a way to animate the width constraint and the layer frames & mask paths together.

1
votes

The problem is that you are creating a CAShapeLayer in layoutSubviews which means that every time an animation occurs it gets call.

Try commenting line 91 and you are going to get what you want.