Search code examples
iosswift3calayercabasicanimation

CATransform3DMakeScale not transforming layer width as expected


I'm trying to get a transform animation to work by having the width of a CALayer increase in 3 increments from a width of 1 to the width of the entire view. If my understanding is correct, each transformation is being applied to the original layer size. If the width is 1 and I'm scaling the transformation by self.view.frame.size.width I expect the animation layer to take up the entire view width, but instead its stopping half way. Why is that?

let progressBar1 = CALayer()
var transform1 = CATransform3DMakeScale(1, 1, 1)
var transform2 = CATransform3DMakeScale(1, 1, 1)
var transform3 = CATransform3DMakeScale(1, 1, 1)

override func viewDidLoad() {
    super.viewDidLoad()
    setupAnimationTransforms()
}

override func viewDidAppear(_ animated: Bool) {

    buildBar()

}

func setupAnimationTransforms(){

    transform1 = CATransform3DMakeScale(20, 1, 1)
    transform2 = CATransform3DMakeScale(40, 1, 1)
    transform3 = CATransform3DMakeScale(self.view.frame.size.width, 1, 1)

}


func buildBar(){        

    progressBar1.bounds = CGRect(x: 0, y: 0, width: 1, height: 5)
    progressBar1.position = CGPoint(x: 0, y: self.view.frame.size.height)

    progressBar1.backgroundColor = UIColor.red.cgColor
    view.layer.addSublayer(progressBar1)
    animate1()

}

func animate1(){

    CATransaction.begin()
    CATransaction.setCompletionBlock {

        self.animate2()
    }

    let anim = CABasicAnimation(keyPath: "transform")
    anim.fromValue = progressBar1.transform
    anim.toValue = transform1
    anim.duration = 1.00
    progressBar1.add(anim, forKey: "transform")
    CATransaction.setDisableActions(true)
    self.progressBar1.transform = transform1
    CATransaction.commit()

}

func animate2(){

    CATransaction.begin()
    CATransaction.setCompletionBlock {

        self.animate3()

    }

    let anim = CABasicAnimation(keyPath: "transform")
    anim.fromValue = transform1
    anim.toValue = transform2
    anim.duration = 1.00
    progressBar1.add(anim, forKey: "transform")
    CATransaction.setDisableActions(true)
    progressBar1.transform = transform2
    CATransaction.commit()
}

func animate3(){

    CATransaction.begin()
    CATransaction.setCompletionBlock {

        print("DONE")

    }

    let anim = CABasicAnimation(keyPath: "transform")
    anim.fromValue = transform2
    anim.toValue = transform3
    anim.duration = 1.00
    progressBar1.add(anim, forKey: "transform")
    CATransaction.setDisableActions(true)
    progressBar1.transform = transform3
    CATransaction.commit()

}

Solution

  • There is no need to use a transform here; you can simply animate the width.

    Also there is a constant CATransform3DIdentity so you don't need CATransform3DMakeScale(1, 1, 1).

    As for your original question, I believe the answer is that you are scaling about the default anchor point which is 0.5, 2.5 so almost half of your scaled up bar is off screen to the left (or more precisely half minus 0.5 points is offscreen to the left). You also need a translate here or you need to move the anchor point if you really ant to use a scale. Animating the width is a much easier solution since there is no need to mess with the anchor or concatenate transforms.