When the uibutton wobbles it pushes uibuttons near itself - ios

I added wobble function to my 2 buttons. The startWobble and stopWobble methods work properly:
- (void)startWobble {
_hint1.transform = CGAffineTransformRotate(CGAffineTransformIdentity, RADIANS(-10));
_hint2.transform = CGAffineTransformRotate(CGAffineTransformIdentity, RADIANS(-10));
[UIView animateWithDuration:0.001
delay:0.0
options:(UIViewAnimationOptionAllowUserInteraction | UIViewAnimationOptionRepeat | UIViewAnimationOptionAutoreverse)
animations:^ {
_hint1.transform = CGAffineTransformRotate(CGAffineTransformIdentity, RADIANS(10));
_hint2.transform = CGAffineTransformRotate(CGAffineTransformIdentity, RADIANS(10));
}
completion:NULL
];
}
- (void)stopWobble {
[UIView animateWithDuration:0.25
delay:0.0
options:(UIViewAnimationOptionAllowUserInteraction | UIViewAnimationOptionBeginFromCurrentState | UIViewAnimationOptionCurveLinear)
animations:^ {
_hint1.transform = CGAffineTransformIdentity;
_hint2.transform = CGAffineTransformIdentity;
}
completion:NULL
];
}
However, I tested my app on the simulator, and when the buttons start to wobble they push the other buttons near themself.The buttons then stop to wobble everything looks fine again. I can't figure the problem out. I've used these methods and never faced a problem like this before.
Any suggestions?

Related

How to repeat shake animation indefinitely or X times using animateWithDuration?

The code below came from this SO question: UIView shake animation.
This shake animation is perfect, except it stops after one iteration. How do you repeat the animation indefinitely, or how do you cause it to repeat it X times?
There are other SO answers on repeating UIView animations that suggest using CAKeyframeAnimation or CABasicAnimation, but this question is different. The goal is to reproduce this exact animation, with the "springy" effect from usingSpringWithDamping and initialSpringVelocity.
Using Autoreverse and Repeat don't reproduce the desired effect because the the initial translation is outside the animation block.
view.transform = CGAffineTransformMakeTranslation(20, 0);
[UIView animateWithDuration:0.4 delay:0.0 usingSpringWithDamping:0.2 initialSpringVelocity:1.0 options:UIViewAnimationOptionCurveEaseInOut animations:^{
view.transform = CGAffineTransformIdentity;
} completion:nil];
If you need exact same code few times than put that code in method and do recursion, something like that:
- (void)animateCount:(NSInteger)count {
if (count == 0) {
return;
}
view.transform = CGAffineTransformMakeTranslation(20, 0);
[UIView animateWithDuration:0.4 delay:0.0 usingSpringWithDamping:0.2 initialSpringVelocity:1.0 options:UIViewAnimationOptionCurveEaseInOut animations:^{
view.transform = CGAffineTransformIdentity;
} completion:^{
count--;
[self animateCount:count];
}];
}
Use (UIViewAnimationOptionAutoreverse | UIViewAnimationOptionRepeat) in options to repeat it indefinitely
[UIView animateWithDuration:0.4 delay:0.0 usingSpringWithDamping:0.2 initialSpringVelocity:1.0 options:(UIViewAnimationOptionAutoreverse | UIViewAnimationOptionRepeat | UIViewAnimationOptionCurveEaseInOut) animations:^{
someView.frame = someFrame1;
} completion:^(BOOL finished) {
[UIView animateWithDuration:0.5f animations:^{
someView.frame = someFrame2;
} completion:nil];
}];

Commit A Different Animation(s) While Another Animation is ending

