iOS adding an animation with Pan Gesture Recognizer - ios

I'm trying to make an animation within a pan gesture recognizer. However, when the recognizer gets to my desired location on the screen, the UIStateGestureRecognizerStateEnded gets called, and my animation does not get fired properly. This is how I am going about doing that. How can I make it so that my animation is smooth and doesn't disrupt the gesture recognizer's state.
-(IBAction)handlePan:(UIPanGestureRecognizer *)recognizer
{
CGPoint translation = [recognizer translationInView:self.view];
recognizer.view.center = CGPointMake(recognizer.view.center.x, recognizer.view.center.y + translation.y);
self.lineView1.center = CGPointMake(self.lineView1.center.x, recognizer.view.center.y + translation.y - 337.5f);
self.lineView2.center = CGPointMake(self.lineView2.center.x, recognizer.view.center.y +translation.y + 337.5f);
[recognizer setTranslation:CGPointMake(0, 0) inView:self.view];
//THIS CODE IS NOT ALLOWING THE ANIMATION TO RUN SMOOTHLY? AND ENDS THE GESTURE'S STATE.
if ((recognizer.view.center.y + translation.y)>300 && (recognizer.view.center.y + translation.y)<345) {
[UIView animateWithDuration:3.5 delay:0 options:UIViewAnimationOptionAllowUserInteraction animations:^{
self.joinHorzConst.constant= 60;
self.joinLabel.alpha = 1;
} completion:^(BOOL finished) {
//completion
}];
}
if (recognizer.state == UIGestureRecognizerStateEnded) {
[UIView animateWithDuration:.35 delay:0 options:UIViewAnimationOptionCurveEaseInOut
animations:^{
recognizer.view.center = CGPointMake(recognizer.view.center.x, 121.5f);
self.lineView1.center = CGPointMake(281, -216);
self.lineView2.center = CGPointMake(281, 459);
} completion:^(BOOL finished) {
[UIView animateWithDuration:.2 animations:^{
recognizer.view.center = CGPointMake(recognizer.view.center.x, 147.5f);
self.lineView1.center = CGPointMake(281, -189);
self.lineView2.center = CGPointMake(281, 486); } completion:^(BOOL finished) {
[UIView animateWithDuration:.1 animations:^{
recognizer.view.center = CGPointMake(recognizer.view.center.x, 141.5f);
self.lineView1.center = CGPointMake(281, -196);
self.lineView2.center = CGPointMake(281, 479);
}];
}];
}];
}
}
Thanks in advance!
EDIT: Exactly how I want my animation to work:
user drags a recognizer up and down a y axis, like a slider. When the slider button reaches within certain y axises (300 - 345), I want there to be an animation on one of my labels that just pushes it to the left a bit and turns the alpha up.
How it is working right now:
Right now, the slider works fine, UNTILL i reach the y axis between 300-345, at that point my gestureReconizerStatedidEnd gets called and the slider returns to its original position with the desired animation. I dont want this to happen. I want the slider to keep moving freely, but the label animation to happen just as the slider is within those 300-345 dimensions.

The way I was setting the labels x value with constraints seemed to be disrupting the gesture? Not sure. But here is how it is working now.
if ((recognizer.view.center.y + translation.y)>300 && (recognizer.view.center.y + translation.y)<345) {
[UIView animateWithDuration:.3 delay:0 options:UIViewAnimationOptionAllowUserInteraction animations:^{
[self.joinLabel setCenter:CGPointMake(183, 324)];
self.joinLabel.alpha = 1;
} completion:^(BOOL finished) {
//completion
}];
}

Related

Resetting scale after transformation

