IOS: View Translation Animation. Proper way. - ios

I'm implementing my own Navigation Controller and I'm faking the animation of adding the new view as in UINavigationContoller.
The following code works great, however if I move -addSubview:, before UIView animation call, it will animate in ios7, but not in ios6, the question is why? (I mean, why does it would make a difference, as animation will be scheduled and executed asynch, right? Or I might suppose It will somehow affect the starting state of animation?)
- (void)didAddViewControllerInNavigationStack:(NSArray*)navigationStack
{
if (self.activeViewController) {
[self.activeViewController viewWillDisappear:YES];
[self.activeViewController viewDidDisappear:YES];
}
self.activeViewController = navigationStack.firstObject;
if (!self.activeViewController) return;
CGRect windowRect = [[UIScreen mainScreen] bounds];
windowRect.origin.x = windowRect.size.width;
self.activeViewController.view.frame = windowRect;
[self.activeViewController viewWillAppear:YES];
[UIView transitionWithView:self.view
duration:0.32
options:UIViewAnimationOptionCurveEaseOut
animations:^ { self.activeViewController.view.frame = [[UIScreen mainScreen] bounds]; }
completion:^(BOOL isDone){[self.activeViewController viewDidAppear:YES];}];
[self.view addSubview:self.activeViewController.view];
[UIView commitAnimations];
}

From the documentation of transitionWithView:duration:options:animations:completion:, it is said you should add the subview inside the animation block:
The block you specify in the animations parameter contains whatever state changes you want to make. You can use this block to add, remove, show, or hide subviews of the specified view.
Also, you are calling commitAnimations without having called beginAnimations:context:. You are mixing things here. Block-based animation API does not need the begin-end API.
Your code should be:
[UIView transitionWithView:self.view
duration:0.32
options:UIViewAnimationOptionCurveEaseOut
animations:^ { [self.view addSubview:self.activeViewController.view]; self.activeViewController.view.frame = [[UIScreen mainScreen] bounds]; }
completion:^(BOOL isDone){[self.activeViewController viewDidAppear:YES];}];

Related

IOS stop UIbutton to take its own place after animation completed

