Reputation: 4124
Here I am showing some frames (.png
) with fixed duration 1 (total frame 20 so 1/20 = 0.05f) in CAKeyframeAnimation. It is running perfectly.
- (void)animateImages
{
self.emoImageView = [[UIImageView alloc] initWithFrame:CGRectMake(100, 160, 50, 50)];
self.emoImageView.backgroundColor = [UIColor whiteColor];
[self.view addSubview:emoImageView];
NSArray *values = [NSArray arrayWithObjects:
(id)[UIImage imageNamed:@"smiley64_1_1.png"].CGImage,
(id)[UIImage imageNamed:@"smiley64_1_2.png"].CGImage,
(id)[UIImage imageNamed:@"smiley64_1_3.png"].CGImage,
(id)[UIImage imageNamed:@"smiley64_1_4.png"].CGImage,
(id)[UIImage imageNamed:@"smiley64_1_5.png"].CGImage,
(id)[UIImage imageNamed:@"smiley64_1_6.png"].CGImage,
(id)[UIImage imageNamed:@"smiley64_1_7.png"].CGImage,
(id)[UIImage imageNamed:@"smiley64_1_8.png"].CGImage,
(id)[UIImage imageNamed:@"smiley64_1_9.png"].CGImage,
(id)[UIImage imageNamed:@"smiley64_1_10.png"].CGImage,
(id)[UIImage imageNamed:@"smiley64_1_11.png"].CGImage,
(id)[UIImage imageNamed:@"smiley64_1_12.png"].CGImage,
(id)[UIImage imageNamed:@"smiley64_1_13.png"].CGImage,
(id)[UIImage imageNamed:@"smiley64_1_14.png"].CGImage,
(id)[UIImage imageNamed:@"smiley64_1_15.png"].CGImage,
(id)[UIImage imageNamed:@"smiley64_1_16.png"].CGImage,
(id)[UIImage imageNamed:@"smiley64_1_17.png"].CGImage,
(id)[UIImage imageNamed:@"smiley64_1_18.png"].CGImage,
(id)[UIImage imageNamed:@"smiley64_1_19.png"].CGImage,
(id)[UIImage imageNamed:@"smiley64_1_20.png"].CGImage, nil];
CAKeyframeAnimation *anim = [CAKeyframeAnimation animation];
//[anim setDuration:[[duration objectAtIndex:i] doubleValue]];
[anim setDuration:1];
[anim setValues:values];
[anim setKeyPath:@"contents"];
[anim setCalculationMode:@"discrete"];
[anim setRepeatCount: 90000];
[self.emoImageView.layer addAnimation:anim forKey:nil];
}
But now I want to show these frames with different Durations. I have an NSArray
with different duration values:
NSArray *duration = [NSArray arrayWithObjects:
[NSNumber numberWithFloat:0.4f],
[NSNumber numberWithFloat:0.4f],
[NSNumber numberWithFloat:0.4f],
[NSNumber numberWithFloat:0.4f],
[NSNumber numberWithFloat:0.4f],
[NSNumber numberWithFloat:0.4f],
[NSNumber numberWithFloat:0.4f],
[NSNumber numberWithFloat:0.4f],
[NSNumber numberWithFloat:0.4f],
[NSNumber numberWithFloat:0.4f],
[NSNumber numberWithFloat:0.4f],
[NSNumber numberWithFloat:0.4f],
[NSNumber numberWithFloat:0.4f],
[NSNumber numberWithFloat:0.4f],
[NSNumber numberWithFloat:0.4f],
[NSNumber numberWithFloat:0.4f],
[NSNumber numberWithFloat:0.4f],
[NSNumber numberWithFloat:0.4f],
[NSNumber numberWithFloat:0.4f],
[NSNumber numberWithFloat:0.4f], nil];
For that, I use a for loop
for both of them, like this:
- (void)animateImages
{
self.emoImageView = [[UIImageView alloc] initWithFrame:CGRectMake(100, 160, 50, 50)];
self.emoImageView.backgroundColor = [UIColor whiteColor];
[self.view addSubview:emoImageView];
NSArray *values = [NSArray arrayWithObjects:
(id)[UIImage imageNamed:@"smiley64_1_1.png"].CGImage,
(id)[UIImage imageNamed:@"smiley64_1_2.png"].CGImage,
(id)[UIImage imageNamed:@"smiley64_1_3.png"].CGImage,
(id)[UIImage imageNamed:@"smiley64_1_4.png"].CGImage,
(id)[UIImage imageNamed:@"smiley64_1_5.png"].CGImage,
(id)[UIImage imageNamed:@"smiley64_1_6.png"].CGImage,
(id)[UIImage imageNamed:@"smiley64_1_7.png"].CGImage,
(id)[UIImage imageNamed:@"smiley64_1_8.png"].CGImage,
(id)[UIImage imageNamed:@"smiley64_1_9.png"].CGImage,
(id)[UIImage imageNamed:@"smiley64_1_10.png"].CGImage,
(id)[UIImage imageNamed:@"smiley64_1_11.png"].CGImage,
(id)[UIImage imageNamed:@"smiley64_1_12.png"].CGImage,
(id)[UIImage imageNamed:@"smiley64_1_13.png"].CGImage,
(id)[UIImage imageNamed:@"smiley64_1_14.png"].CGImage,
(id)[UIImage imageNamed:@"smiley64_1_15.png"].CGImage,
(id)[UIImage imageNamed:@"smiley64_1_16.png"].CGImage,
(id)[UIImage imageNamed:@"smiley64_1_17.png"].CGImage,
(id)[UIImage imageNamed:@"smiley64_1_18.png"].CGImage,
(id)[UIImage imageNamed:@"smiley64_1_19.png"].CGImage,
(id)[UIImage imageNamed:@"smiley64_1_20.png"].CGImage, nil];
NSArray *duration = [NSArray arrayWithObjects:
[NSNumber numberWithFloat:0.4f],
[NSNumber numberWithFloat:0.4f],
[NSNumber numberWithFloat:0.4f],
[NSNumber numberWithFloat:0.4f],
[NSNumber numberWithFloat:0.4f],
[NSNumber numberWithFloat:0.4f],
[NSNumber numberWithFloat:0.4f],
[NSNumber numberWithFloat:0.4f],
[NSNumber numberWithFloat:0.4f],
[NSNumber numberWithFloat:0.4f],
[NSNumber numberWithFloat:0.4f],
[NSNumber numberWithFloat:0.4f],
[NSNumber numberWithFloat:0.4f],
[NSNumber numberWithFloat:0.4f],
[NSNumber numberWithFloat:0.4f],
[NSNumber numberWithFloat:0.4f],
[NSNumber numberWithFloat:0.4f],
[NSNumber numberWithFloat:0.4f],
[NSNumber numberWithFloat:0.4f],
[NSNumber numberWithFloat:0.4f], nil];
for (int i = 0; i < [duration count]; i++)
{
CAKeyframeAnimation *anim = [CAKeyframeAnimation animation];
[anim setDuration:[[duration objectAtIndex:i] doubleValue]];
[anim setValues:@[[values objectAtIndex:i]]];
[anim setKeyPath:@"contents"];
[anim setCalculationMode:@"discrete"];
[anim setRepeatCount: 90000];
[self.emoImageView.layer addAnimation:anim forKey:nil];
}
}
But the Frames are not showing. How can I animate some frames with different Durations in CAKeyframeAnimation
?
Thanks for stopping by.
Upvotes: 1
Views: 930
Reputation: 4124
- (void)viewDidLoad
{
[super viewDidLoad];
self.durationArray = [[NSMutableArray alloc] initWithObjects:
@"0.04",
@"0.04",
@"0.04",
@"0.04",
@"0.04",
@"0.04",
@"0.04",
@"0.04",
@"0.04",
@"0.04",
@"0.04",
@"0.04",
@"0.04",
@"0.04",
@"0.04",
@"0.04",
@"0.04",
@"0.04",
@"0.04",
@"0.04", nil];
[self animateImages];
}
- (void)animateImages
{
self.emoImageView = [[UIImageView alloc] initWithFrame:CGRectMake(100, 160, 50, 50)];
self.emoImageView.backgroundColor = [UIColor whiteColor];
[self.view addSubview:emoImageView];
NSArray *values = [NSArray arrayWithObjects:
(id)[UIImage imageNamed:@"smiley64_1_1.png"].CGImage,
(id)[UIImage imageNamed:@"smiley64_1_2.png"].CGImage,
(id)[UIImage imageNamed:@"smiley64_1_3.png"].CGImage,
(id)[UIImage imageNamed:@"smiley64_1_4.png"].CGImage,
(id)[UIImage imageNamed:@"smiley64_1_5.png"].CGImage,
(id)[UIImage imageNamed:@"smiley64_1_6.png"].CGImage,
(id)[UIImage imageNamed:@"smiley64_1_7.png"].CGImage,
(id)[UIImage imageNamed:@"smiley64_1_8.png"].CGImage,
(id)[UIImage imageNamed:@"smiley64_1_9.png"].CGImage,
(id)[UIImage imageNamed:@"smiley64_1_10.png"].CGImage,
(id)[UIImage imageNamed:@"smiley64_1_11.png"].CGImage,
(id)[UIImage imageNamed:@"smiley64_1_12.png"].CGImage,
(id)[UIImage imageNamed:@"smiley64_1_13.png"].CGImage,
(id)[UIImage imageNamed:@"smiley64_1_14.png"].CGImage,
(id)[UIImage imageNamed:@"smiley64_1_15.png"].CGImage,
(id)[UIImage imageNamed:@"smiley64_1_16.png"].CGImage,
(id)[UIImage imageNamed:@"smiley64_1_17.png"].CGImage,
(id)[UIImage imageNamed:@"smiley64_1_18.png"].CGImage,
(id)[UIImage imageNamed:@"smiley64_1_19.png"].CGImage,
(id)[UIImage imageNamed:@"smiley64_1_20.png"].CGImage, nil];
// The total duration of my animation
float Duration = 0;
for (int i = 0; i < [durationArray count]; i++)
{
Duration = Duration + [[durationArray objectAtIndex: i] floatValue];
}
NSMutableArray *keyTimes = [[NSMutableArray alloc] init];
float keyTimesRow = 0.0;
for (int i = 0; i < [durationArray count]; i++)
{
keyTimesRow = keyTimesRow + ([[durationArray objectAtIndex: i] floatValue]/Duration);
NSNumber *k = [NSNumber numberWithFloat:keyTimesRow];
//keyTimes[0] = @(0.0);
[keyTimes addObject:k];
}
CAKeyframeAnimation *anim = [CAKeyframeAnimation animation];
[anim setDuration:Duration];
[anim setKeyTimes:keyTimes];
[anim setValues:values];
[anim setKeyPath:@"contents"];
[anim setRepeatCount: -1];
[self.emoImageView.layer addAnimation:anim forKey:nil];
}
Here I just calculate the total duration of my Animation. So that latter I can set it with my Animation instance.
// The total duration of my animation
float Duration = 0;
for (int i = 0; i < [durationArray count]; i++)
{
Duration = Duration + [[durationArray objectAtIndex: i] floatValue];
}
Here I am creating KeyTimes array from the time interval of each frames. And create the keyTimes array.
NSMutableArray *keyTimes = [[NSMutableArray alloc] init];
float keyTimesRow = 0.0;
for (int i = 0; i < [durationArray count]; i++)
{
keyTimesRow = keyTimesRow + ([[durationArray objectAtIndex: i] floatValue]/Duration);
NSNumber *k = [NSNumber numberWithFloat:keyTimesRow];
//keyTimes[0] = @(0.0);
[keyTimes addObject:k];
}
And finally set all these in animation instance.
CAKeyframeAnimation *anim = [CAKeyframeAnimation animation];
[anim setDuration:Duration];
[anim setKeyTimes:keyTimes];
[anim setValues:values];
[anim setKeyPath:@"contents"];
[anim setRepeatCount: -1];
[self.emoImageView.layer addAnimation:anim forKey:nil];
Upvotes: 1
Reputation: 131418
You're not doing it right. Don't create multiple keyframe animations in a loop. Just like when you create an animation with all the same duration, create a single keyframe animation. However, to control the duration on a frame-by-frame basis, set the keyTimes property on your animation object to the array of durations that you created.
Note that the values for keyTimes are the relative start time for each frame in the animation. They must range from 0.0 to 1.0, and each subsequent value must be greater than the last:
#define steps 20
NSMutableArray *startTimes = [NSMutableArray arrayWithCapacity: steps];
for (int i = 0; i< steps; i++)
{
startTimes[i] = @(i* 1.0/2.0);
}
You should read the Xcode docs on CAKeyframeAnimation. They're actually quite informative.
BTW, any time you have code that does almost exactly the same thing over and over, it's worth thinking about making a loop. Like when you load your images:
NSMutableArray *images = [NSMutableArray arrayWithCapacity: steps];
for (int i = 0; i< steps; i++)
{
//The filenames start at 1, so use i+1 to create the filename.
NSString *filename = [NSString stringWithFormat: @"smiley64_1_%d", i+1];
images[i] = (id)[UIImage imageNamed: filename].CGImage;
}
Also note that you should usually not include the file extension in calls to imageNamed. If you don't, the system will load the image at the appropriate resolution (normal, @2x retina, or @3x retina.) I believe that including an explicit extension breaks that, although I'm not positive.
Read this bit from the docs:
Each value in the array is a floating point number between 0.0 and 1.0 that defines the time point (specified as a fraction of the animation’s total duration) at which to apply the corresponding keyframe value. Each successive value in the array must be greater than, or equal to, the previous value. Usually, the number of elements in the array should match the number of elements in the values property or the number of control points in the path property. If they do not, the timing of your animation might not be what you expect.
Upvotes: 3