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; 开发者_JS百科 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 aUIViewController
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
orresignFirstResponder:
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:
(perhapstransitionWithView: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 theUIViewAnimationOptionAllowAnimatedContent
option. - I managed to get the desired effect using any of
animateWithDuration: ... completion:
methods. These methods seems to disregard transition options likeUIViewAnimationOptionTransitionFlipFromRight
. - 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
精彩评论