I have a simple UIView animation block. In the block, I only change the view's alpha, but the view's frame is also being animated! WTF?
Here's my code:
UIButton *button = [flowerViews objectAtIndex:index];
UIImageView *newGlowView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"grid_glow.png"]];
newGlowView.frame = CGRectMake(0, 0, 130, 130);
newGlowView.center = button.center;
newGlowView.alpha = 0.0;
[scrollView_ addSubview:newGlowView];
[scrollView_ sendSubviewToBack:newGlowView];
[UIView animateWithDuration:0.3 animations:^{
newGlowView.alpha = 1.0;
}];
As you see, I'm creating a new view and adding it to scrollView_. I'm setting the view's position and alpha before adding it to scrollView_. Once it's added, I have an animation block to animate the view's alpha from 0 to 1.
The problem is, the view's position is also being animated! As it fades in, it looks as if it's animating from an original frame of CGRectZero to the one I've assigned it.
Ostensibly, only properties set within the animation block should be animated, right? Is this a bug? Am I missing something?
Thanks!
Perhaps the whole thing being called from an animation block or maybe within an event that is within an animation block like the autorotate view controller delegate methods.
Related
I have a gesture which action moves a UIView to point B from point A. How can I add a nice transition of the UIView moving from point A to point B. I am looking at making it slowly move from point A to point B. How would I do this? At the moment to move the item I set the frame to point B.
check this code and set frame what you want
[UIView animateWithDuration:0.75 animations:^{
button.frame = CGRectMake(10, 70, 100, 100);
}];
If you're using auto-layout (and you almost certainly are - it's the default) then #PinkeshGjr s solution is likely to have problems. With AutoLayout you can't manipulate a view's frame directly. At some point after moving the frame the view's constraints can snatch it back.
Instead, you want to create constraints that control the property/properties that you want to animate (x and y position in this case) and control-drag from the constraints into your view controller's header to create outlets.
Then you want to change the constant value on the constraints and call layoutIfNeeded inside your animation code. Something like this:
buttonXConstraint.constant = 100;
buttonYConstraint.constant = 100;
[UIView animateWithDuration:0.75 animations:^{
[self.button layoutIfNeeded];
}];
The code above assumes that you've created 2 constraint outlets and called them buttonXConstraint and buttonYConstraint.
You can achieve that by using animateKeyframesWithDuration or animateWithDuration:animations but it will be just a linear interpolation. If you want to have more control over the animation I recommend that you take a look at MTAnimation it's really a powerful and easy to use Lib for achieving beautiful animations
Cheers
Very nice helper method to use for animating view.
- (void) animateView:(UIView *)view ToFrame:(CGRect) frame withDuration:(float) duration withDelay:(float) startDelay
{
[UIView animateWithDuration:duration delay:startDelay options:0 animations:^{
view.frame = frame;
} completion:^(BOOL finished)
{
// Completion actions
}];
}
Is there something I'm missing here?
i want the imageview to slide in then slide out from the bottom of the screen.
also, this seems to put the UIImageView behind the navigation bar how can I make a CGRect to fit the screen under the navbar?
_finga = [[UIImageView alloc] initWithFrame:CGRectMake(0, 88, [UIScreen mainScreen].bounds.size.width, [UIScreen mainScreen].bounds.size.height-88)];
hiddenFrame = CGRectOffset(_finga.frame, 0, _finga.frame.size.height);
_finga.frame = hiddenFrame;
[self.view addSubview:_finga];
_finga.image = [UIImage imageNamed:#"Finga"];
[UIView animateWithDuration:2.0 animations:^{
_finga.frame = self.view.bounds;
}completion:^(BOOL done) {
if (done) {
NSLog(#"Complete!");
[UIView animateWithDuration:2.0 animations:^{
_finga.frame = hiddenFrame;
}];
}
}];
The CGRect you initialize _finga with would put it under the nav bar. In the animation, you are setting the frame to the bounds of the view, which would put it behind the bar, since the y value would be 0.
You could write less code by initializing _finga with this frame from the start:
CGRectMake(0,
self.view.bounds.size.height,
self.view.bounds.size.width,
self.view.bounds.size.height-88);
That will put the view off the screen. Then after you add it as a subview and set its image, animate the view back up to this frame:
CGRectMake(0,
88,
self.view.bounds.size.width,
self.view.bounds.size.height-88);
Which will put the view just below the nav bar. Consider also replacing all the hard coded 88s with a variable or #define so that you can play around with it more easily and in case the nav bar height ever changes.
As for the completion block, try logging that #"Complete!" string before you check if done is YES, or put a breakpoint there and see what the value of done is. Your animation may not be completing for some reason, which would explain why the code in the completion block is not being run.
Generally, though, if you are just using the completion block to run another animation after the first one, you don't need to check the done BOOL at all. It's only crucial when something else in your program depends on the state of the animation. For example, the user may click a button which animates something and then takes the user to a different section of the app. But if the user cancels the animation, you may not want to go to the other section after all, so you can check done.
I have two UIViews (My bad it is a UIView and a UIButton) which I am animating at the same time. I originally had a view and a containerView which would animate just fine and worked like a charm.
Now only one of my UIViews will move/animate in animateWithDuration even though through debugging the frame of the other view says that it is in a position it is not.
CGRect rect = self.toggle.frame;
CGRect tabRect = self.tabButton.frame;
rect.origin.x = rect.origin.x - rect.size.width;
NSLog(#"%f Before",tabRect.origin.x);
tabRect.origin.x = tabRect.origin.x - rect.size.width;
NSLog(#"%f After", tabRect.origin.x);
[UIView animateWithDuration:0.3 animations:^{ // animate the following:
self.toggle.frame = rect; // move to new location
self.tabButton.frame = tabRect;
}];
NSLog(#"%f AfterAnimation", tabButton.frame.origin.x);
The toggle view moves fine, but the tabButton view does not animate or move. The strange thing is that both the "After" and "AfterAnimation" debugging code returns the same value, which suggests the frame has indeed moved. Is there a specific reason that this will not work when toggle is a UIView when it would work as a UIContainerView?
Note that if I remove the line
self.toggle.frame = rect;
tabButton will animate correctly, but if I move toggle, tabButton will not move regardless of whether it is first in the animation block or second.
Edit: I have tried moving them into separate blocks and to change the center point rather than the frame, to no avail. It seems that if the toggle view moves, the tabButton will not move.
Edit 2: The pictorial evidence.{
In the following screenshots tabButton bg is green and toggle bg is red.
Above: Initial position (toggle is off-screen) correct position
Above: The problem in question toggle is correct tabButton is not
Above: When self.toggle.frame = rect is commented out (tabButton correct, toggle not)
}
Edit 3: It's even worse than I feared.{
I have done a few more tests and even if I take the toggle change out of the animation block to make it an instant thing, the tabButton will still not animate. This makes me think the tabButton may just fundamentally dislike the toggle view and/or myself so will not move just to spite me.
}
Edit 4:{
If I change the tabButton animation to tabButton.frame = CGRectMake(10,10,100,100) the View snaps instantly to that location and animates back to its original position in the same time as the animation duration.
}
I better add more bookkeeping/TLDR information in case things aren't clear.
toggle is an instance of ToggleDraw which is a subview of UIView which I created.
tabButton is a UIButton which is part of my IB viewController and a property of the class
Both toggle and tabButton are subviews of self.view
The animations will work individually with no modifications to the logic of the rects but will not work if they are animated at the same time
toggle animation seems to take precedence over tabButton animation regardless of the order
I had a problem with the animation of an UIView created in IB (the animation didn't start from the current position of the view, and ended in the initial position).
All worked fine after sending layoutIfNeeded() to the underlaying view before the animation block:
self.view.layoutIfNeeded()
UIView.animateWithDuration(0.5) { () -> Void in
...
I think it is not a problem about a UIView Animation. Maybe your toggle posiztion is related to your tabButton. For a try, your can set toggle frame to a rect lick (10, 10, 100,100), then check the result.
I've created an example of what you describe and everything seems to work fine. This is what I used:
UIView *toggle = [[UIView alloc] initWithFrame:CGRectMake(320, 64, 100, 100)];
[toggle setBackgroundColor:[UIColor redColor]];
[self.view addSubview:toggle];
UIButton *tabButton = [[UIButton alloc] initWithFrame:CGRectMake(220, 64, 100, 100)];
[tabButton setBackgroundColor:[UIColor greenColor]];
[self.view addSubview:tabButton];
CGRect rect = toggle.frame;
CGRect tabRect = tabButton.frame;
rect.origin.x = rect.origin.x - rect.size.width;
NSLog(#"%f Before",tabRect.origin.x);
tabRect.origin.x = tabRect.origin.x - rect.size.width;
NSLog(#"%f After", tabRect.origin.x);
[UIView animateWithDuration:1 animations:^{ // animate the following:
toggle.frame = rect; // move to new location
tabButton.frame = tabRect;
}];
What I can suggest is to make sure that the code is being ran on mainthread:
dispatch_async(dispatch_get_main_queue(), ^{
[UIView animateWithDuration:0.3 animations:^{
self.toggle.frame = rect; // move to new location
self.tabButton.frame = tabRect;
}];
});
Also take into account that the log you have after the animation code is incorrect as it won't run after the animation, but rather right next to asking for the animation.
If you want code to run after the animation you should use:
[UIView animateWithDuration:0.3 animations:^{
self.toggle.frame = rect; // move to new location
self.tabButton.frame = tabRect;
} completion:^(BOOL finished){
NSLog(#"Finished animating!");
}];
I have found a solution to the problem. If I initialise the UIButton (tabButton) programmatically rather than through the interface builder, both views will animate simultaneously.
This however seems very hacky to me, kind of like putting a bandaid over a missing foot and hoping it will sort itself out.
I could not work out the root cause of the problem but at least I could avoid the symptoms.
If anyone knows why the views would not animate when the button was made in the interface builder post an answer here, I am interested in knowing the reason behind this.
Thanks for your help everyone.
I am currently using this code:
FlipView *fv = [[FlipView alloc]init];
[UIView transitionWithView:flipContainer
duration:1
options:UIViewAnimationOptionTransitionFlipFromLeft
animations:^{
[flipContainer addSubview:fv];
}
completion:NULL];
to flip a container view containing a UIImage around to reveal a second view (FlipView). FlipView is a UIView currently with its background set to red for debugging.
What happens now is that the container flips, but it's displaying the same thing as before, despite using:
[flipContainer addSubview:fv];
What am I doing wrong?
What happens if you add the subview without the animation? Does the subview appear? Maybe the subview isn't being initialized properly (such as maybe its frame isn't being set properly?)
You could try initializing like this:
CGRect frame = CGRectMake(xOrigin,yOrigin,width,height);
FlipView *fv = [[FlipView alloc] initWithFrame:frame];
Or try setting the frame property of the FlipView:
FlipView *fv = [[FlipView alloc]init];
CGRect frame = CGRectMake(xOrigin,yOrigin,width,height);
fv.frame = frame;
I got this warning:
'UIView' may not respond to 'addSubview:withAnimation:'
The line of code which produce that warning is this:
[self.masterView addSubview:self.detailImage withAnimation:def];
And my relevant code is like this:
ExUIViewAnimationDefinition *def = [[ExUIViewAnimationDefinition alloc] init];
def.type = ExUIAnimationTypeTransition;
def.direction = ExUIAnimationDirectionMoveUp;
[self.masterView addSubview:self.detailImage withAnimation:def];
[def release];
I looked on the UIView documentation, i thought addSubview may be deprecated, but it still like this.
Does any one know how to solve this warning? Thanx in advance.
addSubview is a method UIView will respond to. addSubview:withAnimation: is not a method UIView will respond to.
If you want to add a subview with a fade or something like that, try this:
self.detailImage.alpha = 0.0;
[self.masterView addSubview:self.detailImage];
[UIView animateWithDuration:0.3 animations:^{
self.detailImage.alpha = 1.0;
}];
To add a subview to a parent, call the addSubview: method of the parent view. This method adds the subview to the end of the parent’s list of subviews.
To insert a subview in the middle of the parent’s list of subviews, call any of the insertSubview:... methods of the parent view. Inserting a subview in the middle of the list visually places that view behind any views that come later in the list.
[self.masterView addSubView:self.def];
[def release];
This helped me to animate subview by sliding out from down of border of parent view
-(void) addAnimatadView:(UIView *) animatedView toView:(UIView *)aView {
CGRect frame = animatedView.frame;
float origin = frame.origin.y;
frame.origin.y = aView.frame.size.height;
[animatedView setFrame:frame];
[aView addSubview:animatedView];
frame.origin.y = origin;
[UIView animateWithDuration:0.4 animations:^{[animatedView setFrame:frame];}];
}
Just change frame origins as you want to reach free sliding