0
votes

I'm implementing a custom pull-to-refresh component.

Create CALayer with a spinner animation, than add this layer to UICollectionView. Layer's speed is zero (self.loaderLayer.speed = 0.0f;) and layer animation is managed with timeOffset in scrollViewDidScroll:. Problem goes here, because I also want to show a loader always in the center of pulling space, so I change layer's position in scrollViewDidScroll: like this:

[self.loaderLayer setPosition:CGPointMake(CGRectGetMidX(scrollView.bounds), scrollView.contentOffset.y / 2)];

But nothing happens with layer position (calling [self.loaderLayer setNeedsDisplay] doesn't help). I understand that it's because zero speed. And currently I found the way which works (but I don't like that):

self.loaderLayer.speed = 1.0f;
[self.loaderLayer setPosition:CGPointMake(CGRectGetMidX(scrollView.bounds), scrollView.contentOffset.y / 2)];
self.loaderLayer.speed = 0.0f;

How could I change a position for a paused layer right? What am I missing?

1
Setting speed to 0.0f causes all animations for that layer to be paused; including positioning. There is probably a different way to implement your "spinner animation pause" than what you're doing, but it's hard to say what that is without seeing the code of how you construct your spinner layer.Ian MacDonald
Just use two different layers, one to have set zero speed and the other that you want to reposition. The one can contain the other.matt
Yeah, will wrap with second layer. I just think, that there is a correct way to force updating position regardless a pause state. ThanksAnton Gaenko
possible duplicate of How do you move a CALayer instantly (w/o animation) The layer won't move because by default setPosition is animated and you have animation speed set to zero. So move it without animation.David Berry
@David thanks for reference, it's exactly what I'm seeking :)Anton Gaenko

1 Answers

0
votes

All regards to @David for the reference. I just summarize it as an answer.

CoreAnimation works with two kinds of animations (transactions): explicit and implicit. When you see Animatable word in property documentation, it means that each time you set this property, CoreAnimation will animate this property changes implicitly with system default duration (default is 1/4 second). Under hood CALayer has actions for these properties and calling -actionForKey returns such action (implicit animation).

So in my case, when I change a layer position, CoreAnimation implicitly try animating this changes. Because layer is paused (speed is zero) and animation has default duration, we don't see this changes visually.

And answer is to disable implicit animations (disable calling layer -actionForKey). To do that we call [CATransaction setDisableActions:YES].

OR

We can mark this animation as immediate (by setting it's duration to zero) with calling [CATransaction setAnimationDuration:0.0];.

These flags/changes are per thread based and work for all transactions in specific thread until next run loop. So if we want to apply them for a concrete transaction, we wrap code with [CATransaction begin]; ... [CATransaction commit]; section.

In my case final code looks

[CATransaction begin];
[CATransaction setDisableActions:YES];
[self.loaderLayer setPosition:CGPointMake(CGRectGetMidX(scrollView.bounds), scrollView.contentOffset.y / 2)];
self.loaderLayer.transform = CATransform3DMakeScale(scaleFactor, scaleFactor, 1);
[CATransaction commit];

And it works perfectly!

Loader Demo