I'm trying to create a 'wave' effect of UIView animations.
I have numerous layers to animate. I'm currently animating them with animateWithDuration:delay:options:animations:completion:
I have the views displayed in a stack so for example:
-------------------------------
| VIEW 1 |
-------------------------------
| VIEW 2 |
-------------------------------
| VIEW 3 |
-------------------------------
| VIEW 4 |
-------------------------------
| VIEW 5 |
-------------------------------
| VIEW 6 |
-------------------------------
This is important to know because UIView transitions are not what I'm going after.
I can easily animate them and concurrently activate the next view animation in the completion block of the previous view. So this is how it's currently set up:
[UIView animateWithDuration:0.4f animations:^{
self.view1.alpha = 1.0;
} completion:^(BOOL finished) {
[UIView animateWithDuration:0.4f animations:^{
self.view2.alpha = 1.0;
} completion:^(BOOL finished) {
etc...
}];
}];
What i would like to do, instead of concurrently activating the next view animation I would like it to activate while the previous views animation is ending. So say, the last half of time in the animation process, start the next views animation to get a wave effect. None of the UIViewAnimationOptions match my criteria, and I've also researched the developer forums for a similar goal, with no results.
What I have tried:
The workaround that I've tried is adding a delay to the next UIView animations inside the animations block as opposed to the completion block, but I feel this isn't a healthy take on the matter. Ex:
[UIView animateWithDuration:0.4f animations:^{
//Other animations...
self.view1.alpha = 1.0f
[UIView animateWithDuration:0.4f delay:0.2f options:UIViewAnimationOptionAllowUserInteraction animations:^{
self.view2.alpha = 1.0f;
} completion:^(BOOL finished) {
//repeat..
}];
}];
As you can see my work around, well, works. But as I stated above, it doesn't feel an iota of healthy. What am I missing?
EDIT
I inevitably went the route of using UIViewAnimationOptions by delaying and autoreversing the durations:
NSTimeInterval duration = 3.0;
NSTimeInterval delay = 0.5f;
[UIView animateWithDuration:duration delay:0.0f options:(UIViewAnimationOptionRepeat | UIViewAnimationOptionAutoreverse | UIViewAnimationOptionAllowUserInteraction) animations:^{
self.view1.alpha = 1.0f;
} completion:^(BOOL finished) {
}];
[UIView animateWithDuration:duration delay:delay options:(UIViewAnimationOptionRepeat | UIViewAnimationOptionAutoreverse | UIViewAnimationOptionAllowUserInteraction) animations:^{
self.view2.alpha = 1.0f;
} completion:^(BOOL finished) {
}];
[UIView animateWithDuration:duration delay:delay*2 options:(UIViewAnimationOptionRepeat | UIViewAnimationOptionAutoreverse | UIViewAnimationOptionAllowUserInteraction) animations:^{
self.view3.alpha = 1.0f;
} completion:^(BOOL finished) {
}];
etc...
You can use CALayer animation instead of UIView animation to achieve this. Just configure the begin time, duration and the animation on each view's layer. Here is a quick Swift example:
for (var i, v) in enumerate(views) {
let animation = CABasicAnimation(keyPath:"opacity")
animation.toValue = 0.0
animation.duration = 0.4
animation.beginTime = CACurrentMediaTime() + 0.2 * i
v.layer.addAnimation(animation, forKey: "wave")
}

How to do text field shake effect?

I'm trying to imitate the effect at login in Mac OSX: when you put a wrong password the textfield begin a nice shake effect. I tried this but I'm not satisfied.
-(void)shakeText:(UITextField*)textField
{
[UIView animateWithDuration:0.25
delay:0.0
options:UIViewAnimationOptionAutoreverse
animations:^{textField.transform =CGAffineTransformMakeTranslation(5, 0);}
completion:^(BOOL finished) {
[UIView animateWithDuration:0.5
delay:0.0
options:UIViewAnimationOptionAutoreverse
animations:^{textField.transform = CGAffineTransformMakeTranslation(5, 0);}
completion:nil];
}];
}
Is there a function in iOS to do it properly?
Please try the following code:
// Change the 10 & -10 values as you wish
CGAffineTransform leftWobble = CGAffineTransformRotate(CGAffineTransformIdentity, RADIANS(-10.0));
CGAffineTransform rightWobble = CGAffineTransformRotate(CGAffineTransformIdentity, RADIANS(10.0));
textField.transform = leftWobble;
[UIView animateWithDuration:0.25
delay:0
options:(UIViewAnimationOptionRepeat | UIViewAnimationOptionAutoreverse) animations:^{
[UIView setAnimationRepeatCount:6]; // Change this value as you want
textField.transform = rightWobble;
} completion:^(BOOL finished){
textField.transform = CGAffineTransformIdentity;
}];

