Search code examples
iosswiftcalayercashapelayercabasicanimation

CAShapeLayer is appearing before Views


I have two views between which is located resizable line (view with CAShapeLayer). The problem that I'm facing with is that despite the animation on layer, it is appearing faster than views are changing. I know that animation of layer is completely different, but can't understand where is my fault. Please tell me if I made something wrong.

Here is the code of my layer's animation (used "strokeStart" as well, but it didn't worked):

override func layoutSubviews() {
    super.layoutSubviews()
    let centerX = bounds.size.width / 2
    let path = CGMutablePath()
    path.addLines(between: [CGPoint(x: centerX, y: 0), CGPoint(x: centerX, y: frame.height)])
    dashedLayer.path = path
    dashedLayer.frame = bounds

    let animation = CABasicAnimation(keyPath: "strokeEnd")
    animation.fromValue = height
    animation.toValue = bounds.size.height
    height = bounds.size.height
    animation.duration = 0.3
    dashedLayer.add(animation, forKey: nil)
}

Code of views' animation (because of modifying views, I animate layoutIfNeeded):

UIView.animate(withDuration: 0.3, animations: {
    self.view.layoutIfNeeded()
    ...

Solution

  • I think one easy way to deal with this is to animate the constraint constant like this:

      @IBOutlet weak  var heightConstraint: NSLayoutConstraint!
    
      UIView.animate(withDuration: 3, animations: {
    
            self. heightConstaint.constant = 300 // This constant is the height of top big view. 
    
            self.view.layoutIfNeeded()}
    

    There is another view where the CAShapeLayer lies in. I call it line View . Its height should be constrained as same as the height of the top big view.

    In the line view, you can add animations. This should be similar to your implementation.

       override func didMoveToSuperview() {
         super.didMoveToSuperview()
         dashedLayer.backgroundColor = UIColor.clear.cgColor
         layer.addSublayer(dashedLayer)
         height = bounds.size.height
       }
    
      override func layoutSubviews() {
        super.layoutSubviews()
        let centerX = bounds.size.width / 2
        let path = CGMutablePath()
        path.addLines(between: [CGPoint(x: centerX, y: 0), CGPoint(x: centerX, y: frame.height)])
        dashedLayer.path = path
        dashedLayer.lineWidth = 3.0
        dashedLayer.strokeColor = UIColor.blue.cgColor
    
        let animation = CABasicAnimation(keyPath: "strokeEnd")
        animation.fromValue = height / bounds.size.height
        animation.toValue = 1.0 
        animation.timingFunction = CAMediaTimingFunction.init(name: CAMediaTimingFunctionName.easeInEaseOut)
        height = bounds.size.height
        animation.duration = 3.0
        dashedLayer.add(animation, forKey: nil)
    }
    

    It's expanding here. You may adapt the code to contract by yourself.

    enter image description here

    The following is with the reversing animation.

    override func didMoveToSuperview() {
        super.didMoveToSuperview()
        dashedLayer.backgroundColor = UIColor.clear.cgColor
        layer.addSublayer(dashedLayer)
        height = bounds.size.height
          clipsToBounds = true
    }
    
    
    override func layoutSubviews() {
        super.layoutSubviews()
        let centerX = bounds.size.width / 2
        let path = CGMutablePath()
        path.addLines(between: [CGPoint(x: centerX, y: 0), CGPoint(x: centerX, y: max(height , bounds.size.height) )])
        dashedLayer.path = path
        dashedLayer.lineWidth = 3.0
        dashedLayer.strokeColor = UIColor.blue.cgColor
    
        let animation = CABasicAnimation(keyPath: "strokeEnd")
    
        if (height < bounds.size.height){
        animation.fromValue = height / bounds.size.height
            animation.toValue = 1.0}
    
        else {
            animation.fromValue = 1.0
            animation.toValue = bounds.size.height / height}
    
    
        animation.timingFunction = CAMediaTimingFunction.init(name: CAMediaTimingFunctionName.easeInEaseOut)
        height = bounds.size.height
        animation.duration = 3
        dashedLayer.add(animation, forKey: nil)
    }
    

    enter image description here