_cardView =[card initCard];
[self.view addSubview:_cardView];
_cardView.transform=CGAffineTransformScale(CGAffineTransformIdentity, 0.1, 0.1);
[UIView animateWithDuration:0.3
animations:^{
_cardView.transform=CGAffineTransformIdentity;
}];
I have a view which I animate to make it seem as if it is expanding from a point (similar to the animation of an app being opened). This view is returned from [card initCard], with card being a custom class, and assigned to _cardView. Using CGAffineTransformScale I first decrease the scale and then animate the increase of the scale. This works perfectly for the first card that is shown. However, when _cardView is set to nil and a new card is assigned to it, the same transformation and animation code produce the wrong animation which makes the view increase in scale before decreasing. I assume that the problem is in making the scale 0.1,0.1 but I have not been able to solve this.
Setting to nil:
else if (sender.state == UIGestureRecognizerStateEnded || sender.state == UIGestureRecognizerStateCancelled || sender.state == UIGestureRecognizerStateFailed)
{
if(sender.view.center.x>0){
[UIView animateWithDuration:0.2 animations:^{
CGRect rect=[sender.view frame];
rect.origin.x=([self.view frame].size.width/2)-129.5;
[sender.view setFrame:rect];
sender.view.alpha = 0.8;
}];
}else{
[UIView animateWithDuration:0.5 animations:^{
_protoypeView.alpha=0.0;
_protoypeView=nil;
_cardView=nil;
[self nextCard];
}];
}
}
and function nextCard:
-(void)nextCard{
if(cardNumber>[questionArray count]-1){
NSLog(#"Out of cards");
}else{
QuestionCard *card=[[QuestionCard alloc]init];
NSArray* array =[[NSArray alloc]initWithArray:questionArray[cardNumber]];
card.questionString=array[3];
card.whenPosted=array[0];
card.isAnon=array[2];
card.user=array[1];
card.replies=array[4];
card.profileImg=array[5];
_cardView =[card initCard];
[self.view addSubview:_cardView];
_cardView.alpha=0.0;
_cardView.transform=CGAffineTransformScale(CGAffineTransformIdentity, -1, -1);
_cardView.transform=CGAffineTransformIdentity;
[UIView animateWithDuration:1 animations:^{
_cardView.alpha=0.7;
}];
UIPanGestureRecognizer * pan1 = [[UIPanGestureRecognizer alloc]initWithTarget:self action:#selector(handlePanImage:)];
pan1.minimumNumberOfTouches = 1;
[_cardView addGestureRecognizer:pan1];
yOfView=[_cardView frame].origin.y+([_cardView frame].size.height/2);
cardNumber++;
}
}
First case of CGAffineTransform (after successful query to Parse database):
[UIView animateWithDuration:0.5 animations:^{
_protoypeView.alpha=0.0;
}completion:^(BOOL finished){
QuestionCard *card=[[QuestionCard alloc]init];
NSArray* array =[[NSArray alloc]initWithArray:questionArray[cardNumber]];
card.questionString=array[3];
card.whenPosted=array[0];
card.isAnon=array[2];
card.user=array[1];
card.replies=array[4];
card.profileImg=array[5];
_cardView =[card initCard];
[self.view addSubview:_cardView];
_cardView.transform=CGAffineTransformScale(CGAffineTransformIdentity, 0.0, 0.0);
[UIView animateWithDuration:0.3
animations:^{
_cardView.transform=CGAffineTransformIdentity;
}];
UIPanGestureRecognizer * pan1 = [[UIPanGestureRecognizer alloc]initWithTarget:self action:#selector(handlePanImage:)];
pan1.minimumNumberOfTouches = 1;
[_cardView addGestureRecognizer:pan1];
yOfView=[_cardView frame].origin.y+([_cardView frame].size.height/2);
cardNumber++;
}];
Found the problem - [self nextCard] was called in animations, but should be called in completion.

Animation from Pan Gesture begins from initial state instead of current state

In my application I have a view that sits on top of another view. The user is able to swipe the top view to the right, which fades it out and "fades in" the back view.
When the user pans to the right and lets go, the animation kicks in and it should translate from its current position to the new off screen position. The problem is that the animation instead snaps the view back to its starting point and then does the animation.
Here is my code for the pan gesture and animation:
- (void)handlePanGesture:(UIPanGestureRecognizer *)recognizer
{
CGPoint translation = [recognizer translationInView:recognizer.view];
CGPoint velocity = [recognizer velocityInView:recognizer.view];
switch (recognizer.state) {
case UIGestureRecognizerStateBegan: {
[recognizer setTranslation:CGPointMake(self.slidingView.frame.origin.x, 0) inView:recognizer.view];
break;
}
case UIGestureRecognizerStateChanged: {
[self.slidingView setTransform:CGAffineTransformMakeTranslation(MAX(0,translation.x), 0)];
CGFloat percentage = fmaxf(0,(translation.x/recognizer.view.bounds.size.width));
[self.backgroundBlurImageView setAlpha:1-percentage];
[self.slidingView setAlpha:1-percentage];
[self.backgroundImageView setTransform:CGAffineTransformMakeScale(1 + (percentage * 0.05), 1 + (percentage * 0.05))];
[self.backgroundBlurImageView setTransform:CGAffineTransformMakeScale(1 + (percentage * 0.05), 1 + (percentage * 0.05))];
break;
}
case UIGestureRecognizerStateEnded:
case UIGestureRecognizerStateCancelled: {
if (velocity.x > 5.0 || (velocity.x >= -1.0 && translation.x > kMenuViewControllerMinimumPanDistanceToOpen * self.slidingView.bounds.size.width)) {
CGFloat transformedVelocity = velocity.x/ABS(self.slidingView.bounds.size.width - translation.x);
CGFloat duration = 0.66;
[self showViewAnimated:YES duration:duration initialVelocity:transformedVelocity];
} else {
[self hideViewAnimated:YES];
}
}
default:
break;
}
}
- (void)showViewAnimated:(BOOL)animated duration:(CGFloat)duration
initialVelocity:(CGFloat)velocity;
{
// animate
__weak typeof(self) blockSelf = self;
[UIView animateWithDuration:animated ? duration : 0.0 delay:0
usingSpringWithDamping:0.8f initialSpringVelocity:velocity options:UIViewAnimationOptionAllowUserInteraction animations:^{
blockSelf.slidingView.transform = CGAffineTransformMakeTranslation(blockSelf.slidingView.bounds.size.width, 0);
[blockSelf.backgroundBlurImageView setTransform:CGAffineTransformMakeScale(1.05, 1.05)];
[blockSelf.backgroundImageView setTransform:CGAffineTransformMakeScale(1.05, 1.05)];
[blockSelf.backgroundBlurImageView setAlpha:0];
[blockSelf.slidingView setAlpha:0];
} completion:^(BOOL finished) {
blockSelf.tapGestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(didTapOnAaron:)];
[blockSelf.view addGestureRecognizer:self.tapGestureRecognizer];
}];
}
- (void)hideViewAnimated:(BOOL)animated;
{
__weak typeof(self) blockSelf = self;
[UIView animateWithDuration:0.3f animations:^{
blockSelf.slidingView.transform = CGAffineTransformIdentity;
[blockSelf.backgroundBlurImageView setTransform:CGAffineTransformIdentity];
[blockSelf.backgroundImageView setTransform:CGAffineTransformIdentity];
[blockSelf.backgroundBlurImageView setAlpha:1];
[blockSelf.slidingView setAlpha:1];
} completion:^(BOOL finished) {
[blockSelf.view removeGestureRecognizer:self.tapGestureRecognizer];
blockSelf.tapGestureRecognizer = nil;
}];
}
I have already tried settings the animation options to:
UIViewAnimationOptionBeginFromCurrentState
with no effect.
Anyone have a clue what I am missing? Thanks
Kyle, sometimes this sort of thing happens if you are using autolayout. If you don't need it try disabling it and see if it fixes it.

UILabel move to point without snapping

I have a UILabel which I have added a GestureRecognizer to. I am able to drag it around the screen fine but what I am wanting to do is when the label is within an area I want it to stop dragging and have it move smoothly to a designated point within that area.
So if it's x value is greater than 150 move it to x of 200 and y of 150
It does work but it's popping to the location instead of being smoothly moved to the location.
Here is my drag method:
- (void)labelDragged:(UIPanGestureRecognizer *)gesture {
UILabel *label = (UILabel *)gesture.view;
CGPoint translation = [gesture translationInView:label];
// move label
label.center = CGPointMake(label.center.x + translation.x,
label.center.y + translation.y);
[gesture setTranslation:CGPointZero inView:label];
if (label.center.x > 150){
[label setUserInteractionEnabled:NO];
[UIView animateWithDuration:1.5 animations:^{
label.center = CGPointMake(200 , 150);
}];
}
}
Any help would be appreciated. I'm new to working with animation and points.
you have to wait till panGesture state.So condition required.While using pan gesture you removed userInteraction from label.By removing User Interaction it doesn't remove gesture from UILabel that's why this is happening.To work animation on UILabel you have to leave UILabel after dragging.Remove finger from label and it will animate.Use following code.
- (void)labelDragged:(UIPanGestureRecognizer *)gesture {
UILabel *label = (UILabel *)gesture.view;
CGPoint translation = [gesture translationInView:label];
// move label
label.center = CGPointMake(label.center.x + translation.x,
label.center.y + translation.y);
[gesture setTranslation:CGPointZero inView:label];
if (label.center.x > 150){
[label setUserInteractionEnabled:NO];
if(gesture.state == UIGestureRecognizerStateEnded)
{
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:#"position"];
[animation setFromValue:[NSValue valueWithCGPoint:label.center]];
[animation setToValue:[NSValue valueWithCGPoint:CGPointMake(200.0f, 400.0f)]];
[animation setDuration:2.0f];
[label.layer setPosition:CGPointMake(200.0f, 400.0f)];
[label.layer addAnimation:animation forKey:#"position"];
}
}
}
If you really want to cancel the UIPanGestureRecognizer while it is still being performed, you can do so. Set the enabled property to NO, and enable it back on completion of the animation.
if (label.center.x > 150){
gesture.enabled = NO;
[label setUserInteractionEnabled:NO];
[UIView animateWithDuration:1.5 animations:^{
label.center = CGPointMake(200 , 150);
} completion:^(BOOL finished) {
gesture.enabled = YES;
}];
}
But then once the label is snapped, you will not be able to drag it since the gesture will always be disabled due to the position of the label. It is better to snap the label when the gesture is ended.
if (gesture.state == UIGestureRecognizerStateEnded) {
//check the position and snap the label
}

My PanGestureRecognizer won't move when i want to resize it

I Have a UIPanGestureRecognizer. It works fine. I made an if statement so when someone touches the picture, it will be alpha 0.7 and it will be 1,5 times bigger. The alpha works fine, but when i type in the CAAffineTransformMakeScale method, my image won't move.
this is my code:
- (IBAction)Bloemen:(UIPanGestureRecognizer *)recognizer {
CGPoint translation = [recognizer translationInView:self.view];
recognizer.view.center = CGPointMake(recognizer.view.center.x + translation.x,
recognizer.view.center.y + translation.y);
[recognizer setTranslation:CGPointMake(0, 0) inView:self.view];
if (UIGestureRecognizerStateBegan)
{
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDelay:0.1];
[UIView setAnimationDuration:0.4];
bloemen.alpha = 0.7f;
bloemen.transform = CGAffineTransformMakeScale(1.5,1.5);
[UIView commitAnimations];
}
if (UIGestureRecognizerStateEnded) {
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDelay:0.1];
[UIView setAnimationDuration:0.1];
bloemen.alpha = 1.0f;
bloemen.transform = CGAffineTransformIdentity;
[UIView commitAnimations];
}
}
The critical issue is that your if statements are not checking the state property. It should be:
if (recognizer.state == UIGestureRecognizerStateBegan)
{
// began code here
}
else if (recognizer.state == UIGestureRecognizerStateEnded)
{
// ended code here
}
Also be aware that this gesture recognizer will only work if you have autolayout turned off. If you're using autolayout, you have to change the constraints.
If you forgive the stylistic observations, I might also be inclined to suggest:
use block-based animations;
not reference non-local variables if not needed (i.e. reference recognizer.view rather than bloemen), which makes it easier to reuse this handler for dragging and dropping of various UIView objects you choose to add this gesture to; and
use standard naming conventions, start method names with lowercase letter and follow the verbNoun convention.
None of this is critical and please use or disregard as you see fit, but this exhibits a few best practices:
- (void)handlePan:(UIPanGestureRecognizer *)recognizer
{
CGPoint translation = [recognizer translationInView:self.view];
recognizer.view.center = CGPointMake(recognizer.view.center.x + translation.x,
recognizer.view.center.y + translation.y);
[recognizer setTranslation:CGPointMake(0, 0) inView:self.view];
if (recognizer.state == UIGestureRecognizerStateBegan)
{
[UIView animateWithDuration:0.4
delay:0.0 // you had 0.1, but seems worthwhile to give immediate feedback
options:0
animations:^{
recognizer.view.alpha = 0.7f;
recognizer.view.transform = CGAffineTransformMakeScale(1.5,1.5);
}
completion:nil];
}
if (recognizer.state == UIGestureRecognizerStateEnded)
{
[UIView animateWithDuration:0.1
delay:0.0 // you had 0.1, but seems worthwhile to give immediate feedback
options:0
animations:^{
recognizer.view.alpha = 1.0f;
recognizer.view.transform = CGAffineTransformIdentity;
}
completion:nil];
}
}

Move & Rotate an UIImageView

I have an UIImageView with an rotation animation that loops, I need to move the UIImageView by x and y in the screen.
The rotation animation code is:
-(void)rotate{
if (leftRotate){
leftRotate = NO;
}else{
leftRotate = YES;
}
CGRect initFrame = self.frame;
[UIView animateWithDuration:0.5
delay:0.0
options:UIViewAnimationOptionRepeat|UIViewAnimationOptionAutoreverse
animations:^{
if (leftRotate){
self.transform = CGAffineTransformRotate(self.transform, -1 * (M_PI/8));
}else{
self.transform = CGAffineTransformRotate(self.transform, M_PI / 8);
}
}
completion:^(BOOL completed) {
if (completed){
if (!stopRotate){
[self rotate];
}
}
}];
}
I try differents approaches to move the image, like setting the center but I cant move it.
I found the way, I have to set the center out side the animation:
CGPoint facePosition = CGPointMake(self.face1.center.x+1,
self.face1.center.y);
self.face1.center = facePosition;

Resources