In my app I call this method to move a UIButton
- (void) buttonMovement{
[UIView animateWithDuration:0.8
delay:0.0
options:UIViewAnimationOptionAutoreverse | UIViewAnimationOptionRepeat | UIViewAnimationOptionCurveEaseInOut
animations:^{
// do whatever animation you want, e.g.,
play_bt.center = CGPointMake(play_bt.center.x, play_bt.center.y-20);
}
completion:NULL];
}
In this way my button done an "up&down" movement but if I "push" it, IBAction doesn't work. If I don't do this animation it work fine. Why?
If this animation is a problem to call the action, what's the way to do an animation that don't give me problems?
Another way is to move the button as an imageview and over I put an invisible button, but I lost the press effetcts, and it became all noise.
A lot of people are providing wrong information here. I tried to weigh in with comments. Now it's time to post a conclusive answer. To quote my answer to the "clicking an animating image/button" question linked by #Radu above:
The short answer is that you can't tap views while they are animating. The reason is that the views don't actually travel from the start to the end location. Instead, the system uses a "presentation layer" in Core Animation to create the appearance of your view object moving/rotating/scaling/whatever.
What you have to do is attach a tap gesture recognizer to a containing view that completely encloses the animation (maybe the entire screen) and then write code that looks at the coordinates of the tap, does coordinate conversion, and decides if the tap is on a view that you care about. If the tap is on a button and you want the button to highlight you'll need to handle that logic too.
I have a sample project on github that shows how to do this when you are doing both UIView animation and Core Animation with CABasicAnimation (which animates layers, not views.)
Core Animation demo with hit testing
A workaround is to implement touchesBegan instead of IBAction for your animating button.
You may want to check the answers given here:Clicking an animating image / button
- (void)touchesBegan:(NSSet*) touches withEvent:(UIEvent *) event{
// Get location of touched point
CGPoint point = [[touches anyObject] locationInView:self.view];
//
// Check if point is inside your button frame
// or compare it with the presentation layer
//
}
Related
In my swift game, we have a main game scene and then once you die in the game, it puts you on an endgame screen where your score is displayed. From this screen, the user then has the option to tap a button to change the scene back to the game. Most of the time, the button is removed and everything works normally but every so often it doesn't remove it and overlays itself over the game scene. This is the code ran by the button-
scoreLabel.removeFromSuperview()
highLabel.removeFromSuperview()
RestartBtn.removeFromSuperview()
self.RestartBtn.removeFromSuperview()
self.highLabel.removeFromSuperview()
self.menuButton.removeFromSuperview()
self.scoreLabel.removeFromSuperview()
self.scene!.view?.presentScene(GameplayScene(), transition: SKTransition.crossFadeWithDuration(0.8))
self.menuButton and all the others are probably weak references.
i.e., they become stale once the view has been removed from superview. It sometimes work, sometimes doesn't to the whim of how it is referenced in self.menuButton = <something>, which, if done in loadView or viewDidLoad, is just plain dangerous.
Different approach
I would recommend not juggling with the UI so much, and adopt a more constant referencing technique, such as hidden, or alpha = 0.
self.menuButton.removeFromSuperview()
// self.menuButton is stale. You can't re-add your button from that reference
Animate show/hide views
This will not destroy your references, and look oh so much better than removeFromSuperview:
// Hide with animation
[UIView animateWithDuration:.4f animations:^{
[self.menuButton setAlpha:0]; // Hide with animation
} completion:^(BOOL finished) {
// If you decide to remove the view permanently, do it here
// Without knowing more, I wouldn't recommend it
}];
You can then show the UI again, with or without animation:
[self.menuButton setAlpha:1]; // Show without animation
Update
The animation is working for setEnabled=NO.
The animation for setEnabled=YES is being triggered when UIScrollView is scrolling, the UIButton is inside the scrollview and the animation for setEnabled=NO is being triggered when UIScrollView is done scrolling.
So, I think the reason why animation for setEnabled=YES is not working is because the view is moving. I am not sure but this seems to be the only logical explanation from what I have found so far. I did a test with dispatch_after() and the animation worked for setEnabled too, in other words the animation is working if it is being triggered when the view is not moving.
What I need to do ?
I have two different background images for UIButton one for UIControlStateNormal and another for UIControlStateDisabled.
I want a effect where UIButton slowly transitions over from one state to another
What have I been doing ?
BOOL enableDisable = YES;
[UIView transitionWithView:((UIButton*)object)
duration:3.3
options:UIViewAnimationOptionTransitionCrossDissolve
animations:^{ [((UIButton*)object) setEnabled:enableDisable]; }
completion:nil];
The Problem
UIButton transforms to setEnabled=NO state over the duration but no matter what I put in the options setEnabled happens almost instantly.
is there something I am missing ?
Thanks in advance for your time and response.
Unfortunately, enable or disabled state for UIView aren't part of animatable properties in apple docs. The animatable properties are:
frame, center, bounds, transform, alpha, backgroundColor, contentStretch
Reference here: UIView animation
However if you want to create custom property for animation, you can have a look at this post which describes a way to achieve it. Create a custom animatable property
I can confirm that your code works as expected both for the transition
enabled: NO -> YES
and for the transition
enabled: YES -> NO
So, my guess is that something else is happening in your app that somehow interferes with the transition. Try defining a completion block like:
...
completion:^(BOOL finished) {
NSLog(#"FINISHED? %#", finished?#"YES":#"NO");
} ];
and add a trace log before the transitionWith call or inside the animation block to check how long the transition runs from start to completion.
My guess is that while the button is transitioning something else is happening that changes its state and as a secondary effect breaks the transition. I fear that without seeing more code, it will not be possible to help you further...
We have this animation for some bubble effect on a button , but it prevent him from being clickable. if we disable this animation he works.
Can we fix it to let it work with the animation ?
CGRect initi=[[initialPositions objectAtIndex:b.tag] CGRectValue];
int r1=arc4random()%4;
int r2=arc4random()%4;
initi.origin.x+=r1;
initi.origin.y+=r2;
[UIView animateWithDuration:0.8 delay:0.0 options:UIViewAnimationOptionCurveEaseInOut animations:^
{
b.frame=initi;
}
completion:^(BOOL completed){ }];
As far as I know, the button is not actually disabled or unclickable during the animation. When the animation starts the button's touch area is immediately moved to the position where the button will be when the animation ends. This makes it feel like the button were unclickable during the animation.
From touch logic's point of view, the button is just moved from A to B and animation between A and B is just eye candy. This explains why touching doesn't work between the points.
The only working solution to this is to roll your own animation routine. If you want to take this route, this post called The Holy Grail of iOS Animation Intervals? by Ben Bojko should point you to the right direction.
As Markus says in his answer (voted) the button actually moves to it's end position at the beginning of the animation, and will accept clicks there as soon as the animation starts. The animation actually takes place in view hierarchy's "presentation layer".
You can use Markus' suggestion of doing your own animation, but that is processor-intensive and not as smooth as layer-based animation (which is how UIView animation works under the covers.)
If you want to use UIView animation and have your button respond to clicks while the animation is in-flight then you need to make the superview that bounds the entire animation clickable, and implement hit testing on the button's presentation layer to figure out if the tap hits the button or not. (the superview takes the tap, then checks it's coordinates to see if it is inside the buttons' presentation layer using the hitTest: method.)
I have a project on github called iOS CAAnimationGroup demo (link) that shows how to do this, both for CAAnimation based animation and UIView-based animation.
UI animations are great, very easy to use, and are used allot. The only one problem I have with it is that while the animation is moving, the view in not receiving any user interaction.
For example, if a have a UIButton that animates every time it's shown, but the user will not be able to click on it until the animation is over.
//This is a UIButton:
- (void)animationApear
{
CGRect frameSelf = self.frame;
frameSelf.origin.y -= frameSelf.size.height;
[UIView animateWithDuration:1.0 delay:0
usingSpringWithDamping:0.8 initialSpringVelocity:0
options:0
animations:^{
[self setFrame:frameSelf];
} completion:nil];
}
Is there any way to deal with this issue?
Thanks!
You need to supply the option UIViewAnimationOptionAllowUserInteraction.
Also, depending on your view architecture, if the button is within the animated view, or a subview thereof, then the actual location of the button isn't moving. Only the presentation layer of the button is moving, so that is why the button may not be receiving taps. A good test is to tap where the button was when the animation started (and make sure the option UIViewAnimationOptionAllowUserInteraction is on) to see if it is still receiving taps.
On solution, when you actually need to animate buttons, is to make repeated short transforms (CGAffineTransforms, for example) and have those movements in aggregate, create the visual effect of the animation. Though in this case the button itself will move, rather than simply it's presentation.
I have a question regarding Xcode.
I found a tutorial on the net that allowed me to move an image around the screen.
Could you please explain how I can make my button move to the left and back to right with one click (I can't find this anywhere...), and immediately after that run another IBAction that allows me to switch to another subview? (I already have this code naturally...). I tried to add both IBActions in 1 centralized one, but it didn't seem to work :-( It opens in this case immediately the subview without showing me the animation.
What I tried:
The code obtained until now:
-(IBAction) aMove: (id) sender{
if (bMove == NO) {
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:1.0];
btnTarget.transform = CGAffineTransformMakeTranslation(-30,0);
[UIView commitAnimations];
bMove = YES;
}else{
btnTarget.transform = CGAffineTransformIdentity;
bMove = NO;
}
}
-(IBAction) aAnimateActivate: (id) sender {
[self aMove:nil];
[self targetOpenView:nil]; //Opens the subview
}
I appreciate your help! Thanks!
concatenate works to combine animations and I just put two blocks of animations (two of the one shown below) after each other... :
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:.3];
[UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];
CGAffineTransform transform = CGAffineTransformConcat(CGAffineTransformMakeScale(1, 1),CGAffineTransformMakeTranslation(10, -50));
btnGuide.transform = transform;
[btnGuide setAlpha:0.0];
[UIView commitAnimations];
Basically implementing this code did the trick... Easy!
However... I needed some additional pieces of code for the further implementation such as:
[self performSelector:#selector(aGuide) withObject:self afterDelay:0.0];
(don't mind the selector name)
and:
[NSTimer scheduledTimerWithTimeInterval:.5 target:self selector:#selector(targetOpenView:) userInfo:nil repeats:NO];
Without looking at all of your program flow nor doing any actual testing (and I find when using Core Animation that the only way to be sure it works right is to code it and see if it does) the problem with the subview opening right away occurs because right after you invoke your "aMove" method to set up the first animation the thread continues with the next line of code, i.e., the [self] targetOpenView:nil statement, which immediately opens the subview and thus doesn't allow the first animation sequence to be shown. There is no pause to wait for the first animation to be completed. The animation, once commited, runs on its own thread while your code continues to run on the current thread (probably the application's main thread). That might not seem to be the most sensible way but you have to think of the code you write as the process to set up an animation that, once committed, is a separate entity that is free to run on its own (beside your code). The advantage to Apple's implementation is that you can set up a whole bunch of different animations which occur at the same time. One of Core Animation's design goals is to take away the need for the programmer to be handling all the starting and stopping of various animations and instead let the animation coordination be done using various methods of delay and duration or providing the means for one animations events to be observed (and acted upon) by other animations.
In order to do the animation(s) the way you want you will need use a method which only allows the second animation to begin once the first is over. One way (assuming that the subview change would be set up as an animation itself) is to use a completion: handler, an animation block that only begins upon completion of the first animation. Another way is to let the two animations "start together" but include a delay: parameter in the second animation that is equal to the length of the first animation. If the subview change is not done with an animation but is just done with code in the main thread then you need to set up a an animation delegate that is called when certain events occur in your animation, one of which is it's completion. Parameter(s) are passed to your delegate to tell you what is occurring and to which animation.
All of this is discussed, with examples, in the Animations section of the View Programming Guide to iOS (about 10 pages that will probably show you almost exactly how to do what you want):
http://developer.apple.com/library/ios/#documentation/WindowsViews/Conceptual/ViewPG_iPhoneOS/AnimatingViews/AnimatingViews.html#//apple_ref/doc/uid/TP40009503-CH6
Also, in order to set up the first animation to move the button somewhere and back again you might want to read the subtopic in that same section of the guide mentioned above: Implementing Animations That Reverse Themselves. I think it would be the cleanest way to do what you want.
(FYI, I'm better with the MacOS side of Core Animation than the iOS side but the "why did this happen immediately?" problem you have is a common pitfall when getting up to speed with how it works. Hope this explanation helps.)