Reputation: 3809
I have a scrollView with paging enabled and a number N of pages, which are UIViews as subviews of the scrollView.
I'm trying to do the following:
User scrolls to page number n. At that point, 7 CALayers which were previously added to page number n (that is, to page [[scrollView subviews] objectAtIndex:n-1].layer subLayers]) fade in, one after the other.
But I can't figure out how to make the CALayers fadeIn sequentially.So far, I've tried the following 3 approaches from my controller's delegate method: (assume I have an array to the layers and that their opacity was set to 0 upon creation)
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
{
int pageNumber = floor(self.scrollView.contentOffset.x / self.scrollView.frame.size.width);
if(pageNumber == (n-1))
{
int timeOffset = 0;
[CATransaction begin];
for(CALayer *layer in layerArray)
{
CABasicAnimation *a = [CABasicAnimation animationWithKeyPath:@"opacity"];
a.duration = 6;
a.beginTime = timeOffset++;
a.fromValue = [NSNumber numberWithFloat:0.];
a.toValue = [NSNumber numberWithFloat:1.];
[layer addAnimation:a forKey:nil];
}
[CATransaction commit];
}
}
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
{
int pageNumber = floor(self.scrollView.contentOffset.x / self.scrollView.frame.size.width);
if(pageNumber == (n-1))
{
int timeOffset = 0;
[CATransaction begin];
for(CALayer *layer in layerArray)
{
CABasicAnimation *a = [CABasicAnimation animation];
a.duration = 6;
a.beginTime = timeOffset++;
[layer addAnimation:a forKey:@"opacity"];
[layer setOpacity:1];
}
[CATransaction commit];
}
}
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
{
int pageNumber = floor(self.scrollView.contentOffset.x / self.scrollView.frame.size.width);
if(pageNumber == (n-1))
{
int timeOffset = 0;
for(CALayer *layer in layerArray)
{
[CATransaction begin];
CABasicAnimation *a = [CABasicAnimation animation];
a.duration = 6;
a.beginTime = timeOffset++;
[layer addAnimation:a forKey:@"opacity"];
[layer setOpacity:1];
}
for(CALayer *layer in layerArray)
[CATransaction commit];
}
}
But neither seems to work. When the user scrolls to the right page, all layers become visible at once, without much of a fade and definitely not in any sequential order.
Any ideas?
Upvotes: 10
Views: 6433
Reputation: 3809
Actually, it turns out that the key is getting the current time in terms of a frame of reference and adding any time offset to that current time. This works also for non-grouped animations.
For instance, something along the lines of this code would cause n layers (assumed to be stored in some array) to sequentially fade in one after the other, each taking .8 secs.:
CGFloat timeOffset = 0;
[CATransaction begin];
for (CALayer *layer in layers) {
CABasicAnimation *a = [CABasicAnimation animationWithKeyPath:@"opacity"];
a.fromValue = @(0);
a.toValue = @(1);
a.fillMode = kCAFillModeForwards;
a.beginTime = [layer convertTime:CACurrentMediaTime() fromLayer:nil] + timeOffset;
a.duration = 0.8;
a.removedOnCompletion = NO;
[layer addAnimation:a forKey:nil];
timeOffset += a.duration;
}
[CATransaction commit];
In the above case, the frame of reference is simply the current time when the invocations take place.
Upvotes: 18
Reputation: 6599
In Swift 3 (layers is an array of CALayer or CAShapeLayer)
var timeOffset:Double = 0
for layer in layers {
let a = CABasicAnimation(keyPath: "path"
a.fromValue = layer.ovalPathSmall.cgPath
a.toValue = layer.ovalPathLarge.cgPath
a.fillMode = kCAFillModeForwards
a.beginTime = CACurrentMediaTime() + timeOffset
a.duration = 0.3
a.isRemovedOnCompletion = false
layer.add(a, forKey: nil)
timeOffset += 0.3
}
And in case you're wondering what ovalPathSmall and ovalPathLarge are:
ovalPathSmall = UIBezierPath(arcCenter: position, radius: smallRadius, startAngle: 0, endAngle: 2 * .pi, clockwise: true)
ovalPathLarge = UIBezierPath(arcCenter: position, radius: largeRadius, startAngle: 0, endAngle: 2 * .pi, clockwise: true)
Upvotes: 0
Reputation: 385500
The beginTime
property of a CAAnimation
only seems to work if the CAAnimation
is part of a CAAnimationGroup
. I think you'll also need to set the duration
property of the CAAnimationGroup
large enough to last until its final animation completes.
https://stackoverflow.com/a/563486/77567
Upvotes: 5