for example:
label.text = #"I \n love \n stackoverflow ";
how to show those three lines just like first animation fade show "I" , then after animation during time animation fade show "love" finally after animation during time animation fade show "stackoverflow"?
I have created a function for your request. Make sure your label have dynamic height, numberOfLines = 0 and try it ;)
- (void)animateLabel:(UILabel*)label withString:(NSString*)string duration:(NSTimeInterval)duration {
NSUInteger length = [string length];
NSUInteger paraStart = 0, paraEnd = 0, contentsEnd = 0;
NSMutableArray *displayedStringArray = [NSMutableArray array];
NSRange currentRange;
while (paraEnd < length) {
[string getParagraphStart:¶Start end:¶End
contentsEnd:&contentsEnd forRange:NSMakeRange(paraEnd, 0)];
currentRange = NSMakeRange(0, contentsEnd);
[displayedStringArray addObject:[string substringWithRange:currentRange]];
}
CATransition *animation = [CATransition animation];
animation.duration = duration;
animation.type = kCATransitionFade;
animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
__block void (^animationBlock) (int lineNumber);
__block void (^weakAnimationBlock) (int lineNumber);
animationBlock = ^void(int lineNumber){
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(duration * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[label.layer addAnimation:animation forKey:#"changeTextTransition"];
label.text = displayedStringArray[lineNumber];
weakAnimationBlock = animationBlock;
__block int nextLine = lineNumber + 1;
if (nextLine < displayedStringArray.count) {
weakAnimationBlock(nextLine);
}
});
};
animationBlock(0);
}
Simply call
[YOUR_CLASS animateLabel:label withString:#"I\nLove\nStackOverFlow" duration:YOUR_DURATION];]
I have an app which displays some basic animation triggered by the playback of midi file. It works ok during the first playback but freezes during subsequent playbacks. The device then usually reboots generating a crash log indicating that a kernel panic took place. Occasionally it just hangs without a reboot.
The animation consists of an small png image which has the opacity changed from high to low to give a fading effect.
The freeze takes place on the line :[self performSelectorOnMainThread:#selector( setPitch: ) withObject:[NSNumber numberWithInteger:[note intValue]] waitUntilDone:YES];
Here is the method which contains this line:
- (void)log:(NSNotification *)notification {
[NSThread isMainThread];
NSDictionary* info = notification.userInfo;
NSNumber *note;
note = [info objectForKey:kNAMIDI_Note];
BRMidiNoteName *noteConverter = [[BRMidiNoteName alloc] init];
NSString *noteName;
noteName = [noteConverter nameFromNumber:[note intValue] withNotation:[defaults valueForKey:kSettingsNotation]];
[self.currentNoteLabel performSelectorOnMainThread: #selector( setText: ) withObject: noteName waitUntilDone: YES];
[self performSelectorOnMainThread:#selector( setPitch: ) withObject:[NSNumber numberWithInteger:[note intValue]] waitUntilDone:YES];
}
-(void) setPitch:(NSNumber*) pitchValue{
NSInteger visualPitchValue = [pitchValue intValue]- [_currentTrack.trackStartNote intValue] - voiceTransposeValue + 1;
if ([defaults boolForKey:kSettingsVisualiser]) {
//UIImageView *img = [[UIImageView alloc]init];
UIImageView *img;
if ((visualPitchValue > 0 )) {
// highlight new pitch
img = (UIImageView *)[self.view viewWithTag:visualPitchValue];
if (([pitchValue integerValue] < [currentVoice.bridgeLow integerValue])) {
[img setImage:[UIImage imageNamed:#"bar_green.png"]];
}
else if (([pitchValue integerValue] <= [currentVoice.bridgeHigh integerValue])) {
[img setImage:[UIImage imageNamed:#"bar_orange"]];
}
else{
[img setImage:[UIImage imageNamed:#"bar_blue.png"]];
}
[img setAlpha:1.0];
// Set up fade out effect
CABasicAnimation *fadeOutAnimation = [CABasicAnimation animationWithKeyPath:#"opacity"];
[fadeOutAnimation setToValue:[NSNumber numberWithFloat:_fadeToOpacity]];
fadeOutAnimation.fillMode = kCAFillModeForwards;
fadeOutAnimation.removedOnCompletion = NO;
CAAnimationGroup *group = [CAAnimationGroup animation];
group.fillMode = kCAFillModeForwards;
group.removedOnCompletion = NO;
// [group setAnimations:[NSArray arrayWithObjects:fadeOutAnimation, pathAnimation, nil]];
[group setAnimations:[NSArray arrayWithObjects:fadeOutAnimation, nil]];
group.duration = 0.7f;
group.delegate = self;
[group setValue:img forKey:#"imageViewBeingAnimated"];
[img.layer addAnimation:group forKey:#"savingAnimation"];
}
}
}
I found that changing the waitUntilDone from YES to NO stopped the device screen from freezing.
[self.currentNoteLabel performSelectorOnMainThread: #selector( setText: ) withObject: noteName waitUntilDone: NO];
[self performSelectorOnMainThread:#selector( setPitch: ) withObject:[NSNumber numberWithInteger:[note intValue]] waitUntilDone:NO];
I have a method which translates a view and optionally scales it while translating. I am using animation groups and adding the required animations as needed. Here are the animation cases working perfectly.
Translation (x or y axis or both) (OK)
Scaling (OK)
Translation while scaling (MOSTLY)
However, doing a translation while scaling works only when the starting scalefactor is 1. When the view is already scaled, I notice most of the transition jumping instead of animating, although the end result of scaling and translation is correct.
I am posting part of the code for clarification. The problem occurs only when previousZoomScale is not 1! I appreciate any directions you can point me to.
-(void)performAnimationForView: (AnimationType)animationType
WithDX: (CGFloat)dx
AndDY: (CGFloat)dy
Scale: (CGFloat)scale
Duration: (CGFloat)duration
{
CABasicAnimation *translateXAnimation = nil, *translateYAnimation = nil, *scaleAnimation = nil;
NSString* animationGroupName;
CGPoint newDxDy;
switch (animationType)
{
case kAnimationTranslateAndScale:
// set animation key
animationGroupName = #"translatescale";
translateXAnimation = [CABasicAnimation animationWithKeyPath:#"transform.translation.x"];
translateXAnimation.repeatCount = 0;
translateXAnimation.autoreverses = NO;
translateXAnimation.removedOnCompletion = NO;
translateXAnimation.fillMode = kCAFillModeForwards;
translateXAnimation.fromValue = [NSNumber numberWithFloat: self.previousDxDy.x]; // previous point we're tranlsating from
translateXAnimation.toValue = [NSNumber numberWithFloat:dx]; // destination point
translateYAnimation = [CABasicAnimation animationWithKeyPath:#"transform.translation.y"];
translateYAnimation.repeatCount = 0;
translateYAnimation.autoreverses = NO;
translateYAnimation.removedOnCompletion = NO;
translateYAnimation.fillMode = kCAFillModeForwards;
translateYAnimation.fromValue = [NSNumber numberWithFloat: self.previousDxDy.y];
translateYAnimation.toValue = [NSNumber numberWithFloat:dy];
newDxDy = CGPointMake(dx, dy);
self.previousDxDy = newDxDy;
// scaling animation
scaleAnimation = [CABasicAnimation animationWithKeyPath:#"transform.scale"];
scaleAnimation.autoreverses = NO;
scaleAnimation.repeatCount = 0;
scaleAnimation.removedOnCompletion = NO;
scaleAnimation.fillMode = kCAFillModeForwards;
scaleAnimation.fromValue = [NSNumber numberWithDouble: self.previousZoomScale]; // initial zoom scale
scaleAnimation.toValue = [NSNumber numberWithDouble:scale]; // target scale
self.previousZoomScale = scale;
break;
}
NSMutableArray* animationArray = [[NSMutableArray alloc] initWithObjects: nil];
if (translateXAnimation != nil)
[animationArray addObject: translateXAnimation];
if (translateYAnimation != nil)
[animationArray addObject: translateYAnimation];
if (scaleAnimation != nil)
[animationArray addObject: scaleAnimation];
CAAnimationGroup *theGroup = [CAAnimationGroup animation];
theGroup.duration = duration;
theGroup.removedOnCompletion = NO;
theGroup.fillMode = kCAFillModeForwards;
theGroup.repeatCount = 0;
theGroup.timingFunction = [CAMediaTimingFunction functionWithName: kCAMediaTimingFunctionLinear];
theGroup.animations = animationArray;
theGroup.delegate = self;
[theGroup setValue: animationGroupName forKey: #"animationtype"];
[self.backgroundImageView.layer addAnimation: theGroup forKey: animationGroupName];
}
I am trying to animate several views (layers) using CAKeyframeAnimation and CAAnimationGroup. Each view to be animated is contained in an array. My idea is to loop through the array where CAKeyframeAnimation instance of each view would be added to an array of animations, which then would be added to the CAAnimationGroup. My goal is to animate each view one after another. As far as I understand I need to use the group in order to have a reference for the beginTime.
I am probably missing something obvious, but my code doesn't work
CAAnimationGroup *cardAnimationGroup = [CAAnimationGroup animation];
cardAnimationGroup.delegate = self;
cardAnimationGroup.removedOnCompletion = NO;
cardAnimationGroup.beginTime = 0.0f;
[cardAnimationGroup setValue:#"animation0" forKey:#"id"];
cardAnimationGroup.duration = 1.2f * [_myViewsArray count];
NSMutableArray *animationsArray = [[NSMutableArray alloc] init];
// Loop through views
for (UIView *cardView in _myViewsArray)
{
NSUInteger index = [_myViewsArray indexOfObject:cardView];
// my target origin
CGFloat yOffset = (-(cardView.frame.origin.y - _middleCardView.frame.origin.y) - index * 2);
CAKeyframeAnimation *translateCardPositionAnimation = [CAKeyframeAnimation animationWithKeyPath:#"transform.translation.y"];
translateCardPositionAnimation.removedOnCompletion = YES;
//translateCardPositionAnimation.fillMode = kCAFillModeForwards;
translateCardPositionAnimation.beginTime = 1.2f * index;
translateCardPositionAnimation.duration = 1.2f;
translateCardPositionAnimation.values = [NSArray arrayWithObjects:
[NSNumber numberWithFloat:0.0f],
[NSNumber numberWithFloat:yOffset],
[NSNumber numberWithFloat:yOffset],nil];
translateCardPositionAnimation.keyTimes = [NSArray arrayWithObjects:
[NSNumber numberWithFloat:0.0f],
[NSNumber numberWithFloat:1.0f],
[NSNumber numberWithFloat:1.2f], nil];
[_containerCardView addSubview:cardView];
[_containerCardView sendSubviewToBack:cardView];
[animationsArray addObject:translateCardPositionAnimation];
[cardView.layer addObject:translateCardPositionAnimation forKey:#"id"];
if (index == [_myViewsArray count]-1) {
[cardAnimationGroup setAnimations:animationsArray];
}
}
in iOS5.0 using blocks, say if we have an UIViewAnimationOptionAutoreverse animation, and need to put in a delay before the reverse animation starts.. how can we do this?
Thanks for your help on this
The best way to pause and resume animation :
add QuartzCore framework
then use the following apple's code
-(void) pause
{
CFTimeInterval pausedTime = [m_pCustomImageBtnObj.layer convertTime:CACurrentMediaTime() fromLayer:nil];
obj.layer.speed = 0.0;
obj.layer.timeOffset = pausedTime;
}
-(void) resume
{
CFTimeInterval pausedTime = [obj.layer timeOffset];
obj.layer.speed = 1.0;
obj.layer.timeOffset = 0.0;
obj.layer.beginTime = 0.0;
CFTimeInterval timeSincePause = [obj.layer convertTime:CACurrentMediaTime() fromLayer:nil] - pausedTime;
obj.layer.beginTime = timeSincePause;
}
You can try animation completion block like this:
[UIView animateWithDuration:0.6 animations:^(void)
{
self.label.bounds = CGRectMake(10, 10, 200, 200);
}completion:^(BOOL finished) {
[UIView animateWithDuration:0.6 animations:^(void)
{
self.label.bounds = CGRectMake(10, 10, 100, 100);
}completion:^(BOOL finished) {
}];
}];
You should use keyframe animation for that. Here is a piece of code to do rotation with reversing:
self.animations = [NSDictionary dictionaryWithObjects:
[NSArray arrayWithObjects:
[CAKeyframeAnimation animationWithKeyPath:#"transform.rotation.z"],
[CAKeyframeAnimation animationWithKeyPath:#"transform.rotation.z"]
, nil ]
forKeys:
[NSArray arrayWithObjects: THERE_ANIM_KEY, BACK_AGAIN_ANIM_KEY , nil ]];
CAKeyframeAnimation *kfAnim = [animations objectForKey: THERE_ANIM_KEY];
kfAnim.duration = 2;
kfAnim.repeatCount = 1;
kfAnim.values = [NSArray arrayWithObjects:
[NSNumber numberWithFloat: 0.0 * M_PI],
[NSNumber numberWithFloat: 1.0 * M_PI],
[NSNumber numberWithFloat: 2.0 * M_PI],
[NSNumber numberWithFloat: 2.0 * M_PI], //here is delay
[NSNumber numberWithFloat: 1.0 * M_PI],
[NSNumber numberWithFloat: 0.0 * M_PI],
nil];
kfAnim.keyTimes = [NSArray arrayWithObjects:
[NSNumber numberWithFloat: 0.0 ],
[NSNumber numberWithFloat: 0.3 ],
[NSNumber numberWithFloat: 0.5 ], //time of delaying in proportion of general animation duration
[NSNumber numberWithFloat: 0.5 ],
[NSNumber numberWithFloat: 0.8 ],
[NSNumber numberWithFloat: 1.0 ], nil];
Also you can use remember initial state and animate back in
(void)animationDidStop:(CAAnimation *)theAnimation finished:(BOOL)flag
animation delegate method with using performSelector:withObject:afterDelay:. This is much simplier and easy to set up delay.
Not sure if this is exactly what you're trying to do, but here's what I'm using to move a view onto the screen, pause for 1 second, and then move view off the screen.
[UIView animateWithDuration:0.5 animations:^{
self.view.frame = viewFrameOnScreen;
self.view.alpha = 1.0;
} completion:^(BOOL finished){
[UIView animateWithDuration:0.5 delay:1 options:0 animations:^{
self.view.frame = viewFrameOffScreen;
self.view.alpha = 0.0f;
} completion:^(BOOL finished){}];
}];
RIAZ answer in Swift 3:
func pauseAnimation() {
let pausedTime = obj.layer.convertTime(CACurrentMediaTime(), from: nil)
obj.layer.speed = 0.0
obj.layer.timeOffset = pausedTime
}
func resumeAnimation() {
let pausedTime = obj.layer.timeOffset
obj.layer.speed = 1.0
obj.layer.timeOffset = 0.0
obj.layer.beginTime = 0.0
let timeSincePause = obj.layer.convertTime(CACurrentMediaTime(), from: nil) - pausedTime
obj.layer.beginTime = timeSincePause
}