17

I am having some performance issues with some code that I have written for resizing a CALayer using touch. It works fine but the animation is far from snappy enough and lags behind the touch location.

CGPoint startPoint;
CALayer *select;

- (CGRect)rectPoint:(CGPoint)p1 toPoint:(CGPoint)p2 {
     CGFloat x, y, w, h;
     if (p1.x < p2.x) {
         x = p1.x;
         w = p2.x - p1.x;
     } else {
         x = p2.x;
         w = p1.x - p2.x;
     }
     if (p1.y < p2.y) {
         y = p1.y;
         h = p2.y - p1.y;
     } else {
         y = p2.y;
         h = p1.y - p2.y;
     }
     return CGRectMake(x, y, w, h);
 }

 - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
     UITouch *t1 = [[[event allTouches]allObjects]objectAtIndex:0];
     CGPoint loc = [t1 locationInView:self];
     startPoint = loc;
     lastPoint = CGPointMake(loc.x + 5, loc.y + 5);

     select = [CALayer layer];
     select.backgroundColor = [[UIColor blackColor]CGColor];
     select.frame = CGRectMake(startPoint.x, startPoint.y, 5, 5);
     [self.layer addSublayer:select];
 }

 - (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
     UITouch *t1 = [[[event allTouches]allObjects]objectAtIndex:0];
     CGPoint loc = [t1 locationInView:self];
     select.bounds = [self rectPoint:startPoint toPoint:loc]; 
 }

Is there a better way of achieving this?

tikhop
  • 2,012
  • 14
  • 32
EcksMedia
  • 461
  • 2
  • 5
  • 20

2 Answers2

48

The lag is because you are changing the bounds property of the layer, which is an animatable property.

With CALayers (the CA stands for core animation...) any change to an animatable property will be animated by default. This is called implicit animation. The default animation takes 0.25 seconds, so if you are updating it frequently, say during processing of touches, this will add up and cause a visible lag.

To prevent this, you must declare an animation transaction, turn off implicit animations, then change the properties:

[CATransaction begin];
[CATransaction setDisableActions:YES];
layer.bounds = whatever;
[CATransaction commit];
jrturton
  • 118,105
  • 32
  • 252
  • 268
18

Accepted answer in Swift 3/4:

CATransaction.begin()
CATransaction.setDisableActions(true)
layer.bounds = whatever
CATransaction.commit()

Worth mentioning that this also applies to .frame properties, for example when you're resizing an AVPlayerLayer:

override func layoutSubviews() {
    CATransaction.begin()
    CATransaction.setDisableActions(true)
    playerLayer.frame = self.bounds
    CATransaction.commit()
}
brandonscript
  • 68,675
  • 32
  • 163
  • 220