UIView animateWithDuration not getting executed - ios

I am trying to move a UIView from the bottom to the top.
I am using iOS SDK 7.0, the uiviewcontroller's autolayout is on.
- (void) viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
[self.view bringSubviewToFront: myView];
[self moveFromBottom];
}
- (void) moveFromBottom{
[myView setBackgroundColor:[UIColor redColor]];
CGPoint top = myView.center;
top.y = self.view.frame.size.height - top.y;
[UIView animateWithDuration:5.0f
delay:0.0f
options:UIViewAnimationOptionCurveLinear | UIViewAnimationOptionBeginFromCurrentState
animations:^{
myView.center = top;
}
completion:^(BOOL finished){
if (finished) {
NSLog(#"finished");
}
}
];
The completion block gets executed and print finish but the UIView is not moved.
I also tried animate frame instead of center, which does not work neither.
But if I try to animate alpha instead it will work
edit: if I turn off autolayout in the storyboard it will also work.

You cannot explicitly set the frame or center when Autolayout is turned on. My suggestion would be to create an IBOutlet for the y-origin constraint on myView and set the constant property of that in your animation block. That should animate properly.

Where is myView initially? If it is dead center:
top.y = self.view.frame.size.height - top.y;
will be equal to the initial value of top.y. so it will not move. Did you mean to move it flush to the top. If so I think it should be:
top.y = (myView.frame.size.height/2.0f);
Also if you have auto layout enabled, check what constraints you have and make sure its not forcing it to pin in its position. I would disable auto layout to make sure this is not causing your issue. If all else fails, NSLog the value of top.y to make sure its what you intended.

Related

Animating a UIButton to fullscreen

so I have a very simple button that when clicked goes to fullscreen and when clicked again goes back to the same position it was initially in. For some reason it works perfectly without the animation. When I uncomment the animation part when I initially click the button it does nothing, the second time I click it slightly enlarges. The third time I click it animates slowly but back to it's smaller original size... Why is it animating the opposite way?
- (IBAction)viewImage1:(id)sender {
UIButton *btn = (UIButton*) sender;
if (btn.tag == 0)
{
CGRect r = [[UIScreen mainScreen] bounds];
/*[UIView animateWithDuration:0.5f delay:0.0f options:0 animations:^{*/
[sender setFrame: r];
/*}completion:nil];*/
btn.tag = 1;
}
else
{
btn.tag = 0;
[sender setFrame:CGRectMake(0,0,370,200)];
}
}
There are two solutions to your problem either of which will work:
Disable Autolayout. (discouraged)
You can do that in Interface Builder by opening the File Inspector
in the right pane and unchecking the respective check box.
However, if you want to use Autolayout for constraining other UI elements in your view (which is quite a good idea in most cases) this approach won't work which is why I would recommend the second solution:
Keep Autolayout enabled, create an outlet for your button in your view controller and set
self.myButton.translatesAutoresizingMaskIntoConstraints = YES;
in your view controller's viewDidLoad method.
You could also add layout constraints to your button and animate those. (This excellent Stackoverflow post explains how it's done.)
The reason for this tricky behavior is that once you enable Autolayout a view's frame is no longer relevant to the actual layout that appears on screen, only the view's layout constraints matter. Setting its translatesAutoresizingMaskIntoConstraints property to YES causes the system to automatically create layout constraints for your view that will "emulate" the frame you set, in a manner of speaking.
It is easier to do this with auto layout and constraints. Create an IBOutlet for the height constraint of your button call it something like btnHeight. Do the same for the width constraint call it something like btnWidth. Then create an IBAction like so:
- (IBAction)buttonPress:(UIButton *)sender {
UIButton *btn = (UIButton *) sender;
CGRect r = [[UIScreen mainScreen] bounds];
if (CGRectEqualToRect(btn.frame, CGRectMake(0.0, 0.0, 370, 200))) {
[UIView animateWithDuration:0.5 delay:0.0 options:0 animations:^{
self.btnHeight.constant = r.size.height;
self.btnWidth.constant = r.size.width;
[self.view layoutIfNeeded];
} completion:^(BOOL finished){
}];
}else{
[UIView animateWithDuration:0.5 delay:0.0 options:0 animations:^{
self.btnHeight.constant = 200.0;
self.btnWidth.constant = 370.0;
[self.view layoutIfNeeded];
} completion:^(BOOL finished){
}];
}
}
In my experience animating the frame of a UIButton does not work well, a, the only method, I'm aware of, is to use CGAffineTransformScale which will rasterize the title of the button and scale it as well.

How can I animate the height constraint of a UIPickerView?

I've learned that the way to animate constraints in Cocoa Touch is to just set them and then put [self.view layoutIfNeeded] in an animation block, like so:
self.someViewsHeightConstraint = 25.0;
[UIView animateWithDuration:0.5 animations:^{
[self.view layoutIfNeeded];
}];
This is working fine, for example with a simple UIView. However, it does not work with a UIPickerView. It just snaps into the new position without animating.
Any ideas why this might be the case? What ways are there to work around this?
The effect I'm going for is that the Picker View should shrink to just show the chosen item, as the user goes on to input other things. One idea I had is to make a snapshotted view and animate that instead, but I couldn't quite get that working either.
I found trying to animate the height or the placement constraint of a UIPickerView to be problematic. However, doing transforms seems to work well -- even if you have Auto Layout Constraints everywhere, including in the view to be transformed.
Here's an example of what works for me. In this case, I've placed the picker view inside a blurring effects view -- but you don't even need to put your picker view inside another view to animate it.
In the code below, when I call show, it animates up vertically. When I call the hide method, it animates downwards.
- (void)showPickerViewAnimated:(BOOL)animated;
{
__weak MyViewController *weakSelf = self;
[UIView animateWithDuration:(animated ? kPickerView_AppearanceAnimationDuration : 0.0)
delay:(animated ? kPickerView_AppearanceAnimationDelay : 0.0)
options:(UIViewAnimationOptionCurveEaseInOut)
animations:^{
weakSelf.pickerViewContainerView.transform = CGAffineTransformMakeTranslation(0,0);
}
completion:^(BOOL finished) {
[weakSelf.view layoutIfNeeded];
}];
}
- (void)hidePickerViewAnimated:(BOOL)animated;
{
__weak MyViewController *weakSelf = self;
[UIView animateWithDuration:(animated ? kPickerView_DisappearanceAnimationDuration : 0.0)
delay:(animated ? kPickerView_DisappearanceAnimationDelay : 0.0)
options:(UIViewAnimationOptionCurveEaseInOut)
animations:^{
weakSelf.pickerViewContainerView.transform = CGAffineTransformMakeTranslation(0, kPickerView_Height);
}
completion:^(BOOL finished) {
[weakSelf.view layoutIfNeeded];
}];
}
picker view, If you have added constraint To TopLayout for yPosition remove it and add constraint to bottom layout instead.
That will solve the problem. here is my code and its working:
self.timePickerHeightConstraint.constant = pickerIsClosed ? 216 :
0;
[UIView animateWithDuration:0.5 animations:^{
[self.view layoutSubviews];
} completion:^(BOOL finished){
}];

UIView transitionWithView & setting the frame no longer works iOS8

Since updating to iOS8 many animations have stopped working. It appears that the y position of the view cannot be changed. In iOS7 there was no problem. But with iOS8 it will not go past the original y position.It appears that the frame of the view are indeed being updated but the frame is not being redrawn. Here is my code
[UIView transitionWithView:self.view duration:0.3 options:UIViewAnimationOptionCurveEaseInOut animations:^{
self.table.frame = CGRectMake(0, (self.view.bounds.size.height - self.button.frame.size.height) - (252), self.view.bounds.size.width, self.table.frame.size.height);
} completion:nil];
I have also tried this code outside of the animation block;
self.table.frame = CGRectMake(0, (self.view.bounds.size.height - self.button.frame.size.height) - (252), self.view.bounds.size.width, self.table.frame.size.height);
Update The solution is to adjust the auto layout constraints and not the frames origin. Click on the auto layout constraint you would like to adjust (in interface builder e.g top constraint). Next make this an IBOutlet;
#property (strong, nonatomic) IBOutlet NSLayoutConstraint *topConstraint;
Then to animate it;
self.topConstraint.constant = -100;
[self.viewToAnimate setNeedsUpdateConstraints];
[UIView animateWithDuration:.3 animations:^{
[self.viewToAnimate layoutIfNeeded];
}];
The above code will move the view/ or move the constraints upwards. To move it backdown to its original simply call;
self.topConstraint.constant = 0;
[self.viewToAnimate setNeedsUpdateConstraints];
[UIView animateWithDuration:.3 animations:^{
[self.viewToAnimate layoutIfNeeded];
}];
Second Solution
I found the solution. It appears that if self.table is an IBOutlet then the animation wont work. I am guessing this a result of the new dynamic storyboards. Creating your elements all in code and not in the storyboard leads to a successful animation. That is all I had to do to get it to work, create the UITableView in code.

UIView animation happens in the wrong direction

For some reason my UIImageView animates to its start position, from its end position. I can’t work out why but have a feeling it’s something to do with using constraints (I use constraints on all UIViews throughout my app).
My animation should animate logoImageView off the top of the screen.
Here is my code:
NSLog(#"%#", NSStringFromCGPoint(self.logoImageView.center));
[self.logoImageView updateConstraintsIfNeeded];
[UIView animateWithDuration:3.0f
animations:^{
[self.logoImageView setCenter:CGPointMake(CGRectGetMidX(self.logoImageView.frame),
CGRectGetMaxY(self.view.frame) + CGRectGetHeight(self.logoImageView.frame))];
[self.logoImageView updateConstraintsIfNeeded];
} completion:^(BOOL finished) {
NSLog(#"%#", NSStringFromCGPoint(self.logoImageView.center));
[self performSegueWithIdentifier:#"Play" sender:nil];
}];
Here is my log of positions:
2014-09-05 18:08:56.673 Cups[2142:89629] {160, 111.5}
2014-09-05 18:09:01.250 Cups[2142:89629] {160, 645}
But the animation plays the other way round. Any ideas?
This probably has something to do with the fact that you're calling -updateLayoutConstraints without actually updating constraints.
A better approach would be to animate the constraints rather than setting the position and frames of your views. A good trick to remember is that you can create outlets in your XIB to your constraints.
Let's assume you have an NSLayoutConstraint called logoImageViewTopConstraint set up as an IBOutlet in your view controller which sets the top of the logoImageView to it's superview's top layout guide. At design time set that constraint's constant to 100 or so (moving it down 100 points from the top of the view). Then with that constraint you could do something like this for your animation:
[self.view layoutIfNeeded];
self.logoImageViewTopConstraint.constant = 0; // moves image view back to the top
[self.view setNeedsUpdateConstraints];
[UIView animateWithDuration:0.8 delay:0.0 options:UIViewAnimationOptionCurveEaseOut animations:^{
[self.view layoutIfNeeded]; // interpolates animation from changes to constraints
} completion:^(BOOL finished) {
// done
}];
If you get it working just play with the constants of the constraints until you get the exact effect that you want.
What happens if you do this:
[UIView animateWithDuration:3.0f
animations:^
{
[self.logoImageView setCenter:CGPointMake(CGRectGetMidX(self.logoImageView.frame),
-(CGRectGetHeight(self.logoImageView.frame)/2)];
} completion:^(BOOL finished)
{
[self performSegueWithIdentifier:#"Play" sender:nil];
}];
animate to y center = -(logoHeight/2). This is "off the top of the screen" in iOS coordinates
don't call updateConstraints in animation block
It turns out a CABasicAnimation running on the layer of the UIView was interrupting the UIView animation. Thanks for suggestions

iOS UIView setFrame animation error

I am having a weird error while animating a couple of view in iOS. My goal is to switch from a custom "Split View". You can see what's going on in this youtube video: http://youtu.be/ZWbf2bQYMns
You can see the weird "bump" in the Y value of the UIImageView, and I have been wondering how to fix it for quite a while now.
This is the View Controller's interface:
#interface VideoSharing_Pad : UIViewController
{
IBOutlet UIView *videoCallView;
IBOutlet UIImageView *imageView; //This is "inside" mediaView
IBOutlet UIView *mediaView;
CGRect mediaRect;
CGRect videoCallRect;
CGRect imageRect;
}
In viewDidLoad I store both views doing:
//Get frames from XIB
mediaRect = mediaView.frame;
videoCallRect = videoCallView.frame;
imageRect = imageView.frame;
And this is the code that executes when I want to switch from the Split View to a full Screen Mode:
- (IBAction)toggleFullScreen:(id)sender
{
if (iScreenMode == callAndShareMedia) {
CGRect fullScreenRect = CGRectMake(0, 0, 1024, 768);
CGRect dissapearRect = CGRectMake(0, - videoCallView.bounds.size.height, videoCallView.bounds.size.width, videoCallView.bounds.size.height);
[UIView animateWithDuration:1.0
delay:0.1
options: UIViewAnimationCurveEaseOut
animations:^{
[videoCallView setFrame:dissapearRect];
[imageView setFrame:fullScreenRect];
[mediaView setFrame:fullScreenRect];
}
completion:^(BOOL finished){
}];
iScreenMode = onlyShareMedia;
return;
}
else if (iScreenMode == onlyShareMedia)
{
[UIView animateWithDuration:1.0
delay:0.1
options: UIViewAnimationCurveEaseOut
animations:^{
[videoCallView setFrame:videoCallRect];
[mediaView setFrame:mediaRect];
[imageView setFrame:imageRect];
}
completion:^(BOOL finished){
}];
iScreenMode = callAndShareMedia;
return;
}
}
I would really appreciate any help I can get. Thanks a lot!
this is a screenshot of the XIB:
as you can see from the screenshot and the .h file, the imageView is inside an UIView called mediaView, The other UIView, videoCallView is the one with the three dummy pictures.
Interesting question indeed. It definitely has to do with animating superview and subview at the same time. I did sample program, and reproduced similar situation.
My workaround would be to avoid animating the superview (mediaView), and expand only the subview (imageView) to full rectangle. Since your superview (mediaView) does not have much, it should not give so different user experience.
So, instead of
[UIView animateWithDuration:1.0
delay:0.1
options: UIViewAnimationOptionCurveEaseOut
animations:^{
[videoCallView setFrame:dissapearRect];
[imageView setFrame:fullScreenRect];
[mediaView setFrame:fullScreenRect];
}];
You can do
[UIView animateWithDuration:1.0
delay:0.1
options: UIViewAnimationOptionCurveEaseOut
animations:^{
[videoCallView setFrame:dissapearRect];
[imageView setFrame:(CGRect){fullScreenRect.origin.x - mediaRect.origin.x, fullScreenRect.origin.y - mediaRect.origin.y, fullScreenRect.size}];
}];
For coming back to normal mode, you can just ignore mediaView animation. Probably you want to move (animate) the toggleButton along with other animation as well.
#jrturton's answer (second part) seemed a nice workaround, but it did not work on my sample code. It worked on the way to go (expansion), but bumped on the way back (shrink), for the reason I don't know why. But don't dismiss his answer because of my comment, it could be me.
Interesting question. I can't view your video from work but I expect your issue is that you are resizing both a view and its subview during an animation, there will probably be interference from any autoresizing masks (do you have them?) - the superview will change the size of the subview, then the interpolated frame will be applied.
If you think about it there will also be a stage where your image view has to animate more quickly than the superview as it has more ground to cover to get to the same final rect. The interpolation worked out by the animation may struggle with this.
If removing any autoresizing masks doesn't work, you might need to split the animation into two - one to increase the size of the superview, and another to then zoom the image view to full size.

Resources