UIView not animating the first time - iphone

This code animates my view every time it's run, except for the first time. When the keyboard is displayed or hidden, a UIView is repositioned:
[UIView
animateWithDuration:0.26
animations:^{
[self setupActiveOverlayViewFrame];
}completion:nil];
-(void)setupActiveOverlayViewFrame {
float optimalOverlayHeight = [self.activePanel optimalHeight];
float realOverlayHeight = MIN(optimalOverlayHeight, self.displayView.frame.size.height);
if (self.activePanel.frame.size.height != realOverlayHeight) {
self.activePanel.frame = CGRectMake(self.activePanel.frame.origin.x, 0, self.activePanel.frame.size.width, realOverlayHeight);
}
self.activePanel.center = [self correctCenterForOverlay];
}
The method I posted is just to show that all it does is re-size and re-position it.
The first time this code is run, it doesn't animate. It just jumps into position. Every time after that, it animates correctly.

Its possible that the "keyboard did display" notification is being called before your view is fully set up, like if you've pushed a view controller onto a navigation controller and immediately give a text view focus on viewDidLoad
You can either keep track of when the keyboard is up or down and when viewDidAppear is called, check if the keyboard is up, and run it. Or you can postpone giving focus to a text view/field until viewDidAppear: is called.

I got the same problem. I want to advice you to check Panel center before(!) animating to find out has it right position at the first run. If it's not. Just set right position. Also try the next code
[self setupActiveOverlayViewFrame];
[UIView animateWithDuration:0.26
animations:^{
[self.view layoutIfNeeded];
}completion:nil];
P.S. If you are using autolayout better use constraints to move your views using the code above.

Related

How to have tabBar animate in after the view is fully pushed into view?

