iOS animation - upward scroll of text like Siri - ios

I am looking to have an animation in my iOS app which is almost identical to the animation you see when you use Siri when it is displaying possible questions you can ask it.
The effect is an upward scroll of text replaced by more than come from below but the effect is one in a way where not all the questions move as one together, they seem to be independent and follow each other.
I like this animation and would like to emulate it in my app - but I haven't the first idea how to go around this. My only involvement with animations within my apps so far are ones such as CrossDissolve, FlipFromLeft, etc.
Does anyone know of a tutorial which teaches an effect like this, or can point me in the right direction to start?
Thanks to all in advance.

Having a quick look it looks fairly straight forward.
The animation can be broken down into several stages.
First, there are 5 UILabels animated separately. Each with a very short delay on the previous label.
Each label is animated like this...
Set the frame of the label to be low down on the screen and set alpha to 0.0.
Animate the frame of the label to around 300 points higher on the screen and alpha of 1.0 over a duration of around 0.2 seconds.
Animate the frame of the label to about 30 points higher on the screen over about 2 or 3 seconds.
Animate the frame of the label another 300 points higher on the screen and alpha back to 0.0 over about 0.2 seconds again.
Change text on the label and repeat.
I think the easiest way to do this would be with the method...
+ (void)animateKeyframesWithDuration:(NSTimeInterval)duration
delay:(NSTimeInterval)delay
options:(UIViewKeyframeAnimationOptions)options
animations:(void (^)(void))animations
completion:(void (^)(BOOL finished))completion
Lemme give this a try in actual code and get back to you. Code completion helps a lot.
OK, here goes
Darn, I just installed Yosemite and don't have Xcode. OK, I'll give it a try here...
- (void)animateLabels:(NSArray *)labels
{
CGFloat firstLabelStartTop = 600;
[labels enumerateObjectsUsingBlock:^(UILabel *label, NSUInteger idx, BOOL *stop) {
CGFloat delay = 0.1 * idx;
label.frame = CGRectMake(0, firstLabelStartTop + 30 * idx, 320, 21);
label.alpha = 0.0;
[UIView animateKeyFramesWithDuration:2.4
delay:delay
options:0
animations:^{
[UIView addKeyframeWithRelativeStartTime:0.0
relativeDuration:0.1
animations:^{
label.alpha = 1.0;
label.frame = CGRectOffset(label.frame, 0, -200);
}];
[UIView addKeyframeWithRelativeStartTime:0.1
relativeDuration:0.9
animations:^{
label.frame = CGRectOffset(label.frame, 0, -30);
}];
[UIView addKeyframeWithRelativeStartTime:0.9
relativeDuration:0.1
animations:^{
label.alpha = 0.0;
label.frame = CGRectOffset(label.frame, 0, -200);
}];
}
completion:nil];
}];
}
This is a first attempt without Xcode to check my code and without being able to run it at all so it might not be perfect but it should give you an idea of where to go from here.
EDIT
From #WilliamGeorges comment it looks like they used a combination of completely separate animations using the method I detailed in my blog.
You would have to do 3 separate animations on each label but its still a similar idea to what I put in this answer.

Related

Migrating from `POPSpringAnimation` to native iOS framework in Swift 4

