I am having this hair pulling problem, i hope anybody will be able to help me out. it seems like a little thing but i have not been able to find any solution.
I have a UIButton that i want to move around with a animation block. Everytime i click the button i have it move to a new location, the button always moves to the correct location the problem is that it always moves from its first location to its new location.
lets say that i have a button at (0,0) i then move it to (0,50) works like a beaut!
Now i want to move the button to (0,100) and i expect it to animate from (0,50) to (0,100) but what it does it animate from (0,0) to (0,100) so it ends up in the right location, but moves FROM the wrong location.
i move the UIButton by setting a a new frame in a animation block
[UIView animateWithDuration:0.5f
delay:0.0f
options:UIViewAnimationCurveLinear
animations:^{
}
completion:nil];
in my case i move the button in a completion block when other controls a finished moving.
i really hope somebody out there knows exatly what im talking about and have the answer for me.
the code i use for animating the uibutton is this, the buttons name is "btnAddPhoneNumber" and its also the "sender"
[UIView animateWithDuration:1
delay:0
options:UIViewAnimationCurveLinear
animations:^{
txtNewPhoneNumber.frame = CGRectMake(newXnumber, newYnumber, txtPhoneNumber.frame.size.width, txtPhoneNumber.frame.size.height);
}
completion:^(BOOL finished) {
txtNewPhoneNumber.placeholder = #"here you go!";
//animate add textfield button.
[UIView animateWithDuration:0.5f
delay:0
options:UIViewAnimationCurveLinear
animations:^{
[sender setFrame:CGRectMake(btnAddPhoneNumber.frame.origin.x, newYnumber, btnAddPhoneNumber.frame.size.width, btnAddPhoneNumber.frame.size.height)];
txtNewPhoneNumberTitle.frame = CGRectMake(newXtitle, newYtitle, txtPhoneNumberTitle.frame.size.width, txtPhoneNumberTitle.frame.size.height);
[btnDelete setFrame:btnDeleteRect];
}
completion:^(BOOL finished) {
}];
}];
thank you for reading my question, and for helping me find a solution.
EDIT (added code example)
Try adding UIViewAnimationOptionBeginFromCurrentState to the options of the second animation, see if it solves your problem.
[UIView animateWithDuration:0.5f
delay:0.0f
options:UIViewAnimationCurveLinear | UIViewAnimationOptionBeginFromCurrentState
animations:^{
}
completion:nil];
Another way to go would be to set the frame of the button again (to the same value) in the completion handler of the second animation.
Check this work around. As I mentioned in comments, you can use this as your last option. Not a recommended approach.
[UIView animateWithDuration:1
delay:0
options:UIViewAnimationCurveLinear
animations:^{
txtNewPhoneNumber.frame = CGRectMake(newXnumber, newYnumber, txtPhoneNumber.frame.size.width, txtPhoneNumber.frame.size.height);
}
completion:^(BOOL finished) {
txtNewPhoneNumber.placeholder = #"here you go!";
[self performSelector:#selector(callSecondAnimation) withObject:nil afterDelay:1.5f)]; //or 0.5f
}];
- (void)callSecondAnimation {
//animate add textfield button.
[UIView animateWithDuration:0.5f
delay:0
options:UIViewAnimationCurveLinear
animations:^{
[sender setFrame:CGRectMake(btnAddPhoneNumber.frame.origin.x, newYnumber, btnAddPhoneNumber.frame.size.width, btnAddPhoneNumber.frame.size.height)];
txtNewPhoneNumberTitle.frame = CGRectMake(newXtitle, newYtitle, txtPhoneNumberTitle.frame.size.width, txtPhoneNumberTitle.frame.size.height);
[btnDelete setFrame:btnDeleteRect];
}
completion:^(BOOL finished) {
}];
}
Update:
Try this first,
[UIView animateWithDuration:1
delay:0
options:UIViewAnimationCurveLinear
animations:^{
txtNewPhoneNumber.frame = CGRectMake(newXnumber, newYnumber, txtPhoneNumber.frame.size.width, txtPhoneNumber.frame.size.height);
}
completion:^(BOOL finished) {
txtNewPhoneNumber.placeholder = #"here you go!";
txtNewPhoneNumber.frame = CGRectMake(newXnumber, newYnumber, txtPhoneNumber.frame.size.width, txtPhoneNumber.frame.size.height);//try setting it here once and then call next one
[self callSecondAnimation];
}];
- (void)callSecondAnimation {
//animate add textfield button.
[UIView animateWithDuration:0.5f
delay:0
options:UIViewAnimationCurveLinear
animations:^{
[sender setFrame:CGRectMake(btnAddPhoneNumber.frame.origin.x, newYnumber, btnAddPhoneNumber.frame.size.width, btnAddPhoneNumber.frame.size.height)];
txtNewPhoneNumberTitle.frame = CGRectMake(newXtitle, newYtitle, txtPhoneNumberTitle.frame.size.width, txtPhoneNumberTitle.frame.size.height);
[btnDelete setFrame:btnDeleteRect];
}
completion:^(BOOL finished) {
}];
}
I found the problem, the view used AutoLayout, when i removed the tick in AutoLayout the new position of my button is saved, and the next animation continues from the previously location.
thanks to ACB and Tobi for their input their helped me try different solutions that ended up fixing the problem!
Related
I my mapview app, I have a search bar used to search a concrete address.
It works bur for now, it appear in "static" way above the map.
My idea is make that it appear and disapperar using a button "search" as in the picture:
Any idea about hoy do thath?
Thanks in advance
You can do this in code. All you have to do is change its frame.
Add it to your navigationBar.frame and below it and then, when the icon is tapped, put it down. I usually use this code to make it look nice too:
[UIView animateWithDuration:0.2f delay:0 options:UIViewAnimationOptionCurveEaseInOut animations:^{
//Move it down here by changing its .frame
}completion:^(BOOL finished){
}];
You just Need To change the Frame of UISearchBar as You click search Icon to display it.
See
Initially,When You don't to display UISearchBar You should set the Frame In such manner.
initially set The Frame of UISearchBar Out Side The ViewFrame.
thisSearchBar.frame = CGRectMake(0.0, -50.0, searchBarWidth, searchBarHeight)]; in viewDidLoad method
On clicking the Search Icon to display the UISearchBar
- (void)showSearchBar{
[UIView animateWithDuration:0.2f delay:0 options:UIViewAnimationOptionCurveEaseInOut animations:^{
thisSearchBar.frame = CGRectMake(0.0, 50, searchBarWidth, searchBarHeight)];
}completion:^(BOOL finished)
{
}];
}
And You finished Work With SearchBar Again Call Set The Frame of Searchbar with ANimation
- (void)hideSearchBar{
[UIView animateWithDuration:0.2f delay:0 options:UIViewAnimationOptionCurveEaseInOut animations:^{
thisSearchBar.frame = CGRectMake(0.0, -50, searchBarWidth, searchBarHeight)];
}completion:^(BOOL finished)
{
}];
}
Per my comment, simply use:
searchBar.hidden = true;
I've created an animation that, when finished, is supposed to remove the image being animated. For some reason, it's not being working (i.e. it's not removing the image). Is there something I'm doing wrong?:
[UIView animateWithDuration:1.8 delay:0 options:UIViewAnimationOptionCurveEaseInOut
animations:^{
appleView[i].frame = CGRectMake(applePosition, 400.0, 25.0, 25.0);
}
completion:^(BOOL finished){ [appleView[i] removeFromSuperview]; }];
The possible reason is that appleView[i] is nil. Put a breakpoint inside completion block and check that.
try with this code ..
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:1.8];
[UIView setAnimationTransition: UIViewAnimationOptionCurveEaseInOut forView:self.view cache:YES];
[appleView[i] removeFromSuperview];
[UIView commitAnimations];
i hope this help you...
Is appleView a local variable, or a class member? Does anything else alter appleView?
The completion block will run around 1.8 seconds after you start the animation, if appleView is a class member and something changes it you could be removing the wrong thing. Try capturing appleView[i] in a local variable, like this:
UIView *goingAway = appleView[i];
[UIView animateWithDuration:1.8 delay:0 options:UIViewAnimationOptionCurveEaseInOut
animations:^{
goingAway.frame = CGRectMake(applePosition, 400.0, 25.0, 25.0);
} completion:^(BOOL finished){
[goingAway removeFromSuperview];
}];
I've searched loads of SO stuff and in Apple's references, but still unable to manage my problem.
What I have:
A screen with 2 UIImageViews and 2 UIButtons connected to them
2 kinds of animation:
Scaling up and then down of each image, one after another, only once in viewDidLoad
When a button pressed (a custom button hidden 'inside' of each UIImageView) it triggers animation of appropriate UIImageView–only one, not both–(also scale up, then down).
As I am writing for iOS4+ I'm told to use block based animations!
What I need:
How do I cancel a running animation? I've managed to cancel after all but the last one... :/
Here is my code snippet:
[UIImageView animateWithDuration:2.0
delay:0.1
options:UIViewAnimationOptionAllowUserInteraction
animations:^{
isAnimating = YES;
self.bigLetter.transform = CGAffineTransformScale(self.bigLetter.transform, 2.0, 2.0);
} completion:^(BOOL finished){
if(! finished) return;
[UIImageView animateWithDuration:2.0
delay:0.0
options:UIViewAnimationOptionAllowUserInteraction
animations:^{
self.bigLetter.transform = CGAffineTransformScale(self.bigLetter.transform, 0.5, 0.5);
} completion:^(BOOL finished){
if(! finished) return;
[UIImageView animateWithDuration:2.0
delay:0.0
options:UIViewAnimationOptionAllowUserInteraction
animations:^{
self.smallLetter.transform = CGAffineTransformScale(self.smallLetter.transform, 2.0, 2.0);
} completion:^(BOOL finished){
if(! finished) return;
[UIImageView animateWithDuration:2.0
delay:0.0
options:UIViewAnimationOptionAllowUserInteraction
animations:^{
self.smallLetter.transform = CGAffineTransformScale(self.smallLetter.transform, 0.5, 0.5);
}
completion:^(BOOL finished){
if (!finished) return;
//block letter buttons
[self.bigLetterButton setUserInteractionEnabled:YES];
[self.smallLetterButton setUserInteractionEnabled:YES];
//NSLog(#"vieDidLoad animations finished");
}];
}];
}];
}];
Somehow the smallLetter UIImageView is not working properly, because when pressed (through button) bigLetter is canceling animations properly...
EDIT:
I've used this solution, but still having problem with scaling down smallLetter UIImageView - not cancelling at all...
solution
EDIT2: I've added this at the beginning of next/prev methods:
- (void)stopAnimation:(UIImageView*)source {
[UIView animateWithDuration:0.01
delay:0.0
options:(UIViewAnimationOptionBeginFromCurrentState | UIViewAnimationOptionAllowUserInteraction)
animations:^ {
source.transform = CGAffineTransformIdentity;
}
completion:NULL
];
}
problem stays... :/ no idea how to interrupt last animation for letters in animation chain
You can stop all animations on a view by calling:
[view.layer removeAllAnimations];
(You'll need to import the QuartzCore framework to call methods on view.layer).
If you want to stop a specific animation, not all animations, your best best bet is to use CAAnimations explicitly rather than the UIView animation helper methods, then you will have more granular control and can stop animations explicitly by name.
The Apple Core Animation documentation can be found here:
https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/CoreAnimation_guide/CreatingBasicAnimations/CreatingBasicAnimations.html
For iOS 10 use UIViewPropertyAnimator to animate.
It provides methods to start, stop and pause UIView animations.
let animator = UIViewPropertyAnimator(duration: 2.0, curve: .easeOut){
self.view.alpha = 0.0
}
// Call this to start animation.
animator.startAnimation()
// Call this to stop animation.
animator.stopAnimation(true)
I'd add to Nick's answer that to make removeAllAnimations smooth next idea be very handy.
[view.layer removeAllAnimations];
[UIView transitionWithView:self.redView
duration:1.0f options:UIViewAnimationOptionTransitionCrossDissolve animations:^{
[view.layer displayIfNeeded];
} completion:nil];
You can try this (in Swift):
UIView.setAnimationsEnabled(false)
UIView.setAnimationsEnabled(true)
Note: you can put code between those two calls if necessary, for example:
UIView.setAnimationsEnabled(false)
aview.layer.removeAllAnimations() // remove layer based animations e.g. aview.layer.opacity
UIView.setAnimationsEnabled(true)
I wanted to rotate a UIView on its horizontal axis for 360 degrees and then refresh the content in the view. I was looking out for solutions on this. Found a couple here n there. This is what I came out with.
[UIView animateWithDuration:1 delay:0 options:UIViewAnimationOptionTransitionNone animations:^{
self.tableView.layer.transform = CATransform3DMakeRotation(M_PI,1.0,0.0,0.0);
} completion:^(BOOL finished){
NSLog(#"Finished first pi");
[UIView animateWithDuration:1 delay:0 options:UIViewAnimationOptionTransitionNone animations:^{
self.tableView.layer.transform = CATransform3DMakeRotation(M_PI,1.0,0.0,0.0);
} completion:^(BOOL finished) {
NSLog(#"Finished second pi");
}];
}];
This flips the view but only by 180 degrees. I want it to flip one more time so that I can see the view normally..
Both my NSLogs are displayed one after the other. I am sure I am missing something here. ANy help would be helpful..
Thanks
In your completion block try concatenating the new transform with the current transform.
self.tableView.layer.transform = CATransform3DConcat(self.tableView.layer.transform, CATransform3DMakeRotation(M_PI,1.0,0.0,0.0));
You should check this answer How to make a CATransform3dMakeRotation rotate the other way? And chain together
I think that your problem is related with: "When you are working with a transform directly, Core Animation will interpolate the transform from the current value to the specified transform. It will find the shortest path to get to that transform, which will restrict the animation direction. If you try to animate the same transform property twice, the second value will simply override the first, not combine the two transforms together."
use this:-
rotatingView.layer.anchorPoint = CGPointMake(0.0f, 0.0f);
rotatingView.center = CGPointMake(0,
(rotatingView.center.y -
(rotatingView.bounds.size.height/2.0f)));
rotatingView.center = CGPointMake(0,
(rotatingView.center.y-
(rotatingView.bounds.size.height/2)));
// start the Page Open
[UIView beginAnimations:#"Animation" context:nil];
[UIView setAnimationDuration:13.0];
// set angle as per requirement
[rotatingView.layer setValue:[NSNumber numberWithInt:280]
forKeyPath:#"transform.rotation.x"];
[UIView commitAnimations];
You just have to change your code from 1.0 to 0.0 inside completion block and woohoo all done.
[UIView animateWithDuration:1 delay:0 options:UIViewAnimationOptionTransitionNone animations:^{
self.tableView.layer.transform = CATransform3DMakeRotation(M_PI,1.0,0.0,0.0);
} completion:^(BOOL finished){
NSLog(#"Finished first pi");
[UIView animateWithDuration:1 delay:0 options:UIViewAnimationOptionTransitionNone animations:^{
self.tableView.layer.transform = CATransform3DMakeRotation(M_PI,0.0,0.0,0.0);
} completion:^(BOOL finished) {
NSLog(#"Finished second pi");
}];
}];
You can also approach this using the .Repeat and .Autoreverse animation options + setting animation's repeat count. Here's an example in Swift:
UIView.animateWithDuration(time, delay: 0, options: [.Repeat, .Autoreverse], animations: {
UIView.setAnimationRepeatCount(3)
self.view.layer.transform = CATransform3DMakeRotation(CGFloat(M_PI), 1, 0, 0)
}) { (completed) in
// completion
}
I have 2 layer is top and bottom on my program
how can i remove top layer with animation or bring top layer to back is it possible?
The easiest is playing with frame and alpha before removing it.
You can get some cool effects
-(void)removeWithEffect:(UIView *)myView
{
[UIView beginAnimations:#"removeWithEffect" context:nil];
[UIView setAnimationDuration:0.5f];
//Change frame parameters, you have to adjust
myView.frame = CGRectMake(0,0,320,480);
myView.alpha = 0.0f;
[UIView commitAnimations];
[myView performSelector:#selector(removeFromSuperview) withObject:nil afterDelay:0.5f];
}
iOS Update
You can now use blocks to perform your animation
[UIView animateWithDuration:0.5f
animations:^{view.alpha = 0.0;}
completion:^(BOOL finished){ [view removeFromSuperview]; }];
If you're targetting iOS 4.0 upwards you can use animation blocks:
[UIView animateWithDuration:0.2
animations:^{view.alpha = 0.0;}
completion:^(BOOL finished){ [view removeFromSuperview]; }];
(above code comes from Apple's UIView documentation)