I am trying to emulate the way TweetBot/NetBot animates the tabBar in after a push from the tableView of Accounts action. When the view is fully pushed, only then does the taBar animate in from the bottom. I have tried all sorts of hide/show methods and all seem to fail when it comes to the "show" part.
Does anyone have a suggestion as to how this can be done?
First of all, I presume you are not using a UITabViewController since it cannot be pushed into a UINavigationController stack, so I think you are using a standalone UITabBar embedded in a UIViewController. Is this assumption right?
Try with this code (I didn't try it).
- (void)viewDidAppear {
[super viewDidAppear];
// Calls showTabBar method after SOME_DELAY. You can also call directly [self showTabBar] if you want zero delay.
[self performSelector:#selector(showTabBar) afterDelay:SOME_DELAY];
}
- (void)showTabBar {
// Before the animation begins, your UITabBar must be outside the view controller's view frame.
CGRect tabBarFrame = CGRectMake(0,
CGRectGetHeight(self.view.bounds),
CGRectGetWidth(self.view.bounds),
CGRectGetHeight(self.tabBar.frame);
self.tabBar.frame = tabBarFrame;
// Let's start with the animation, setting a new frame for tab bar inside an animation block
[UIView animateWithDuration:ANIMATION_DURATION animations:^{
// Change origin Y. It assumes that the height of self.tabBar is right, otherwise put the height you want instead of CGRectGetHeight(self.tabBar.frame).
tabBarFrame.origin.y = CGRectGetHeight(self.view.bounds) - CGRectGetHeight(self.tabBar.frame);
self.tabBar.frame = tabBarFrame;
}];
}

Making a view appear like twitter app does with new tweet (iPad)

I'm trying to achieve an effect similar to what twitter does when you bring up the new tweet dialogue. They drop down a view from the top, shrinking the other views but still allowing you to interact with all of them if you dismiss the keyboard. It obviously isn't a modal view, but I can't put my finger on what the starting point to do something similar to this would be.
It looks straight-forward as a view hierarchy, just cleverly dressed with art. The bottom is the regular interface, above is a view containing the UITextView with some nice notepad art around it.
One way to achieve this is to hang two subviews under the view controller's main view. The first child contains the notepad art and the text view. It's positioned at 0,-NOTEPAD_HEIGHT. The second child is at 0,0 and occupies the entire parent view's bounds.
The compose button tells the text view to become first responder, and when editing begins...
- (void)textViewDidBeginEditing:(UITextView *)textView {
[self setNotepadHidden:NO animated:YES];
}
I often make a show/hide method of the following form to rearrange things like this ...
- (void)setNotepadHidden:(BOOL)hidden animated:(BOOL)animated {
NSTimeInterval duration = (animated)? 0.3 : 0.0;
CGFloat offset = (hidden)? -NOTEPAD_HEIGHT : NOTEPAD_HEIGHT;
[UIView animateWithDuration:duration animations:^{
self.firstChild.frame = CGRectOffset(self.firstChild.frame, 0.0, offset);
self.secondChild.frame = UIEdgeInsetsInsetRect(self.secondChild.frame, UIEdgeInsetsMake(offset, 0, 0, 0));
}];
}
Call with ...Hidden:YES whenever you want to hide it again. Make sure that the second child's subviews have autoresizing behavior setup so that they do the right thing when their parent shrinks.
I often find the need for one like this, also...
- (BOOL)isNotepadHidden {
return self.firstChild.frame.origin.y < 0.0;
}
Hopefully, that's a good start.

UIView resize and translate animation does not animate subviews correctly

I am using a UIView animation to resize and translate a view containing multiple subviews. The animation for the parent view happens perfectly; however, the subviews exhibit strange behaviour. When the animation begins, the subviews are immediately resized and then moved to their final position.
For example, if the duration and delay of the animation was five-seconds, as soon as the animation was called, the subviews would move to the desired end-of-animation values. After five-seconds, the superview would be resized and translated to the desired.
My code for the animation is:
[UIView animateWithDuration:0.5 animations:^{
if (UIDeviceOrientationIsLandscape(self.interfaceOrientation)) {
self.leftPaneView.frame = leftPaneLandscapeFrame;
self.rightPaneContainerView.frame = rightPaneLandscapeFrame;
}
if (UIDeviceOrientationIsPortrait(self.interfaceOrientation)) {
CGFloat offset = 300;
self.leftPaneView.frame = CGRectOffset(leftPanePortraitFrame, -offset, 0);
self.rightPaneContainerView.frame = rightPanePortraitFrame;
}
}];
Any ideas?
Note: rightPaneContainerView contains the view of a UIViewController that is a child of the view controller that calls this animation.
I managed to solve the problem. The content mode for some of the views was set to Left. When the animation started, the views would jump the left, and then be animated to the desired end-of-animation value.
An amateur mistake. Thanks everyone who took a look.

UIView has gap on top after animating

I have a tabbarview application that has a button in one of the tabs. When Pressing that button, something will happen, and the user will be switched to another tab.
I made an animation in that button:
UIView * fromView = self.tabBarController.selectedViewController.view;
UIView * toView = [[self.tabBarController.viewControllers objectAtIndex:0] view];
[UIView transitionFromView:fromView
toView:toView
duration:0.6
options:(UIViewAnimationOptionTransitionCurlDown)
completion:^(BOOL finished) {
if (finished) {
self.tabBarController.selectedIndex = 0;
}
}];
Which I got from here. However the problem is that after animating, I seem to have a gap on the top of the screen that is about as high as the status bar. Does anyone know what's causing this? This gap quickly closes when the animation finishes (which is when we do self.tabBarController.selectedIndex = 0
By the way, the problem still persist if I swap the animation to something else or even without animation.
Additional info, here's the frame details:
from frame: x:0.000000, y:0.000000, w:320.000000, h:411.000000
to frame: x:0.000000, y:0.000000, w:320.000000, h:431.000000
The tab bar controller's area also covers the area underneath the status bar. So it's own client view has origin.y of 20.
Thus you need to set the incoming view frame correctly before invoking the transition.
I've found a very hacky way to do it:
CGRect to = fromView.superview.frame;
to.origin.y -= 20;
fromView.superview.frame = to;
Anyone that can explain to me why I had to do this and a more elegant way to do this will get the answer accepted.

UIView block animation transitions with animated content on showing/hiding keyboard

In my app I have a text field on some view which is covered by the keyboard when it shows up. So I have to scroll the view (or even rearrange the subviews). To do this I:
register for keyboard notifications:
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(moveViewUp)
name:UIKeyboardWillShowNotification
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(moveViewDown)
name:UIKeyboardWillHideNotification
object:nil];
upon receiving a notification, move the view using block animations like this:
- (void)moveViewUp {
void (^animations)(void) = nil;
oldViewFrame = self.view.frame;
animations = ^{
CGRect newViewFrame = oldViewFrame;
newViewFrame.origin.y -= kViewOffset;
self.view.frame = newViewFrame;
};
[UIView animateWithDuration:1.0
animations:animations];
}
- (void)moveViewDown {
void (^animations)(void) = nil;
animations = ^{
self.view.frame = oldViewFrame;
};
[UIView animateWithDuration:1.0
animations:animations];
}
This works fine, the view scrolls up and down, until I add some more animation. Specifically I'm adding a transition to a next view when the user taps a button:
- (IBAction)switchToNextView:(id)sender {
// [self presentModalViewController:nextViewController animated:YES];
[UIView transitionFromView:self.view
toView:self.nextView
duration:1.0
options:UIViewAnimationOptionTransitionFlipFromRight
completion:nil];
}
Now we got to the problem.
If the first view was shifted when the button was tapped (that means that the keyboard was visible), the transition to the next view starts simultaneously as the keyboard slides down, but the view itself doesn't move down, so for a split second we can actually see the underlying view. That's not right. When I present the next view modally (see the commented line) all animations go as I want them to: i.e. the keyboard is hiding, the view is flipping from right and scrolling down -- all at the same time. This would be fine, but the problem is that I actually don't have a UIViewController for that view. In fact I'm trying to simulate the modal behavior without UIViewController (why so? perhaps it's just a bad design, I'll post another question on that).
So why does in this case the animation from moveViewDown method is not triggered at the proper time?
Update 1
I added a debug print to each function to check the order of calling, this is what I get:
-[KeyboardAnimationViewController moveViewUp]
__-[KeyboardAnimationViewController moveViewUp]_block_invoke_1 <-- scroll up animation
-[KeyboardAnimationViewController switchToNextView:]
-[KeyboardAnimationViewController moveViewDown]
__-[KeyboardAnimationViewController moveViewDown]_block_invoke_1 <-- scroll down animation
Even if I explicitly move the view down before the transition like this
- (IBAction)switchToNextView:(id)sender {
// [self presentModalViewController:nextViewController animated:YES];
NSLog(#"%s", __PRETTY_FUNCTION__);
if (self.view.frame.origin.x < 0)
[self moveViewDown];
[UIView transitionFromView:self.view
toView:self.nextView
duration:1.0
options:UIViewAnimationOptionTransitionFlipFromRight
completion:nil];
}
I get exactly the same log.
Update 2
I've experimented some more and made following conclusions:
If I call moveViewDown or resignFirstResponder: explicitly, the animation is postponed until the end of current run loop, when all pending animations actually start to play. Though the animation block logs to the console immediately -- seems strange to me!
The method transitionFromView:toView:duration:options:completion: (perhaps transitionWithView:duration:options:animations:completion: too, didn't check this one) apparently makes a snapshot of the "from-view" and the "to-view" and creates an animation using these snapshots solely. Since the scrolling of the view is postponed, the snapshot is made when the view is still offset. The method somehow disregards even the UIViewAnimationOptionAllowAnimatedContent option.
I managed to get the desired effect using any of animateWithDuration: ... completion: methods. These methods seems to disregard transition options like UIViewAnimationOptionTransitionFlipFromRight.
The keyboard starts hiding (implicitly) and sends corresponding notification when removeFromSuperview is called.
Please correct me if I'm wrong somewhere.
If you are trying to simulate the modal behavior without UIViewController I guess you want your next view to show up from the bottom of the screen right?. correct me if I am wrong.
If you want such an animation you can try a work-around where you change the frame of the next view within an animation block such that it appears as if its similar to presentModalViewController