iOS UIView setFrame animation error - ios

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.

Related

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 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

UIViewAnimation bounce after setting frame

i got a UIImageView which should slide from the top into the view and then when it stops it should make a bounce animation.
I am animating the y.position change like this:
[UIView animateWithDuration:1.3
delay:0.0
options:UIViewAnimationOptionCurveEaseIn
animations:^{
[grassView setFrame:CGRectMake(grassView.frame.origin.x, 425, grassView.frame.size.width, grassView.frame.size.height)];
[headlineView setAlpha:1.0];
[self.loginButton setAlpha:1.0];
// [textView setAlpha:1.0];
[textView setFrame:CGRectMake(textView.frame.origin.x, -136, textView.frame.size.width, textView.frame.size.height)];
}
completion:^(BOOL finished) {
self.usernameField.placeholder = NSLocalizedString(#"username", nil);
self.passwordField.placeholder = NSLocalizedString(#"password", nil);
}];
Don't worry about setting the origin.y coordinate to -136. My image view lays outside the screen at -460 because its height is 450. But when sliding the imageview into the screen i don't want to show the whole image which is why it does not animate to a y.position >= 0. But this part works just fine. My imageview is shown at the correct position where i want it to be. Now i need advise on how to let the imageview bounce when the position was changed. I already tried some CABasicAnimation but it didn't looked like i wanted it to be. Also when i call the method to let the imageview bounce in the completion block of my UIViewAnimation the bouncing effect starts to late.
Does somebody has a hint or idea for me how to get the imageview bounce after the frame was set ?
since iOS7 you can use
[UIView animateWithDuration:<#(NSTimeInterval)#> delay:<#(NSTimeInterval)#> usingSpringWithDamping:<#(CGFloat)#> initialSpringVelocity:<#(CGFloat)#> options:<#(UIViewAnimationOptions)#> animations:<#^(void)animations#> completion:<#^(BOOL finished)completion#> ]
just play around with the damping value and the initial velocity.

Weird behaviour happens when using UIView animate and CGAffineTransform

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.

UIView animation is not working as expected

I have two UIImageView filling the view controller, the first is filling the top half, and the second for the bottom half. I set them directly in the storyboard file.
In the viewDidLoad method, i am setting code in order to perform animation for the two UIImageView, so that it will look like a basket which gets opened (the first UIImageView moved to the top until it gets out of the view, and the second UIImageView moved to the bottom of the view until it gets out of the view).
This is my code so far:
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
CGRect basketTopFrame = self.basketTop.frame;
basketTopFrame.origin.y = -basketTopFrame.size.height;
CGRect basketBottomFrame = self.basketBottom.frame;
basketBottomFrame.origin.y = self.view.bounds.size.height;
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:0.5];
[UIView setAnimationDelay:1.0];
[UIView setAnimationCurve:UIViewAnimationCurveEaseOut];
self.basketTop.frame = basketTopFrame;
self.basketBottom.frame = basketBottomFrame;
[UIView commitAnimations];
}
But the behavior is different than i was expecting, the top frame is not moving, and the bottom frame is animated from top left to bottom (origin position, and not moving out of the view).
This is UIImageViews position as i set in the storyboard:
This is the UIImageViews when i launch the app:
And this is the UIImageViews when the app finish the animation(viewDidLoad):
Please note that this code works in Xcode 4.2, but since upgrading to Xcode 4.5 and using Storyboard, i begin getting this issue. Thanx in advance.
You may have auto layout enabled on your storyboard. In this case the view components will not have a valid frame value at viewDidLoad, so your code won't work.
You can disable auto layout by selecting the file inspector in the storyboard and unchecking "Use Autolayout".
In any case, that is the wrong method to be starting an animation from. Try moving the code to viewDidAppear.
Once you have "AutoLayout" turned off and if you are targeting IOS 4 and later than block based UIView animation is recommended by Apple as it simplifies the process and you can do things in one line. Please check UIView Reference for further information.
In your case , whether you plan to do the animation in ViewDidLoad / ViewWillAppear, it can be done in the following way using block based animation:
CGRect basketTopFrame = self.boxTop.frame;
CGRect basketBottomFrame = self.boxBottom.frame;
[UIView animateWithDuration: 0.5 delay:0.1 options:UIViewAnimationOptionCurveEaseOut animations:^{
[self.boxTop setFrame:CGRectMake(basketTopFrame.origin.x, -basketTopFrame.size.height, basketTopFrame.size.width, basketTopFrame.size.height)];
[self.boxBottom setFrame:CGRectMake(basketBottomFrame.origin.x, self.view.bounds.size.height, basketBottomFrame.size.width, basketBottomFrame.size.height)];
} completion:^(BOOL finished) {
// TODO : Do any additional stuffs.
}];

Resources