83

I'm trying to drag a CALayer in an iOS app.

As soon as I change its position property it tries to animate to the new position and flickers all over the place:

 layer.position = CGPointMake(x, y)

How can I move CALayers instantly? I can't seem to get my head around the Core Animation API.

Manoj Rlogical
  • 239
  • 1
  • 4
Mel
  • 2,269
  • 1
  • 18
  • 18
  • **regarding this extremely old QA, the critical issue today is this:** https://stackoverflow.com/a/56980329/294884 – Fattie Jan 22 '20 at 16:30

5 Answers5

174

You want to wrap your call in the following:

[CATransaction begin]; 
[CATransaction setValue: (id) kCFBooleanTrue forKey: kCATransactionDisableActions];
layer.position = CGPointMake(x, y);
[CATransaction commit];
David Duponchel
  • 3,959
  • 3
  • 28
  • 36
Ben Gottlieb
  • 85,404
  • 22
  • 176
  • 172
38

Swift 3 Extension :

extension CALayer {
    class func performWithoutAnimation(_ actionsWithoutAnimation: () -> Void){
        CATransaction.begin()
        CATransaction.setValue(true, forKey: kCATransactionDisableActions)
        actionsWithoutAnimation()
        CATransaction.commit()
    }
}

Usage :

CALayer.performWithoutAnimation(){
    someLayer.position = newPosition
}
CryingHippo
  • 5,026
  • 1
  • 28
  • 32
  • 1
    It's useful to add the `@noescape` attribute to the block to allow (and make it clear) that `self` is not needed in the block – Andrew Aug 03 '16 at 13:14
23

You can also use the convenience function

[CATransaction setDisableActions:YES] 

as well.

Note: Be sure to read the comments by Yogev Shelly to understand any gotchas that could occur.

Yogurt
  • 2,913
  • 2
  • 32
  • 63
  • 5
    Important, this effects all CALayer so you want to reenable actions after you're done, i.e [CATransaction setDisableActions:YES] – Yogev Shelly Jan 25 '12 at 14:19
  • Damn I should have said that. Good save. The command disables animations until it is set to NO or until the core graphics animation engine has completed a run cycle. Not sure if that's the right word for it :/ But Thank you for clearing that up for everybody. – Yogurt Feb 14 '12 at 03:28
15

As others have suggested, you can use CATransaction.
The problem comes arises because CALayer has a default implicit animation duration of 0.25 seconds.

Thus, an easier (in my opinion) alternative to setDisableActions is to use setAnimationDuration with a value of 0.0.

[CATransaction begin];
[CATransaction setAnimationDuration:0.0];
layer.position = CGPointMake(x, y);
[CATransaction commit];
So Over It
  • 3,668
  • 3
  • 35
  • 46
  • Easy to remember, easy to understand when you're re-reading the code, and easier to type. Thanks! – Mr. T May 11 '13 at 07:24
5

Combining previous answers here for Swift 4, to clearly make the animation duration explicit...

extension CALayer
{
    class func perform(withDuration duration: Double, actions: () -> Void) {
        CATransaction.begin()
        CATransaction.setAnimationDuration(duration)
        actions()
        CATransaction.commit()
    }
}

Usage...

CALayer.perform(withDuration: 0.0) {
            aLayer.frame = aFrame
        }
Giles
  • 1,428
  • 11
  • 21