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
}
Related
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];]
(a) How to animate strikethrough? I tried the following but not working.
-(void)strikeThrough
{
NSNumber *strikeSize = [NSNumber numberWithInt:1];
NSDictionary *strikeThroughAttribute = [NSDictionary dictionaryWithObject:strikeSize forKey:NSStrikethroughStyleAttributeName];
NSAttributedString* strikeThroughText = [[NSAttributedString alloc] initWithString:_label.text attributes:strikeThroughAttribute];
_label.attributedText = nil;
[UIView animateWithDuration:2.0
animations:^{ _label.attributedText = strikeThroughText; }
completion:^(BOOL finished){ }];
}
(b) Also, the style of strikethrough does not match with the font. For example, I use chalk font, but the strike line does not look like chalk. How to deal with it?
Thanks a lot for the help.
One way of having a have an animation for it is to use CATransition
NSNumber *strikeSize = [NSNumber numberWithInt:1];
NSDictionary *strikeThroughAttribute = [NSDictionary dictionaryWithObject:strikeSize forKey:NSStrikethroughStyleAttributeName];
NSAttributedString* strikeThroughText = [[NSAttributedString alloc] initWithString:self.mylabel.text attributes:strikeThroughAttribute];
self.mylabel.attributedText = nil;
CATransition *transition = [CATransition new];
transition.delegate = self;
transition.type = kCATransitionFromLeft;
transition.duration = 2.0f;
self.mylabel.attributedText = strikeThroughText;
[self.mylabel.layer addAnimation:transition forKey:#"transition"];
and for the completion block
-(void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag
{
//Check for CATransition here
}
For your second point check here . The link briefly gives you other ways to handle the way you want the strike to look like, you cannot change the font of it.
Hope this helps.
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];
}
}
I made this code to create CAKeyframeAnimation but result is not bouncing at all
-(CAKeyframeAnimation *)keyframeBounceAnimationFrom:(NSValue *)from
to:(NSValue *)to
forKeypath:(NSString *)keyPath
withDuration:(CFTimeInterval)duration
{
CAKeyframeAnimation * animation = [CAKeyframeAnimation animationWithKeyPath:keyPath];
NSMutableArray * valuesArray = [NSMutableArray array];
NSMutableArray * timeKeyArray = [NSMutableArray array];
[self createBounceFrom:from to:to Values:valuesArray keyTimes:timeKeyArray];
[animation setValues:valuesArray];
[animation setKeyTimes:timeKeyArray];
animation.duration = duration;
return animation;
}
-(void)createBounceFrom:(NSValue *)from to:(NSValue *)to Values:(NSMutableArray *)values keyTimes:(NSMutableArray *)keyTimes
{
CGPoint toPoint= [to CGPointValue];
CGFloat offset = 60;
CGFloat duration = 1.0f;
NSUInteger numberOfOscillations= 4;
[values addObject:from];
[keyTimes addObject:[NSNumber numberWithFloat:0.0f]];
//============
//ideally bouncing will depend from starting position to end poisiton as simulating real Dumping Oscillations
//============
for (NSUInteger index= 0; index <numberOfOscillations ; index++)
{
CGPoint viaPoint = CGPointMake(toPoint.x, toPoint.y + offset);
NSValue * via = [NSValue valueWithCGPoint:viaPoint];
[values addObject:via];
//add time consumed for each oscillation
[keyTimes addObject:[NSNumber numberWithFloat:duration]];
// duration = duration - 0.1;
offset = - offset ;
}
[values addObject:to];
[keyTimes addObject:[NSNumber numberWithFloat:0.6]];
}
Here's a great link with some examples of how to use it:
(After some more looking I found this: http://www.cocoanetics.com/2012/06/lets-bounce/ which I think makes a much better bounce, but more advanced. So wont put the code here)
http://www.touchwonders.com/some-techniques-for-bouncing-animations-in-ios/
The example with CAKeyframeAnimation would be: (taken from the link)
-(void)animateGreenBall
{
NSValue *from = #(greenBall.layer.position.y);
CGFloat bounceDistance = 20.0;
CGFloat direction = (greenUp? 1 : -1);
NSValue *via = #((greenUp ? HEIGHT_DOWN : HEIGHT_UP) + direction*bounceDistance);
NSValue *to = greenUp ? #(HEIGHT_DOWN) : #(HEIGHT_UP);
NSString *keyPath = #"position.y";
CAKeyframeAnimation *animation = [self keyframeBounceAnimationFrom:from
via:via
to:to
forKeyPath:keyPath
withDuration:.6];
[greenBall.layer addAnimation:animation forKey:#"bounce"];
[greenBall.layer setValue:to forKeyPath:keypath];
greenUp = !greenUp;
}
-(CAKeyframeAnimation *)keyframeBounceAnimationFrom:(NSValue *)from
via:(NSValue *)via
to:(NSValue *)to
forKeyPath:(NSString *)keyPath
withDuration:(CFTimeInterval)duration
{
CAKeyframeAnimation *animation = [CAKeyframeAnimation animationWithKeyPath:keyPath];
[animation setValues:#[from, via, to, nil]];
[animation setKeyTimes:#[#(0), #(.7), #(1), nil]];
animation.duration = duration;
return animation;
}