I am working on an old project and want to get rid of POP framework I am sure that any animation can be done with native iOS framework.
Here is the old code:
POPSpringAnimation *springAnimation = [POPSpringAnimation animationWithPropertyNamed:kPOPViewFrame];
springAnimation.toValue = [NSValue valueWithCGRect:rect];
springAnimation.velocity = [NSValue valueWithCGRect:CGRectMake(springVelocity, springVelocity, 0, 0)];
springAnimation.springBounciness = springBounciness;
springAnimation.springSpeed = springSpeed;
[springAnimation setCompletionBlock:^(POPAnimation *anim, BOOL finished) {
if (finished) {
// cool code here
}
}];
[self.selectedViewController.view pop_addAnimation:springAnimation forKey:#"springAnimation"];
What I have tried:
[UIView animateWithDuration:1.0
delay:0
usingSpringWithDamping:springBounciness
initialSpringVelocity:springVelocity
options:UIViewAnimationOptionCurveEaseInOut animations:^{
self.selectedViewController.view.frame = rect;
} completion:^(BOOL finished) {
// cool code here
}];
But I dont get the same result, and some question rises:
is springBounciness in pop equivalent to usingSpringWithDamping ?
What is equivalent of springSpeed in UIView animation ?
what about the duration, what is the duration of POPSpringAnimation ?
Edit:
About the third question I found an issue in Github.
If UIView is not the way to go can that be done using Core Animation or any other iOS native animation framework ?
Pop parameter values range from 0-20. But the usingSpringWithDamping do not have such range. Obviously as Pop is a custom library, it has its own range of values while UIView animation has its own.
From Apple documentation, usingSpringWithDamping parameter is actually damp ratio, and it specifies:
To smoothly decelerate the animation without oscillation, use a value
of 1. Employ a damping ratio closer to zero to increase oscillation.
1.So if you want equivalent bounciness, you need to use values anything below 1, I guess you could try the following formula for springBounciness.
float uiViewBounciness = (20.0 - springBounciness) / 20.0;
.. usingSpringWithDamping:uiViewBounciness ..
2.As for springVelocity, Pop implements a same speed for all animation frames, whereas UIView animation only specifies the INITIAL speed, and this speed is decayed over time based on total duration and damp ratio. So to get as close animation as possible, you could do the following:
float uiViewSpeed = springVelocity * 2.0;
.. initialSpringVelocity:uiViewSpeed ..
3.As for duration, you can implement the same value to animateWithDuration in UIView method.
Finally, you need to experiment with the values and compare it to Pop animations. I don't think you can get the exact same animations with Pop by using UIView animate, but it should be close enough.

Animate Change Height and Width at Different Times

I have an image that I am animating in order to make it look as if it is "breathing".
Currently I have the image moving in a decent manner with the following code below: (I am animating a UIView that contains a few UIImageView's, which all move as one)
- (IBAction)animateButton:(id)sender {
[UIView animateWithDuration:0.64
delay:0
options:UIViewAnimationOptionAutoreverse | UIViewAnimationOptionRepeat
animations:^{
_testView.transform = CGAffineTransformMakeScale(1.08f, 1.02f);
} completion:nil];
}
HOWEVER, I can not seem to figure out how to animate stretching the image in the x at a different rate as the y. The point of this is to appear as if the image is actually alive without appearing to cycle through a clear repetitive motion.
I tried by attempting to anchor the center of the UIView to a specific location, then add some number to the width, through an animation of lets say 1.0 seconds.
I then tried to simultaneously call another animation that does the same animation only to the height, with a different amount added, for about 1.3 seconds. I could not get these two to perform at the same time though, as one would take precedence over the other.
If someone could lead me in the right direction as to animating a repetitive stretch of the width and height at different rates I would be most appreciative. Thanks!
Consider that two changes overlapping in time look like this:
|---- change x ---|
|---- change y ----|
If the two intervals are arbitrary and overlapping, the can be represented by three animations: one changing one dimension individually, one changing both dimensions together, and another changing one dimension individually.
You can see that there's numerous ways to specify this, but lets try a straight-forward one. To avoid the arithmetic of compounding scales, lets specify a dimension, a pixel change and a duration. For example...
#[ #{#"dimension":#"width", #"delta":#10, #"duration":0.2},
#{#"dimension":#"both", #"delta":#40, #"duration":0.8},
#{#"dimension":#"width", #"delta":#10, #"duration":0.2} ]
... means a longer change in width straddling a shorter change in height. You can see how this can be a pretty complete language to get done what you want.
We need an animation method that will perform the changes serially. A simple way to do this is to treat the array of actions as a to-do list. The recursive algorithm says: to do a list of things, do the first one, then do the rest....
- (void)animateView:(UIView *)view actions:(NSArray *)actions completion:(void (^)(BOOL))completion {
if (actions.count == 0) return completion(YES);
NSDictionary *action = actions[0];
NSArray *remainingActions = [actions subarrayWithRange:NSMakeRange(1, actions.count-1)];
[self animateView:view action:action completion:^(BOOL finished) {
[self animateView:view actions:remainingActions completion:completion];
}];
}
For the animation, you probably want to use a linear timing curve for the intermediate animations, though I can see you getting more elaborate and change the timing curve at the start and end of the list.
- (void)animateView:(UIView *)view action:(NSDictionary *)action completion:(void (^)(BOOL))completion {
NSString *dimension = action[#"dimension"];
CGFloat delta = [action[#"delta"] floatValue];
NSTimeInterval duration = [action[#"duration"] floatValue];
CGRect frame = view.frame;
if ([dimension isEqualToString:#"width"]) {
frame = CGRectInset(frame, -delta, 0);
} else if ([dimension isEqualToString:#"height"]) {
frame = CGRectInset(frame, 0, -delta);
} else {
frame = CGRectInset(frame, -delta, -delta);
}
[UIView animateWithDuration:duration delay:0 options:UIViewAnimationOptionCurveLinear animations:^{
view.frame = frame;
} completion:completion];
}
If the array of dictionaries is too clumsy to specify (and it is rather general), you could add some convenience methods on top that provide some simpler scheme to the caller and builds the array of more general representation.

UiTextField not animating in properly..instead it's doing an odd masking reveal?

I wasn't quiet sure how to word this, but here goes.
I have a couple of UIControls that I am sliding into the view with in the viewWillAppear method. The UIControls animate correctly as well as my graphics inside those UIControls, but the UITextField is not. It should be starting to the right 50 points with an alpha of 0 fading into 1. Instead the UITextFields does this odd masking and from the other direction (left to right instead of right to left, as well as a bit higher on the y axis). Does this have something to do with the UITextField not being ready for display when I start my animation? Below is the code.
nameOfBillViewConstraint.constant = 50;
nameOfBillView.alpha = 0.0;
[UIView animateWithDuration:10
delay: 0.5
options: UIViewAnimationOptionCurveEaseIn
animations:^{
nameOfBillViewConstraint.constant = 10;
nameOfBillView.alpha = 1.0;
[self.view layoutIfNeeded];
}
completion:nil];
Thanks for your help on this.It's more of a visual problem, but I wasn't sure how to display that here...

IOS - Autoresizing views issue when shrinking too much

I have my subviews inside my view with the appropriate AutoresizingMasks so that they accommodate accordingly when the view's size changes.
My issue comes when the view is shrinked too much, or even not that much. The subviews seems to forget their original position and begin to place in weird positions or scale too much or too less.
The original requirement is to "Shrink a view" to its center and then to "Popit up" like a balloon, from the ~=zero-size to the original frame size.
I have shrinked and popped up views before without much problem in other projects I don't know if it's because the view is more complex now
Well, as I said, I have done this before so I just checked my other project and I found my problem, I post the answer here to share the knowledge for anyone interested, as I couldn't google this moments before.
The problem is that shrinking or growing views using their frame is not the correct method to do this.
Bad example:
[UIView animateWithDuration: 0.4 delay:0 options: UIViewAnimationCurveEaseInOut animations:^{
self.frame = CGRectMake(frame.origin.x + frame.size.width/2, frame.origin.y + frame.size.height/2, 10, 10);
} completion: nil];
instead, transforms should be used:
self.transform = CGAffineTransformScale(CGAffineTransformIdentity, 1, 1);
[UIView animateWithDuration: 0.4 delay:0 options: UIViewAnimationCurveEaseInOut animations:^{
self.transform = CGAffineTransformScale(CGAffineTransformIdentity, 0.001, 0.001);
} completion: nil];
The above places the elements where they are supposed to after I shrink and grow my views, taking into account that is is only for animation purposes.
Hope it helps.

Intersection Of Two UIViewImages

How can I determine if two uiviewimages intersect. I want to do an auto snap feature. When the small image intersects with the big image or close to it (Lets says the distance<=x), I want the small image automatically snap(connect) to the big image at the point where they intersect.
CGRect bigframe = CGRectInset(bigView.frame, -padding.x, -padding.y);
BOOL isIntersecting = CGRectIntersectsRect(smallView.frame, bigFrame);
The previous two posters have been on the right track with CGRectIntersectsRect.
BOOL isIntersecting = CGRectIntersectsRect(smallImage.frame, largeImage.frame);
if(isIntersecting){
//Animate the Auto-Snap
[UIView beginAnimation:#"animation" context:nil];
[UIView setAnimationDuration:0.5];
smallImage.frame = largeImage.frame;
[UIView commitAnimations];
}
Basically what this says that if the two images are intersecting, the small image frame snap to the larger image over 0.5 seconds.
You don't have to animate it though; you could just make it instantaneous by removing all code except for smallImage.frame = largeImage.frame;. However, I recommend the animation way.
Hope this helps.
-------EDIT--------
You could use the code:
BOOL isIntersecting = CGRectIntersectsRect(smallImage.frame, largeImage.frame);
if(isIntersecting){
//Animation
[UIView beginAnimation:#"animation" context:nil];
[UIView setAnimationDuration:0.5];
smallImage.center = CGPointMake(largeImage.center.x, largeImage.center.y);
//If you want to make it like a branch, you'll have to rotate the small image
smallImage.transform = CGAffineTransformMakeRotation(30);
//The 30 is the number of degrees to rotate. You can change that.
[UIView commitAnimations];
}
Hope this fixed your problem. Remember to up vote and pick as the answer if this helped.
-------EDIT---------
One last thing. I said that the "30" in CGAffineTransformMakeRotation(30) stood for 30 degrees, but that is incorrect. The CGAffineTransformMakeRotation function takes its parameter in radians, so if you wanted 30 degrees you could do this:
#define PI 3.14159265
CGAffineTransformMakeRotation(PI/6);
You can use the CGRectIntersectsRect method to check for frames:
if (CGRectIntersectsRect(myImageView1.frame, myImageView2.frame))
{
NSLog(#"intersected")
}

Resources