Reset animation state during user interaction

I have a circle that users can touch, when you touch it it changes size then reverts back to its original size to indicate you touched it. I want users to be able to touch it as often as they like regardless of whether its currently animating or not. I updated my animation to use the UIViewAnimationOptionAllowUserInteraction flag which does allow me to touch it during the animation but it doesnt behave as I'd expect.
I think I need to somehow stop the currently playing animation, reset the size to normal and then play it again, is this correct and if so how do I do it? If not what do I do?
- (void)pop{
[view.layer removeAllAnimations];
[UIView animateWithDuration:0.2
delay:0.0
options:UIViewAnimationOptionAutoreverse | UIViewAnimationOptionCurveEaseOut | UIViewAnimationOptionAllowUserInteraction // reverse back to original value
animations:^{
// scale down 20%
self.transform = CGAffineTransformMakeScale(0.8, 0.8);
} completion:^(BOOL finished) {
self.transform = CGAffineTransformMakeScale(1, 1);
}];
}
self.btn = [[UIButton alloc] initWithFrame:CGRectMake(200, 200, 100, 30)];
[self.btn addTarget:self action:#selector(tapDown:) forControlEvents:UIControlEventTouchDown];
[self.btn addTarget:self action:#selector(tapUp:) forControlEvents:UIControlEventTouchUpInside];
- (IBAction)tapDown:(id)sender
{
[UIView animateWithDuration:0.2
delay:0.0
options: UIViewAnimationOptionCurveEaseOut | UIViewAnimationOptionAllowUserInteraction
animations:^{
// scale down 20%
self.btn.transform = CGAffineTransformMakeScale(0.8, 0.8);
} completion:nil];
}
- (IBAction)tapUp:(id)sender {
[UIView animateWithDuration:0.2
delay:0.0
options:UIViewAnimationOptionCurveEaseOut | UIViewAnimationOptionAllowUserInteraction // reverse back to original value
animations:^{
// scale up to 100%
self.btn.transform = CGAffineTransformMakeScale(1, 1);
} completion:nil];
}
Sure this is not what you want? When the player taps and pop is called the animation should start over?
- (void)pop{
self.transform = CGAffineTransformMakeScale(1, 1); // If you want the animation to start over you need to reset the size before staring it again
[view.layer removeAllAnimations];
[UIView animateWithDuration:0.2
delay:0.0
options: options:UIViewAnimationOptionAutoreverse | UIViewAnimationOptionCurveEaseOut| UIViewAnimationOptionAllowUserInteraction // reverse back to original value
animations:^{
// scale down 20%
self.transform = CGAffineTransformMakeScale(0.8, 0.8);
} completion:^(BOOL finished) {
self.transform = CGAffineTransformMakeScale(1, 1);
}];
}

Animating UIView alpha according to views location

I am animating a view using this code but would like to set the alpha of the orb according to its location . is this possible as I understand that the animation block is executed immediately and I have no access to its location
self.orbView.center = self.arrowView.center;
self.orbView.alpha = 1;
[UIView animateWithDuration:1.0 delay:0 options:UIViewAnimationOptionAutoreverse | UIViewAnimationOptionRepeat animations:^{
self.orbView.center = CGPointMake(arrowCornerRight,self.orbView.center.y);
self.orbView.alpha = 0;
} completion:^(BOOL finished) {
}];

Resources