I am creating a fairly simple animation of several buttons and images in my app. Basically it looks like a menu that unfolds.
I use auto-layout storyboard to manage the view and I have set proper constraints so it looks the same on every devices.
I have two buttons and two images over these buttons, when first show,
NSLog(#"Button position is %#", NSStringFromCGRect(_toclass.frame));
NSLog(#"Image position is %#", NSStringFromCGRect(_revel2.frame)); I get the answer : Button position is {{0, 153}, {600, 133}} and Image position is {{254, 179}, {92, 60}}.
I set the following variables and perform the first animation :
Hprofile = 20;
Hclass = 153;
Htuto = 2 * Hclass;
Hicones = 26;
[UIView animateWithDuration:1.2
delay:0
options:(UIViewAnimationOptions) UIViewAnimationCurveEaseInOut
animations:^{
[_toclass setFrame:CGRectMake(0, -Htuto, self.view.bounds.size.width, _toclass.frame.size.height)];
[_revel2 setFrame:CGRectMake(_revel2.frame.origin.x, -Htuto+Hicones, 92, 60)];
}
completion:^(BOOL finished){
}];
After this animation (that works fine) is done, there is another button that you can use to come back to the previous settings :
[UIView animateWithDuration:1
delay:0
options:(UIViewAnimationOptions) UIViewAnimationCurveEaseInOut
animations:^{
[_toclass setFrame:CGRectMake(0, Hclass, self.view.bounds.size.width, 133)];
[_revel2 setFrame:CGRectMake(_revel2.frame.origin.x, Hclass+Hicones, 92, 60)];
}
completion:^(BOOL finished){
NSLog(#"Button position is %#", NSStringFromCGRect(_toclass.frame));
NSLog(#"Image position is %#", NSStringFromCGRect(_revel2.frame));
}];
After this animation the buttons are in the correct place but the images are not.
I get the answer : Button position is {{0, 123}, {320, 102.5}} and Image position is {{114, 179}, {92, 60}}.
I imagine there is some conflict between the animation and the auto-layout constraints, but I can't figure it out.
Help !
Thank you for your time.
If you are using auto layout you should not change frames anymore, you should work with constraints.
As far as I can understand from your code, you want to move your button and image up and down. The correct way would be to create an IBOutlet from the top offset constraints in the storyboard (for both the button and the image) and change the constant on them so that it fits your animation.
Something like this instead of the first frame modification.
[UIView animateWithDuration:1.2
delay:0
options:(UIViewAnimationOptions) UIViewAnimationCurveEaseInOut
animations:^{
[self.buttonTopOffsetConstraint setConstant:-Htuto];
[self.imageTopOffsetConstraint setConstant:(-Htuto+Hicones)];
[self.view layoutIfNeeded];
}];
Let me know if you need more help or something is unclear. Good luck!
Related
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){
}];
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
I created some animations in my project. Basically, I use UIView animate and CGAffineTransform, but a very strange thing happened and I have no idea. Hope someone can help me solve this problem. Thanks in advance.
This is the strange thing:
After the user clicks on a button, the button slides off screen and another two buttons slide on the screen (I just changed the center point of these buttons to achieve this animation). And, some time later, a view on the screen start shaking (I use CGAffineTransform to achieve this).
At this moment, the strange thing happens - the button that previous slid off screen show up at its original position again and the other two buttons disappear (No animation, just shows up and disappear).
The following is the related code,
1) Button slide off and slide in animation related code
- (IBAction)start:(id)sender
{
// 1. Slide in cancel and pause button
[UIView animateWithDuration:0.5f animations:^{
[startButton setCenter:CGPointMake(startButton.center.x + 300.0f, startButton.center.y)];
[cancelButton setCenter:CGPointMake(cancelButton.center.x + 300.0f, cancelButton.center.y)];
[pauseButton setCenter:CGPointMake(pauseButton.center.x + 300.0f, pauseButton.center.y)];
} completion:^(BOOL finished) {
if (finished) {
NSLog(#"Move finished");
}
}];
}
2) The view shaking animation related code
- (void)shakeView:(UIView *)viewToShake
{
CGFloat t = 2.0;
CGAffineTransform translateRight = CGAffineTransformTranslate(CGAffineTransformIdentity, t, 0.0);
CGAffineTransform translateLeft = CGAffineTransformTranslate(CGAffineTransformIdentity, -t, 0.0);
viewToShake.transform = translateLeft;
[UIView animateWithDuration:0.07 delay:0.0 options:UIViewAnimationOptionAutoreverse|UIViewAnimationOptionRepeat animations:^{
[UIView setAnimationRepeatCount:2.0];
viewToShake.transform = translateRight;
} completion:^(BOOL finished) {
if (finished) {
[UIView animateWithDuration:0.05 delay:0.0 options:UIViewAnimationOptionBeginFromCurrentState animations:^{
viewToShake.transform = CGAffineTransformIdentity;
} completion:nil];
}
}];
}
This isn't weird, it's a common problem with
auto layout. If you move UI elements by changing frames, then as soon as something else takes place that requires laying out views, the moved views will revert to the position defined by their constraints. To fix it, you either need to turn off auto layout, or do your animations by changing constraints not frames.
I have tried your code in my test project. The problem is probably because you are using Autolayout in your xib.
Please checking your xib file and uncheck the Autolayout property.
I want to make the UIButton image transform from its current size to twice its size and then back to its original size similar to the facebook "like" animation.
I'm using this code but for some reason it is doing the reverse action; i.e. first shrinks and only then getting large again.
What am I doing wrong? It works perfectly on a uiimageView but not on a uibutton
[self.likeButton setClipsToBounds:NO];
CGAffineTransform firstTransform = self.likeButton.imageView.transform;
[UIView animateWithDuration:.15f animations:^{
self.likeButton.imageView.transform = CGAffineTransformScale(firstTransform, 2, 2);
} completion:^(BOOL finished) {
[UIView animateWithDuration:.15f animations:^{
self.likeButton.imageView.transform = firstTransform;
}];
}];
Layers have a property masksToBounds and UIView has clipsToBounds. Most likely the button has these set to YES on the primary view or subviews. Its possible you can discover which ones have these set to YES, and set them temporarily to NO.
If anyone is interested I solved it by adding a uiimageview above the button and animate it instead of the button image.
its a hack but its working.
//set button.masktobound=NO
[UIView animateWithDuration:.15f animations:^{
self.likeButton.imageView.transform = CGAffineTransformMakeScale(2.0,2.0);
} completion:^(BOOL finished) {
[UIView animateWithDuration:.15f animations:^{
self.likeButton.imageView.transform = CGAffineTransformMakeScale(1.0,1.0);
}];
}];
//Try this
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.