0

I'm a noobie when it comes to Core Animation. I'm trying to implement simple animation where UILabel moves from point A to point B. I have the following code that I got from an animation code example but I can't get this to work. The label simply doesnt move. What am I doing wrong?

let frame = self.view.frame
let blueBox = UIView(frame: frame)
blueBox.backgroundColor = UIColor(red:  46/255, green: 83/255, blue: 160/255, alpha: 1.0)

let label = UILabel(frame: CGRect(x: 0, y: 0, width: 300, height: 400))
label.center = CGPoint(x: frame.size.width/2, y: frame.size.height/2)
label.textAlignment = .center
label.lineBreakMode = .byWordWrapping
label.numberOfLines = 0

label.layer.position = label.center

var attrsA = [NSFontAttributeName: UIFont(name: "LemonMilk", size: 92), NSForegroundColorAttributeName: UIColor.white]
var a = NSMutableAttributedString(string:"Hello\n", attributes:attrsA)
var attrsB =  [NSFontAttributeName: UIFont(name: "LemonMilk", size: 38), NSForegroundColorAttributeName: UIColor.white]
var b = NSAttributedString(string:"World", attributes:attrsB)
a.append(b)


label.attributedText = a

let theAnimation = CABasicAnimation(keyPath: "position");
theAnimation.fromValue = [NSValue(cgPoint: CGPoint(x: screenWidth/2, y: screenHeight/2))]
theAnimation.toValue = [NSValue(cgPoint: CGPoint(x: 100.0, y: 100.0))]
theAnimation.duration = 3.0;
theAnimation.autoreverses = false //true - reverses into the initial value either smoothly or not
theAnimation.repeatCount = 2

blueBox.addSubview(label)

view.addSubview(blueBox)
label.layer.add(theAnimation, forKey: "animatePosition");
user594883
  • 1,261
  • 2
  • 14
  • 34
  • 2
    It might be a lot easier for you to just use `UIView.animate(withDuration:)` – Pierce Feb 03 '17 at 17:08
  • @Pierce but that doesn't answer the question – matt Feb 03 '17 at 17:10
  • @matt - I know that's why I didn't answer with that. I was just making a recommendation in the comments – Pierce Feb 03 '17 at 17:11
  • @matt - What is your problem? Get off your high-horse and go read my comment again. I never said "don't do that", I simply made a recommendation of a way that might be easier for them to accomplish their goal. He said he was a newbie. I didn't give him an answer which didn't solve his problem, and I never told him he couldn't do what he was trying. All I said "it MIGHT be easier" to try an alternative. – Pierce Feb 03 '17 at 17:23
  • @matt - your comment implies that it's unreasonable to suggest, and seek out alternatives - which is intrinsically wrong. – Pierce Feb 03 '17 at 17:25
  • @Pierce We disagree. I see a lot of "don't do that" on OP when people are doing perfectly reasonable things or are constrained to do it using specific tools or techniques. To me, the "don't do that" response is what's instrinsically wrong. Core Animation is _good_. The OP should be _encouraged_ to use it and needs to figure out how. – matt Feb 03 '17 at 17:26
  • @matt - I apologize for seeming dismissive and argumentative. While that might be the intentions of some people here, I was simply recommending an alternative. I specifically didn't provide an answer that didn't pertain the to original question. I appreciate your input here, and value your vast experience. I apologize if I came across as hostile. – Pierce Feb 03 '17 at 17:29
  • I appreciate the energy my question arroused. Thank you @ Matt and @Pierce for your enthusiastic attitude! Many times nowbie questions go unnoticed. – user594883 Feb 03 '17 at 19:47

1 Answers1

2

First: You cannot call addSubview on label and add(animation:) on label.layer in the same breath. You can only animate a view that is already in the view hierarchy. In other words, even if everything about your code were fine, you are calling add(animation:) too soon. Try introducing a delay.

Second: These lines are bogus:

theAnimation.fromValue = [NSValue(cgPoint: CGPoint(x: screenWidth/2, y: screenHeight/2))]
theAnimation.toValue = [NSValue(cgPoint: CGPoint(x: 100.0, y: 100.0))]

Neither the fromValue nor to toValue can be an array. Get rid of those braces. And in Swift 3.0.1 and later, you don't need to coerce to NSValue either. So:

theAnimation.fromValue = CGPoint(x: screenWidth/2, y: screenHeight/2)
theAnimation.toValue = CGPoint(x: 100.0, y: 100.0)

Third: What is the fromValue even for? If you want to animate from where the label already is, simply omit the fromValue.

Thus, I modified your code to end like this, and I saw animation:

label.attributedText = a
blueBox.addSubview(label)
view.addSubview(blueBox)
delay(1) {
    let theAnimation = CABasicAnimation(keyPath: "position");
    theAnimation.toValue = CGPoint(x: 100.0, y: 100.0)
    theAnimation.duration = 3.0;
    theAnimation.autoreverses = false //true - reverses into the initial value either smoothly or not
    theAnimation.repeatCount = 2
    label.layer.add(theAnimation, forKey: "animatePosition");
}
Community
  • 1
  • 1
matt
  • 447,615
  • 74
  • 748
  • 977
  • The animation still won't do what you want, I think, but at least with those suggestions you should _see_ something happen. If not, please let me know. – matt Feb 03 '17 at 17:18
  • The delay fixed the problem for me but there has to be a better way. This just seems like an ugly hack. Is there an event or something that I can listen to that says "everything's set up, you can add animations now"? What exactly am I waiting for? – Indiana Kernick Dec 28 '19 at 03:17
  • Hi @Kerndog73 - You're waiting for the runloop to complete. A much shorter delay (0.1) should work just as well, I think. Or you could try just flushing the current transaction instead; that tells the layout engine to get everything into place before proceeding, and is a technique I sometimes use. – matt Dec 28 '19 at 03:24
  • Thanks for the quick reply. How do I do that? – Indiana Kernick Dec 28 '19 at 03:26
  • I think you'd say `CATransaction.flush()` but don't quote me. – matt Dec 28 '19 at 03:26
  • That didn't work. I added the sublayer, then flushed, then added the animation. Any other suggestions or things I could research? – Indiana Kernick Dec 28 '19 at 03:29
  • The simplest solution is probably the way to go though. If it works, it works – Indiana Kernick Dec 28 '19 at 03:31
  • @Kerndog73 I'd need to see your actual code. Maybe you could post a separate question. – matt Dec 28 '19 at 03:34