I'm trying to make my first animation. Currently I'm using animateWithDuration:animations and transitionFromView:toView:duration:options:completion:, but I'm not sure if I'm in "correct page".
What I want to do is to create such animation: in the left upper corner user taps on button and UITableView arrives from the left side of the screen.
I never did any animation in iOS SDK so any help will be appreciated: similiar problems in StackOverflow, some tutorials etc. I don't know where should I begin.
Assume you have a UITableView as subview of a UIViewController's view.
First, before the animation, set the UITableView out of the screen, use center or frame property:
CGRect screenFrame = [[UIScreen mainScreen] applicationFrame];
self.theTableView.frame = CGRectMake(-screenFrame.size.width, 0.0, screenFrame.size.width, screenFrame.size.height);
Then, complete the animation code:
[UIView animateWithDuration:0.5
delay:0
options:UIViewAnimationOptionCurveEaseInOut
animations:^{
self.theTableView.frame = CGRectMake(0.0, 0.0, screenFrame.size.width, screenFrame.size.height);
}
completion:nil];
Hope it will help you! :D
Related
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 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 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.
}];
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.
I'm pretty new to iOS animations, I've been trying to accomplish something that in my head seems very simple but I am not sure what would be the best way to implement such a thing.
Usage
User clicks on a tab located on the right side of the app.
A small window slides from the side (or below doesn't really matter direction right now)
The sliding object is a UIScrollView currently
Here's a small draft so you guys have an idea
http://dl.dropbox.com/u/919254/animations.png
The black is the tab, there are severals below. When clicked, the red one shows up from a particular direction.
My questions are:
How could I accomplish an animation like that?
Any good tutorials out there you guys can refer me to?
Seeing sample code here wouldn't hurt!
In your view did load:
// Hide scrollView off screen (x=320 for iPhone/iPod)
scrollView.frame = CGRectMake(320, 0, scrollView.frame.size.width, scrollView.frame.size.height);
[self.view addSubview: scrollView];
In your method for opening the view:
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:1.0];
[UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];
// Assuming 0 is the x-coordinate of where you want your view to go
scrollView.frame = CGRectMake(0, 0, scrollView.frame.size.width, scrollView.frame.size.height);
[UIView commitAnimations];
This will animate your scrollView from the right to the left