Animate center point of a view along a quadratic curve - objective-c

I have an app that stores user events in history. The events are added on one controller and are displayed in a controller within a different tab.
I would like to add a visual confirmation that an event has been recorded when a user taps "save button". I'm thinking of animating an interface element to move towards a tab bar controller that is responsible for showing records to the user.
In order to do so, I'm thinking of animating the center point of one of my interface elements. I know how to animate the center point, but it does so in a straight line. How can I animate the center point in a more "curved" way? Is there some way to accomplish this?
CGPoint center = self.ratingReticle.center;
[UIView animateWithDuration:0.6 delay:0 options:UIViewAnimationCurveEaseIn animations:^{
//move the element offscreen
self.ratingReticle.center = CGPointMake(260,500);
} completion:^(BOOL finished) {
//hide the interace element once it is offscreen
self.ratingReticle.alpha = 0;
//restore the position of the interface element
self.ratingReticle.center = center;
[ UIView animateWithDuration:0.4 animations:^{
//fade the element back at the original position
self.ratingReticle.alpha = 1;
} completion:^(BOOL finished) {
}];
}];

You could that using Core Animation to move your object along a Bezier curve using CAKeyFrameAnimation.
-(void)animate
{
CAKeyframeAnimation *posAnim = [CAKeyframeAnimation animationWithKeyPath:#"position"];
posAnim.path = [self bezierPath];
[image.layer addAnimation:posAnim forKey:#"posAnim"];
}
where bezierPath returns a CGPathRef that suits your case.

Related

iOS why my UIImageView within collection view cell does not respond to scale transform properly?

I got a UICollectionViewCell with an icon within it. I would like the cell to visually indicate that the user has tapped on it.
I'm trying UIView's animateWithDuration: and am trying to scale the icon's transform by 1.2 to make it larger, then set it to identity to make it original size. I've used such code before with regular images, and it seemed to work.
I get strange behavior where the image within a cell scales up, as if it is pinned by the top left corner. However, when it is scaling back to identity, it does so as if it scaling from the center (as you would expect it to behave). The result is that there's an unpleasant shift of the image center from one position to the next in between two animations.
How can I properly scale images within UICollectionViewCells?
-(void)setSelected:(BOOL)selected
{
[super setSelected:selected];
if(selected)
{
//I tried this, does not seem to do anything
self.icon.layer.anchorPoint = CGPointMake(0.5, 0.5);
[UIView animateWithDuration:0.3 animations:^{
self.icon.transform = CGAffineTransformMakeScale(1.2, 1.2);
} completion:^(BOOL finished) {
[UIView animateWithDuration:0.4 animations:^{
self.icon.transform = CGAffineTransformIdentity;
}];
}];
DLog(#"selected");
}else
{
DLog(#"unselected");
}
}

Scaling UIView using transform after animating frame change causes UIView to jump back to original frame before scaling

I'm trying to scale a UIView (with animation) after I move it (with animation). The problem is, when the scaling animation begins, it jumps back to the original position. Why?
[UIView animateWithDuration:t delay:0 options:UIViewAnimationOptionCurveEaseIn animations:^{
// Drop the ball
CGRect frame = coinView.frame;
frame.origin.y += d;
coinView.frame = frame;
coinView.shouldSparkle = NO;
} completion:^(BOOL finished) {
[UIView animateWithDuration:0.3 animations:^{
// Initial scale up for ball "poof"
coinView.transform = CGAffineTransformScale(coinView.transform, 1.5, 1.5);
} completion:^(BOOL finished) {
[UIView animateWithDuration:0.3 animations:^{
coinView.transform = CGAffineTransformScale(coinView.transform, 0.000001, 0.000001);
} completion:^(BOOL finished) {
[coinView removeFromSuperview];
}];
}];
}];
EDIT: This is how I generated my d:
static CGFloat groundPositionY = 325;
CGRect convertedFrame = [coinView.superview convertRect:coinView.frame toView:self.view];
CGFloat d = groundPositionY - CGRectGetMaxY(convertedFrame);
EDIT2: Okay, so I changed the second UIView animation to the following and I discovered that the jump (and the scale down) happens before the second animation occurs, i.e. when animateWithDuration:delay:options:animations:completion: is called.
[UIView animateWithDuration:5 delay:3 options:0 animations:^{
coinView.transform = CGAffineTransformScale(coinView.transform, 1.5, 1.5);
} completion:nil];
I tested your code and it works fine for me. How do you generate your d? and on which block exactly it goes back?
I found the problem...
The coinView is a subview of a child view controller's view. The problem comes from the fact that I overrode that child view controller's viewDidLayoutSubviews method to lay out all the coinView's, so whenever that method was called, the coin view would move back to its original position and size intended by the child view controller.
Thanks for all of your help, repoguy and sergio!!

UIView animation not working with addSubview

I have an iPad app that is displaying a "note" (subclassed UILabel) with text on it. In order to navigate to the next note, I'd like it to slide it off the screen to the left while having the next one slide in from the right. I can get either animation to work, but not both at the same time.
Here's the code in my controller:
- (void)slideOutLeft {
// create the new note
flSlidingNote *newNote = [[flSlidingNote alloc] init];
newNote.text = #"blah blah";
CGRect newFrame = CGRectMake(1000, 70, 637, 297); // off the right
newNote.frame = newFrame;
[self.view addSubview:newNote];
// slide off the current one
CGRect currentFrameEnd = noteLabel.frame; // noteLabel is the existing note
currentFrameEnd.origin.x = 0 - noteLabel.frame.size.width; // off the left
[UIView animateWithDuration:1.0 animations:^{
noteLabel.frame = currentFrameEnd;
} completion:nil];
}
noteLabel does not animate at all. If I comment out the addSubview:newNote part it does. I'm still relatively new at this, so it's probably just something simple.
The problem happens whether newNote is animated or not (not animated in the code snippet).
You want to put the animation for both your views and the addSubview call for your new view in one animation block, like so:
// layout the new label like the old, but 300px offscreen right
UILabel * newNote = [[UILabel alloc] initWithFrame:CGRectOffset(self.noteLabelView.frame, 300, 0)];
newNote.text = #"NEW NOTE";
[UIView animateWithDuration:2.0f
delay:0
options:UIViewAnimationCurveEaseInOut
animations:^{
// animate the old label left 300px to offscreen left
self.noteLabelView.center = CGPointMake(self.noteLabelView.center.x-300, self.noteLabelView.center.y);
// add the new label to the view hierarchy
[self.view addSubview:newNote];
// animate the new label left 300px into the old one's spot
newNote.center = CGPointMake(newNote.center.x-300, newNote.center.y);
}
completion:^(BOOL finished) {
// remove the old label from the view hierarchy
[self.noteLabelView removeFromSuperview];
// set the property to point to the new label
self.noteLabelView = newNote;
}];
In this snippet above, I'm assuming the old label is addressable via the property self.noteLabelView.
(Also have a look at https://github.com/algal/SlidingNotes , though I can't promise that will stay there long so maybe SO isn't the right format for such a link? )
Adding to what Gabriele Petronella commented, here is a good resource on KeyFrames and AnimationGroup, with a sample GitHub project linked:
http://blog.corywiles.com/using-caanimationgroup-for-view-animations
This really helped me understand animations better.
not sure but i've seen other code that loops through subviews and performs animations, you may want to try the following:
for(UIView *view in self.subviews)
{
// perform animations
}

How to animate a UIView going off the screen to the right, and then reappearing back from the left

I have a view that is contained in the main view of my application (a square in the middle of a screen). I've written the following code that slides the view off the screen to the right, if you swipe it to the left:
- (IBAction)swipeLeft:(id)sender
{
CGRect initCardViewFrame = self.cardView.frame;
CGRect movedCardToRightViewFrame = CGRectMake(initCardViewFrame.origin.x + 1000, initCardViewFrame.origin.y, initCardViewFrame.size.width, initCardViewFrame.size.height);
[UIView animateWithDuration:0.7
animations:^{self.cardView.frame = movedCardToRightViewFrame;}
completion:nil];
}
This works great, however I would like to extend the code such that once the square goes off the right side of the screen, it comes right back from the left side back to the middle. I'm not really sure how to "re-draw" it on the left outside of the window, and then slide it back in the middle. I've tried just redoing the frames, but the animations only show the last frame movement in the animation block. I'm assuming that I need to push the view off the screen, then redraw it on the outside of the left hand of the screen, then slide it in the middle. Unless there is a more intuitive way to do it of course.
Update:
I've answered this question below, but I can't help but think there is a better way to do this without nesting UIView animations inside each other. Is this the only way to tackle this problem?
I ended up adding the following code inside the "completion" parameter:
completion:^(BOOL finished)
{
self.cardView.frame = moveCardToLeftViewFrame;
[UIView animateWithDuration:0.4
animations:^{self.cardView.frame = initCardViewFrame;}
completion:nil];
}];
Note* moveCardToLeftViewFrame is a CGRect that places the view to the left of the visible window. So after it slides to the right, it is placed outside of screen to the left, then slides back into the middle.
you can use this code in case :
view will go out off screen to Left and Get In From Right.
view will go out off screen to Right and Get In From Left.
code sniped:
- (void)animateViewWithTransformation:(MyEnumAnimationTransformation)animationTransformation withDuration:(CGFloat)duration {
CGFloat goOutOffScreenToX, cameInToScreenFromX;
switch (animationTransformation) {
case goOutToLeftGetInFromRight:
goOutOffScreenToX = -1 * [UIScreen mainScreen].applicationFrame.size.width;
cameInToScreenFromX = [UIScreen mainScreen].applicationFrame.size.width;
break;
case goOutToRightGetInFromLeft:
goOutOffScreenToX = [UIScreen mainScreen].applicationFrame.size.width;
cameInToScreenFromX = -1 * [UIScreen mainScreen].applicationFrame.size.width;
break;
default:
break;
}
CGRect originViewFrame = self.frame;
[UIView animateWithDuration:duration
animations:^{[self setX:goOutOffScreenToX];}
completion:^(BOOL finished)
{
[self setX:cameInToScreenFromX];
[UIView animateWithDuration:duration
animations:^{[self setX:originViewFrame.origin.x];}
completion:nil];
}];
}

Objective-C: Issue with CGRect .frame intersect/contains

I have two UIImageViews located about center of a horizontal screen and when the user clicks a button another UIImageView, located off screen, slides in from the right. I'm am just trying to detect when the view being brought onto the screen collides with the two static views. The problem is when I run my code and check the CGRect frames they are returned from where the views start, not end, regardless of where I place the frame calls in my method or even if I place them outside the method in a separate one. I'm a bit new to Obj-C and I understand that Core Animation runs on a separate thread and I am guessing that is why I am getting the starting values. (Correct me if I am wrong here).
So, I guess the question here is how do I detect collisions when one item is static and another is animated. Here's my code (feel free to clean it up):
- (IBAction)movegirl
{
//disabling button
self.movegirlbtn.enabled = NO;
//getting graphics center points
CGPoint pos = bushidogirl.center;
CGPoint box1 = topbox.center;
CGPoint box2 = bottombox.center;
//firing up animation
[UIView beginAnimations: nil context: NULL];
//setting animation speed
[UIView setAnimationDuration:2.0 ];
//setting final position of bushidogirl, then resetting her center position
pos.x = 260;
bushidogirl.center = pos;
//running animations
[UIView commitAnimations];
//playing gong sound
[self.audioplayer play];
//enabling button
self.movegirlbtn.enabled = YES;
[self collisiondetection: bushidogirl : topbox];
}
- (void)collisiondetection:(UIImageView *)item1 : (UIImageView *)item2
{
CGRect item1frame = item1.frame;
CGRect item2frame = item2.frame;
NSLog(#"Item1 frame = %d and Item 2 frame = %d", item1frame, item2frame);
}
How about:
if (CGRectIntersectsRect(item1frame, item2frame))
{
// collision!!
}