I'd like to get the behavior similar to Messages app (also common in most texting apps) in iOS7, where in a conversation view swiping right from the left edge of the screen would behave like the back button in a UINavigationController.
I have managed to implement this behavior, however, if the keyboard is open in the presenting view, when I start swiping back, the keyboard gets stuck and does not animate with the view to the right as I move my finger. I'd like to animate keyboard and the presenting view as one unit, not as if keyboard is on top of the other views and they are animating behind it, which is what I get now (see the second screenshot):
(UPDATE: Note that the keyboard will eventually go away after the main view animation is finished; what I am focused on is the position of keyboard during the swipe process, and when you keep touching the device half of the way, which is not in sync with the actual view. The second screenshot clarifies this desired behavior. I also wonder why it is not the default.)
It is easy to replicate the issue by simply creating a new master-detail iPhone app in Xcode 5.0.2 and adding a Text Field to the detail view (preferably somewhere in the upper half) in the StoryBoard, running the app, adding an item, tapping on it to go to the detail view and clicking on the text field you added. Edge-swipe from the left side of the device while keeping your finger on it and you'll see the issue.
Desired behavior:
Current behavior:
Unfortunately, there is no built-in method to do that. I really hope there will be something like UIScrollViewKeyboardDismissModeInteractive
for UIViewController
s.
For now, to do any animations in-between viewControllers, you should use a transitionCoordinator:
- (BOOL)animateAlongsideTransition:(void (^)(id <UIViewControllerTransitionCoordinatorContext>context))animation
completion:(void (^)(id <UIViewControllerTransitionCoordinatorContext>context))completion;
- (BOOL)animateAlongsideTransitionInView:(UIView *)view
animation:(void (^)(id <UIViewControllerTransitionCoordinatorContext>context))animation
completion:(void (^)(id <UIViewControllerTransitionCoordinatorContext>context))completion;
For the keyboard you should do something like this:
[self.transitionCoordinator animateAlongsideTransitionInView:self.keyboardSuperview
animation:
^(id<UIViewControllerTransitionCoordinatorContext> context) {
self.keyboardSuperview.x = self.view.width;
}
completion:nil];
As for keyboardSuperview
- you can get that by creating a fake inputAccessoryView
:
self.textField.inputAccessoryView = [[UIView alloc] init];
Then the superview will be self.textField.inputAccessoryView.superview
If the current firstResponder
is located inside of active UIViewController
and it dismiss throughout UINavigationController
mechanism, the expected keyboard animation (horizontal) will be performed automatically. Therefore, sometimes this default behaviour is broken by other strange factors and the keyboard starts to disappear with slide-down animation instead of horizontal animation.
I spent some days with debugging internal UIKit stuff (around methods needDeferredTransition
, allowCustomTransition
and other) to find one special factor that plays key role in my case.
I discovered that the logic inside UIPeripheralHost
checks frame
of current UIViewConroller
's view, frame
of UINavigationController
's view (container) and screen size
and, if it all doesn’t equal each other, UIPeripheralHost
decides that this current situation seems like modal window and sets flag allowCustomTransition = NO
. That turn-off UINavigationController
-specific horizontal animation.
Fixing issue with frame
s completely solves my problem.
If you are experiencing same problems, you can try to debug internal UIKit stuff around these private methods and find your conditions that turn off horizontal animation:
https://github.com/JaviSoto/iOS8-Runtime-Headers/blob/master/Frameworks/UIKit.framework/UIPeripheralHost.h
https://github.com/JaviSoto/iOS8-Runtime-Headers/blob/master/Frameworks/UIKit.framework/_UIViewControllerKeyboardAnimationStyle.h
You can use https://github.com/cotap/TAPKeyboardPop if you don't need anything special.
In my case I've got some logic connected with UIKeyboardWillShowNotification
and UIKeyboardWillHideNotification
that were fired on "swipe-to-back" gesture. I've combine this answer and TAPKeyboardPop
and this is what I've got:
- (void)beginAppearanceTransition:(BOOL)isAppearing animated:(BOOL)animated {
[super beginAppearanceTransition:isAppearing animated:animated];
if (isAppearing || !animated || !_keyboardIsShown) {
return;
}
if ([self respondsToSelector:@selector(transitionCoordinator)]) {
UIView *keyboardView = self.searchBar.inputAccessoryView.superview;
[self.searchBar becomeFirstResponder];
[self.transitionCoordinator animateAlongsideTransitionInView:keyboardView
animation:^(id<UIViewControllerTransitionCoordinatorContext> context)
{
CGRect endFrame = CGRectOffset(keyboardView.frame, CGRectGetWidth(keyboardView.frame), 0);
keyboardView.frame = endFrame;
} completion:^(id<UIViewControllerTransitionCoordinatorContext> context)
{
if (![context isCancelled]) {
[self.searchBar resignFirstResponder];
}
}];
}
}
EDIT:
I've added >iOS7 support and logic for knowing when keyboard is shown (_keyboardIsShown
is set in UIKeyboardWillShowNotification/UIKeyboardWillHideNotification
or in UIKeyboardDidHideNotification/UIKeyboardDidShowNotification
).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With