I have set the animation like seen in below image. In which UIbutton move from left to right and then top to bottom. Animation work correctly but after completion of animation UIButton comes to its original place before the segue perform. so, it's not look good. I want to set that after the completion of animation UIButton can't come to it's own place before segue .
Here is my try with Image.
//Move button Left to Right
- (IBAction)btnEasy:(id)sender {
Easy=YES;
NSLog(#"your x is: %f ", self.btnEasy.frame.origin.x);
NSLog(#"your y is: %f ", self.btnEasy.frame.origin.y);
x1=self.btnEasy.frame.origin.x;
y1=self.btnEasy.frame.origin.y;
CGRect screenRect = [[UIScreen mainScreen] bounds];
[UIView animateWithDuration:1.150 delay:0 options:UIViewAnimationOptionCurveEaseInOut animations:^{
self.btnEasy.frame = CGRectMake(screenRect.size.width/1.80, self.btnEasy.frame.origin.y, self.btnEasy.frame.size.width, self.btnEasy.frame.size.height);
[self performSelector:#selector(btneasyanimation) withObject:self afterDelay:1.160 ];}
completion:^(BOOL finished) {
}];
//Move Button top to Bottom
if (Easy==YES) {
if (isiPad2 || isiPadAir || isiPadRatina) {
//[self.view layoutIfNeeded];
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationBeginsFromCurrentState:YES];
[UIView setAnimationDuration:1.0];
[_btnEasy setFrame:CGRectMake(self.view.frame.origin.x+290, self.view.frame.origin.y+900, self.btnEasy.frame.size.width, self.btnEasy.frame.size.height)];
[UIView commitAnimations];
}
else if (isiPhone4s) {
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationBeginsFromCurrentState:YES];
[UIView setAnimationDuration:1.0];
[_btnEasy setFrame:CGRectMake(self.view.frame.origin.x+92, self.view.frame.origin.y+428, self.btnEasy.frame.size.width, self.btnEasy.frame.size.height)];
[UIView commitAnimations];
}
}
[self performSelector:#selector(segueeMethod) withObject:self afterDelay:1.160 ];
Image :-
If you are not placing directly the button given it the frame, thus using autolayout (autoresize will end with the same effect). You need to explicitly use autolayout, retain a reference to your constraints and update them (you can search here how to do that) and then set the UIView animation block [button layoutIfNeeded]
You can see a detailed answer about it in this answer
There is an easy and quick way that will work with your current implementation. Provided you know exactly where you want the view to be once the animation is done, you can do the following:
in UIView animateWithDuration: ... assign the final transform (position & orientation) of the view in the completion block, i.e.:
completion:^(BOOL finished) {
// Assign views transform and appearance here, for when the animation completes
}
Hope that helps.

scrollView.setContentOffset does not scroll the view, at all

I've been following this and a bunch of other tutorials/blogs,etc trying to get my view to scroll when the user hits "return" and ends on a UITextField that is being covered by the keyboard but nothing is actually working.
I'm wondering what must I be doing or missing that is causing this?
Basically:
the user wants to do something in the app: add a credit card
show a UIView that contains all the CC fields
1/2 of the UITextFields are covered by the keyboard
scroll the view when the user gets there.
Nothing is happening. The code for detecting the 'covered by keyboard' case is being hit and executed but: [self.scrollView setContentOffset:scrollPoint animated:YES] has no effect at all.
Thoughts?
Reveal screenshot:
code: same as the tutorial in the link. Nothing new there.
Did you check the contentSize property?
To make a scroll view scrollable, the content must be larger than the display area(usually the bounds of the scrollview).
You can achieve this by explicitly setting the contentSize property which is CGSizeZero by default
In addition, setting the contentInset property to adjust the size of display area(in this case you can set bottom value equals keyboard height) when the keyboard is popped up
Try this,
- (void)textFieldDidBeginEditing:(UITextField *)textField
{
[UIView animateWithDuration:0.25 animations:^{
[yourTextField setFrame:CGRectMake(yourTextField.frame.origin.x, yourTextField.frame.origin.y-keyboardHeight, yourTextField.frame.size.width, yourTextField.frame.size.height)];
}];
}
and
- (void)textFieldDidEndEditing:(UITextField *)textField
{
[UIView animateWithDuration:0.25 animations:^{
[yourTextField setFrame:CGRectMake(yourTextField.frame.origin.x, yourTextField.frame.origin.y+keyboardHeight, yourTextField.frame.size.width, yourTextField.frame.size.height)];
}];
}
What I do to avoid the textField being covered by the keyboard is to animate the textfield to the top of the screen when the user touches it to start entering something with the keyboard and animate it back when done. It is much more elegant in my opinion. Maybe that could be an alternative solution for you... I do it for the different screensizes with different numbers... you might need to adjust it of course...
So listen to the textField delegate and in
- (void)textFieldDidBeginEditing:(UITextField *)textField {
you do
CGSize result = [[UIScreen mainScreen] bounds].size;
if(result.height == 480){
({[UIView beginAnimations:nil context:NULL]; [UIView setAnimationDuration:0.35f]; CGRect frame = self.view.frame; frame.origin.y = -196; [self.view setFrame:frame]; [UIView commitAnimations];});
}
if(result.height == 568){
({[UIView beginAnimations:nil context:NULL]; [UIView setAnimationDuration:0.35f]; CGRect frame = self.view.frame; frame.origin.y = -196; [self.view setFrame:frame]; [UIView commitAnimations];});
}
if(result.height == 667){
({[UIView beginAnimations:nil context:NULL]; [UIView setAnimationDuration:0.35f]; CGRect frame = self.view.frame; frame.origin.y = -238; [self.view setFrame:frame]; [UIView commitAnimations];});
}
if(result.height == 736){
({[UIView beginAnimations:nil context:NULL]; [UIView setAnimationDuration:0.35f]; CGRect frame = self.view.frame; frame.origin.y = -251; [self.view setFrame:frame]; [UIView commitAnimations];});
}
and in
- (void)textFieldDidEndEditing:(UITextField *)textField {
you just animate back
({[UIView beginAnimations:nil context:NULL]; [UIView setAnimationDuration:0.35f]; CGRect frame = self.view.frame; frame.origin.y = 0; [self.view setFrame:frame]; [UIView commitAnimations];});

How to stop reloadData from moving a table view

I have a table view that I animate to a new position when pressing a button. It covers a small portion of the screen at the bottom and then pressing the button calls a method that makes it cover about half the screen. This way the user can choose a file from the table view.
However, it seems that when I call reloadData, the table view moves back to its original position, and I would like to stop that from happening so that the user doesn't need to press the button every time (such as with deleting a file, renaming a file, or adding a new file to the table view).
Any ideas how I could accomplish this?
I've included code. The file IBAction is what happens when the button is pressed.
-(IBAction)file:(id)sender {
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:0.2];
if (self.tableView.frame.origin.y == 457) {
[self tableAppear];
}
else {
[self tableDisappear];
}
}
-(void)tableDisappear {
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:0.2];
if ([[UIScreen mainScreen] bounds].size.height == 568) { [self.tableView setCenter:CGPointMake(160, 545)];
}
else { [self.tableView setCenter:CGPointMake(160, 545)];
}
[UIView commitAnimations];
}
-(void)tableAppear {
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:0.2];
if ([[UIScreen mainScreen] bounds].size.height == 568) { [self.tableView setCenter:CGPointMake(160, 390)];
}
else { [self.tableView setCenter:CGPointMake(160, 390)];
}
[UIView commitAnimations];
}
This is most likely a consequence of auto layout (when the screen needs to be redrawn, all views will revert to the positions determined by their constraints rather then any frames that you set). You can turn off auto layout to see if this is the problem. If you want to use auto layout, then you need to do any moving or resizing of view using constraints rather than setting views.

An example of how UIViewAnimationOptionLayoutSubviews works?

Apple's documentation describes UIViewAnimationOptionLayoutSubviews as:
Lay out subviews at commit time so that they are animated along with
their parent.
Here is a sample of the code I'm interested in. I wish to animate the -layoutSubviews of detailView; however, it doesn't seem to layout the subviews of detailView, so I'm not sure what effect it actually has.
void (^animation) () = ^
{
[self.detailView setNeedsLayout];
[self.detailView layoutIfNeeded];
};
[UIView animateWithDuration:0.5
delay:0.0
options:UIViewAnimationOptionLayoutSubviews
animations:^{
animation();
}
completion:nil];
Since you want your second animation to occurs from the current state of your first animation (whether it is finished or not) I recommend to use the UIViewAnimationOptionLayoutSubviews option when setting your second animation.
[UIView animateWithDuration:0.2
delay:0.0
options:UIViewAnimationOptionLayoutSubviews
animations:^{
CGAffineTransform settingsTransform = CGAffineTransformMakeTranslation(self.animatedView.frame.size.width, 0);
self.animatedView.transform = settingsTransform;
}
completion:nil];

Am I releasing this object too early after the animation?

I have a UIView and I am animating it.
The thing here is that I need to release theView once it is animated out.
originalRECT = [[UIScreen mainScreen] bounds];
if(theView)
{
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:0.3];
[UIView setAnimationCurve:UIViewAnimationCurveEaseOut];
theView setFrame = originalRECT;
[UIView commitAnimations];
[theView autorelease];
theView = nil;
}
So I know the code is setting theView to nil, but the animation does finish ok (No SIGABRT or something like that)
Or is there a callback function I can use to know that the view disappeared? How can I use a function like that in this case?
Thanks!
Few things -
First of all if you want to be sure you can use:
[UIView animateWithDuration:1.0
delay:0.0
options:UIViewAnimationCurveEaseInOut
animations:^ {
//your animation
theView setFrame = originalRECT;
}
completion:^(BOOL finished) {
//Animation finished you can release
[theView release];
}];
second - why do you use autoRelease and not release?
[theView release];
Third - Just for w meeter you don't know. USE ARC it will make your life a bit easier. You can read more here ARC tutorial

Resources