In our iOS 8 app, the search screen, which is similar to the search screen of the App Store app, no longer works reliably. When a user taps a key, the keyboard is sometimes closed or even an action executed.
Part of the reason is that the tap event is passed on to lower layers, which is close the keyboard (smoke screen), navigate to a search result (UITableView with search result) or execute the search (UITableView with search term suggestions).
For some unknown reason, it properly works as long as the user stays in the app. However, if he/she goes to a different app and then returns, the events are passed on. This behavior affects all iOS 8 version (8.0.x, 8.1).
How can we prevent the keyboard from passing on tap events or how can we detect such an event (e.g. from tableView:didSelectRowAtIndexPath:)?
The question "Keyboard intermittently disappears when editing using IOS 8" seems to refer to the same problem though I can't figure out how to apply that ugly hack to my situation.
I've just found a similar post in Apple's developer forum. Unfortunately, it has no answers and has been archived in the mean time:
I have overriden -hitTest:withEvent: on a view on my view hierarchy
where I check if it was touched and will forward the touch to its
subviews and fire a selector to dismiss the keyboard.
On iOS 7 (and, more strangely, when the app is launched on iOS 8) this
works perfectly and -hitTest:withEvent: will never be called if the
view is behind the keyboard and the user taps on the keyboard.
But on iOS 8, if the user sends the app to the background and brings
it back to the foreground, tapping anything on the keyboard will
trigger -hitTest:withEvent: as if the view was above the keyboard on
the view hierarchy. I've used Reveal.app to verify that it is not
above the keyboard, it is behind as expected.
Anyone got any ideas of what could be happening? I've created a sample
project and attached it to a radar for Apple as this looks like a bug
on iOS 8 for not working the same way consistently.
Update
My search screen contains two views (on top of each other): a background view visible when no search results are available and a table view visible if search results are available. On top of these, I dynamically add two additional views if the search bar becomes active: a smoke class view that can be tapped to end the search text entry and a table view that displays search text suggestions. All four views are directly contained in the view controller's main view and cover the full area.
The interesting thing now is that the keyboard forwards event the two dynamically added views but not to the two lower views that are always there.
I believe it's a bug that tap events are passed on to views underneath the keyboard. As I've figured out in the mean time that only the dynamically added views are affected and not the ones that are part of the Interface Builder file, I've now come up with a work around: if the keyboard appears, I shrink these two view so they do not extend underneath the keyboard. And I grow them again when the keyboard disappears.
So this is the code. The upper part up to (and excluding) [[NSNotificationCenter defaultCenter] has existed before. The rest is the workaround.
- (BOOL) searchBarShouldBeginEditing: (UISearchBar*) searchBar {
// calculate from for entire area
CGRect frame = ... // omitted
// add smoke screen
_smokeScreen = [UIButton buttonWithType: UIButtonTypeCustom];
_smokeScreen.frame = frame;
_smokeScreen.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
_smokeScreen.backgroundColor = [UIColor colorWithWhite: 0.0f alpha: 0.5f];
[_smokeScreen addTarget: self action: #selector(smokeScreenPressed:) forControlEvents: UIControlEventTouchDown];
[self.view addSubview: _smokeScreen];
// add table view for search term suggestions
_suggestionTableView = [[SearchControllerTableView alloc] initWithFrame:frame style:UITableViewStylePlain];
_suggestionTableView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
_suggestionTableView.dataSource = self;
_suggestionTableView.delegate = self;
_suggestionTableView.searchController = self;
_suggestionTableView.hidden = YES;
[self.view addSubview:_suggestionTableView];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyboardDidShow:)
name:UIKeyboardDidShowNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyboardWillHide:)
name:UIKeyboardWillHideNotification object:nil];
return YES;
}
- (void) keyboardDidShow:(NSNotification *) notification
{
// shrink the smoke screen area and the table view because otherwise they'll receive tap events from the keyboard
CGRect screenRect = [[[notification userInfo] objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue];
CGRect windowRect = [self.view.window convertRect:screenRect fromWindow:nil];
CGRect viewRect = [self.view convertRect:windowRect fromView:nil];
[self setBottom: viewRect.origin.y];
}
- (void) keyboardWillHide:(NSNotification *) notification
{
// grow the views again
[self setBottom: self.view.frame.size.height];
}
- (void) setBottom: (CGFloat)y
{
CGRect frame = _suggestionTableView.frame;
frame.size.height = y - frame.origin.y;
_suggestionTableView.frame = frame;
frame = _smokeScreen.frame;
frame.size.height = y - frame.origin.y;
_smokeScreen.frame = frame;
}
You can try to play with view property userInteractionEnabled or
exclusiveTouch when search view called.
I come across this problem with Xamarin.iOS, this is the solution for Xamarin.iOS use C#.
In WillMoveToSuperview method, observe keyboard show/hide event and store the keyboard frame in variable KeyboardEndFrame:
if (newsuper != null)
{
var wrThis = new WeakReference<XInterceptTouchView> (this);
// add notification observe
keyboardDidShow = UIKeyboard.Notifications.ObserveDidShow ((sender, e) =>
{
XInterceptTouchView interceptTouchView;
if (wrThis.TryGetTarget (out interceptTouchView))
{
interceptTouchView.KeyboardEndFrame = e.FrameEnd;
}
});
keyboardDidHide = UIKeyboard.Notifications.ObserveDidHide ((sender, e) =>
{
XInterceptTouchView interceptTouchView;
if (wrThis.TryGetTarget (out interceptTouchView))
{
interceptTouchView.KeyboardEndFrame = e.FrameEnd;
}
});
}
else
{
// remove notification observe
keyboardDidShow?.Dispose ();
keyboardDidHide?.Dispose ();
}
In HitTest method, get the keyboard window and process touch event with the window:
if (KeyboardEndFrame.Contains (point))
{
IntPtr handle = ObjCRuntime.Class.GetHandle ("UITextEffectsWindow");
if (handle != IntPtr.Zero)
{
var keyboardWindow = UIApplication.SharedApplication.Windows.FirstOrDefault (w => w.IsKindOfClass (new ObjCRuntime.Class ("UITextEffectsWindow")));
if (keyboardWindow != null)
return keyboardWindow.HitTest (point, uievent);
}
}
var hitTestView = base.HitTest (point, uievent);
this.EndEditing (true);
return hitTestView;
I found that keyboard touches trigger hitTest if I show the keyboard, send the application to the background, and come back. This shouldn’t happen so it looks like an iOS bug. I see two solutions:
#1 Send the touch away
Solution #1: hitTest shouldn’t be called for touches on the keyboard, so check against the keyboard frame and send the touch away.
var keyboardFrame: CGRect?
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView?
{
if let keyboardFrame = keyboardFrame {
if keyboardFrame.contains(point){
return super.hitTest(CGPoint(x:-1,y:-1), with: event)
}
}
return super.hitTest(point, with: event)
}
deinit {
NotificationCenter.default.removeObserver(self)
}
func subscribeKeyboard(){
NotificationCenter.default.addObserver(self, selector: #selector(MyView.keyboardDidShow(_:)), name: NSNotification.Name.UIKeyboardDidShow, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(MyView.keyboardWillHide(_:)), name: NSNotification.Name.UIKeyboardWillHide, object: nil)
}
func keyboardDidShow(_ notification: NSNotification) {
keyboardFrame = (notification.userInfo![UIKeyboardFrameEndUserInfoKey] as! NSValue).cgRectValue
}
func keyboardWillHide(_ notification: NSNotification) {
keyboardFrame = nil
}
#2 Pull the keyboard before resigning
Solution 2#: Pull the keyboard before resigning active. Two flaws: you are touching the delegate to fix something elsewhere, and the user is surprised to find the keyboard down when the app comes back.
func applicationWillResignActive(_ application: UIApplication) {
UIApplication.shared.sendAction(#selector(self.resignFirstResponder), to: nil, from: nil, for: nil)
}
Or if you want this done for a particular view controller only:
if let controller = UIApplication.shared.keyWindow?.topmostViewController {
if controller is MyViewController {
NSLog("Pulling the keyboard to prevent touches from falling through after coming back from the background.")
UIApplication.shared.sendAction(#selector(self.resignFirstResponder), to: nil, from: nil, for: nil)
}
}
where topmostViewController is:
extension UIViewController {
static var topmostViewController: UIViewController? {
return UIApplication.shared.keyWindow?.topmostViewController
}
var topmostViewController: UIViewController? {
return presentedViewController?.topmostViewController ?? self
}
}
extension UINavigationController {
override var topmostViewController: UIViewController? {
return visibleViewController?.topmostViewController
}
}
extension UITabBarController {
override var topmostViewController: UIViewController? {
return selectedViewController?.topmostViewController
}
}
extension UIWindow {
var topmostViewController: UIViewController? {
return rootViewController?.topmostViewController
}
}
Leonardo Cardoso wrote the extensions above.
I have a view controller that can be popped with the new interactivePopGestureRecognizer. If there is a keyboard present and the swipe animation begins the keyboard does not move with the view. I have had a look at this question and implemented it like this in my view controller that gets dismissed
-(void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
[self.transitionCoordinator animateAlongsideTransitionInView:self.aTextInputView.keyboardSuperView animation:^(id<UIViewControllerTransitionCoordinatorContext> context) {
CGRect frame = self.aTextInputView.keyboardSuperView.frame;
frame.origin.x = self.view.frame.size.width;
self.aTextInputView.keyboardSuperView.frame = frame;
} completion:nil];
}
Now what I get when the view animates to disappear is the keyboard animates off the screen to the x point of 320 which makes sense as thats what I set it to, my question is how do I get the keyboard to animate with the swipe back?
Update
For any one that sees a weird animation when the view disappears you can get remove the keyboard by doing this.
[self.transitionCoordinator notifyWhenInteractionEndsUsingBlock:^(id<UIViewControllerTransitionCoordinatorContext> context){
if (![context isCancelled]) {
[keyboardSuperview removeFromSuperview];
}
}];
You have a lot of custom code in your snippet, so correct me if I am wrong, but it seems you have incorrect self.aTextInputView.keyboardSuperView.
Double check that it is not nil. If it is, you forgot to add an inputAccessoryView.
Here is the full code snippet without any extensions:
- (void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
UIView *keyboardSuperview = self.textField.inputAccessoryView.superview;
[self.transitionCoordinator animateAlongsideTransitionInView:keyboardSuperview
animation:
^(id<UIViewControllerTransitionCoordinatorContext> context) {
CGRect keyboardFrame = keyboardSuperview.frame;
keyboardFrame.origin.x = self.view.bounds.size.width;
keyboardSuperview.frame = keyboardFrame;
}
completion:nil];
}
- (void)viewDidLoad
{
[super viewDidLoad];
self.textField.inputAccessoryView = [[UIView alloc] init];
}
Just found really simple solution for iOS8
- (void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
[self.aTextInputView resignFirstResponder];
}
In my app I have a view that is a form that has quite a few inputs.
When the UITextField calls textFieldDidBeginEditing, it checks the tag and will bring up a UIPopoverController or the keyboard depending on the what the input is meant to be.
If the keyboard is up, I need it disappear when the user presses a textfield that brings up the popover. However I cannot make it disappear, I have tried every way to get rid of the keyboard but it just stays there. I have tried:
calling resignFirstResponder in textFieldDidEndEditing
calling [self.view endEditing:YES] in textFieldDidEndEditing
calling resignFirstResponder AND [self.view endEditing:YES] in textFieldDidBeginEditing checking for the previous tag is equal to a keyboard input text field.
Any ideas would be great.
I have ripped it out and and put it in a example project if anyone wants to see the exact behaviour.
http://dl.dropbox.com/u/61692457/KB_Test.zip
Declare a Global UITextField in .h file
UITextField *txtfld;
Replace Your method textFieldDidBeginEditing with textFieldShouldBeginEditing and now write this code
- (BOOL)textFieldShouldBeginEditing:(UITextField *)textField
{
if (textField.tag == 1 || textField.tag==3)
{
if(numPickerPopover == nil)
{
numPicker = [[[NumPicker alloc] initWithStyle:UITableViewStylePlain] autorelease];
numPicker.delegate = self;
numPickerPopover = [[UIPopoverController alloc] initWithContentViewController:numPicker];
[numPickerPopover setPopoverContentSize:CGSizeMake(60.0, 260.0f)];
}
[numPickerPopover presentPopoverFromRect:textField.frame inView:self.view permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES];
[txtfld resignFirstResponder];
return NO;
}
if (textField.tag == 2)
{
txtfld = textField;
return YES;
}
return YES;
}
To dismiss the keyboard when the user touches the textField that brought it up, add this method:
- (IBAction)dismissKeyboard:(id)sender {
[textField resignFirstResponder];
}
In Interface Builder, connect this method to the textField event you want, like touch up inside (or whatever is more appropriate).
I have an app where, in Interface Builder, I set up a UIView that has a text field near the bottom of the view. When I run the app and try to enter text into that field, the keyboard slides up overtop of the field so I can't see what I'm typing until I hide the keyboard again.
Has anyone else run into this problem and found a good way to solve it without either making the parent view scrollable or moving the text field farther up the screen?
The usual solution is to slide the field (and everything above it) up with an animation, and then back down when you are done. You may need to put the text field and some of the other items into another view and slide the view as a unit. (I call these things "plates" as in "tectonic plates", but that's just me). But here is the general idea if you don't need to get fancy.
- (void)textFieldDidBeginEditing:(UITextField *)textField
{
[self animateTextField: textField up: YES];
}
- (void)textFieldDidEndEditing:(UITextField *)textField
{
[self animateTextField: textField up: NO];
}
- (void) animateTextField: (UITextField*) textField up: (BOOL) up
{
const int movementDistance = 80; // tweak as needed
const float movementDuration = 0.3f; // tweak as needed
int movement = (up ? -movementDistance : movementDistance);
[UIView beginAnimations: #"anim" context: nil];
[UIView setAnimationBeginsFromCurrentState: YES];
[UIView setAnimationDuration: movementDuration];
self.view.frame = CGRectOffset(self.view.frame, 0, movement);
[UIView commitAnimations];
}
This worked wonders for me sliding uitextfields
In particular it has the benefit of calculating the slide animation distance depending on the position of the text field.
IQKeyboardManager do this for you with NO LINE OF CODE, only need to drag and drop related source file to project. IQKeyboardManager also support Device Orientation, Automatic UIToolbar Management, keyboardDistanceFromTextField and much more than you think.
Here is the Control Flow Chart:
Step1:- Added global notifications of UITextField, UITextView, and UIKeyboard in a singleton class. I called it IQKeyboardManager.
Step2:- If found UIKeyboardWillShowNotification, UITextFieldTextDidBeginEditingNotification or UITextViewTextDidBeginEditingNotification notifications, then try to get topMostViewController instance from the UIWindow.rootViewController hierarchy. In order to properly uncover UITextField/UITextView on it, topMostViewController.view's frame needs to be adjusted.
Step3:- Calculated expected move distance of topMostViewController.view with respect to first responded UITextField/UITextView.
Step4:- Moved topMostViewController.view.frame up/down according to the expected move distance.
Step5:- If found UIKeyboardWillHideNotification, UITextFieldTextDidEndEditingNotification or UITextViewTextDidEndEditingNotification notification, then again try to get topMostViewController instance from the UIWindow.rootViewController hierarchy.
Step6:- Calculated disturbed distance of topMostViewController.view which needs to be restored to it's original position.
Step7:- Restored topMostViewController.view.frame down according to the disturbed distance.
Step8:- Instantiated singleton IQKeyboardManager class instance on app load, so every UITextField/UITextView in the app will adjust automatically according to the expected move distance.
That's all
To expand on Amagrammer answer, here is a sample class:
LoginViewController.h
#interface LoginViewController : UIViewController <UITextFieldDelegate> {
}
#property (nonatomic, retain) IBOutlet UITextField *emailTextField;
#property (nonatomic, retain) IBOutlet UITextField *passwordTextField;
Notice we are implementing the "UITextFieldDelegate"
LoginViewController.m
#implementation LoginViewController
#synthesize emailTextField=_emailTextField;
#synthesize passwordTextField=_passwordTextField;
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
//Register to receive an update when the app goes into the backround
//It will call our "appEnteredBackground method
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(appEnteredBackground)
name:UIApplicationDidEnterBackgroundNotification
object:nil];
}
return self;
}
- (void) animateTextField: (UITextField*) textField up: (BOOL) up
{
const int movementDistance = 80; // tweak as needed
const float movementDuration = 0.3f; // tweak as needed
int movement = (up ? -movementDistance : movementDistance);
[UIView beginAnimations: #"anim" context: nil];
[UIView setAnimationBeginsFromCurrentState: YES];
[UIView setAnimationDuration: movementDuration];
self.view.frame = CGRectOffset(self.view.frame, 0, movement);
[UIView commitAnimations];
}
- (void)textFieldDidBeginEditing:(UITextField *)textField
{
[self animateTextField: textField up: YES];
}
- (void)textFieldDidEndEditing:(UITextField *)textField
{
[self animateTextField: textField up: NO];
}
- (BOOL)textFieldShouldReturn:(UITextField *)textField
{
[textField resignFirstResponder];
return YES;
}
//This is called when the app goes into the background.
//We must reset the responder because animations will not be saved
- (void)appEnteredBackground{
[self.emailTextField resignFirstResponder];
[self.passwordTextField resignFirstResponder];
}
How about the official solution: Moving Content That Is Located Under the Keyboard
Adjusting your content typically involves temporarily resizing one or
more views and positioning them so that the text object remains
visible. The simplest way to manage text objects with the keyboard is
to embed them inside a UIScrollView object (or one of its subclasses
like UITableView). When the keyboard is displayed, all you have to do
is reset the content area of the scroll view and scroll the desired
text object into position. Thus, in response to a
UIKeyboardDidShowNotification, your handler method would do the
following:
Get the size of the keyboard.
Adjust the bottom content inset of your scroll view by the keyboard
height.
Scroll the target text field into view.
// Call this method somewhere in your view controller setup code.
- (void)registerForKeyboardNotifications
{
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(keyboardWasShown:)
name:UIKeyboardDidShowNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(keyboardWillBeHidden:)
name:UIKeyboardWillHideNotification object:nil];
}
// Called when the UIKeyboardDidShowNotification is sent.
- (void)keyboardWasShown:(NSNotification*)aNotification
{
NSDictionary* info = [aNotification userInfo];
CGSize kbSize = [[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;
UIEdgeInsets contentInsets = UIEdgeInsetsMake(0.0, 0.0, kbSize.height, 0.0);
scrollView.contentInset = contentInsets;
scrollView.scrollIndicatorInsets = contentInsets;
// If active text field is hidden by keyboard, scroll it so it's visible
// Your app might not need or want this behavior.
CGRect aRect = self.view.frame;
aRect.size.height -= kbSize.height;
if (!CGRectContainsPoint(aRect, activeField.frame.origin) ) {
[self.scrollView scrollRectToVisible:activeField.frame animated:YES];
}
}
// Called when the UIKeyboardWillHideNotification is sent
- (void)keyboardWillBeHidden:(NSNotification*)aNotification
{
UIEdgeInsets contentInsets = UIEdgeInsetsZero;
scrollView.contentInset = contentInsets;
scrollView.scrollIndicatorInsets = contentInsets;
}
I have face the same issue in UITableView textField cells. I solve this issue by implementing following method to listen the keyboard notification.
Observer for the notifications here:
[[NSNotificationCenter defaultCenter]addObserver:self selector:#selector(keyboardWasShown:) name:UIKeyboardWillShowNotification object:nil];
[[NSNotificationCenter defaultCenter]addObserver:self selector:#selector(keyboardWillBeHidden:) name:UIKeyboardWillHideNotification object:nil];
Handle those notification by using below function:
(void)keyboardWasShown:(NSNotification*)aNotification
(void)keyboardWillBeHidden:(NSNotification*)aNotification
Check this out.
No hassle for you.
This solution is very neat. All you have to do is to add your textfields in a UIScrollView and change its class to TPKeyboardAvoidingScollView, if you are using storyboards. The scroll view is extended in such a way that it would detect when keyboard is visible and will move itself above keyboard at a reasonable distance. It is perfect solution because its independent of your UIViewController. Every necessary thing is done within the the above mentioned class. Thanks Michael Tyson et all.
TPKeyboardAvoiding
Below is a swift version of Amagrammer's answer. Also, a variation using the UIKeyboardWillShowNotification event since I needed to know the keyboards size before moving the view out of the way.
var keyboardHeight:CGFloat = 0
override func viewDidLoad() {
super.viewDidLoad()
NSNotificationCenter.defaultCenter().addObserver(self, selector: "keyboardWillChange:", name: UIKeyboardWillShowNotification, object: nil)
}
func textFieldDidBeginEditing(textField: UITextField) {
//keyboardWillChange (below) is used instead of textFieldDidBeginEditing because textFieldDidBeginEditing
//is called before the UIKeyboardWillShowNotification necessary to determine the keyboard height.
}
func textFieldDidEndEditing(textField: UITextField) {
animateTextField(false)
}
func animateTextField(textFieldUp:Bool) {
let movementDistance:CGFloat = keyboardHeight
let movementDuration = 0.3
let movement:CGFloat = (textFieldUp ? -movementDistance : movementDistance)
UIView.beginAnimations("anim", context: nil)
UIView.setAnimationBeginsFromCurrentState(true)
UIView.setAnimationDuration(movementDuration)
self.view.frame = CGRectOffset(self.view.frame, 0, movement)
UIView.commitAnimations()
}
func keyboardWillChange(notification:NSNotification) {
let keyboardRect:CGRect = ((notification.userInfo![UIKeyboardFrameEndUserInfoKey])?.CGRectValue)!
keyboardHeight = keyboardRect.height
animateTextField(true)
}
There was a great walkthrough at editing textfields without obscuring (link dead now, here's a Wayback link: https://web.archive.org/web/20091123074029/http://acts-as-geek.blogspot.com/2009/11/editing-textfields-without-obscuring.html). It shows how to move an existing UIView onto a UIScrollView, and to scroll it automatically when the keyboard appears.
I've updated it a bit to calculate the correct height for the UIScrollView when there are controls (such as a UITabBar) below the UIScrollBar. See post updating uiview.
Here's a solution using Xcode5, iOS7:
Use the UITextfieldDelegate and animation blocks.
This is nearly all the code for the ViewController but I wanted to include the delegate code for those still somewhat unfamiliar with the delegate pattern (like me). I also included code to hide the keyboard when you tap away from the textview.
You can move the views(buttons, textfields, etc) as high as you'd like just make sure to put them back in place (+100 then later -100).
#interface ViewController () <UITextFieldDelegate>
#property (strong, nonatomic) IBOutlet UITextField *MyTextField;
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
self.MyTextField.delegate = self;
}
- (void)textFieldDidBeginEditing:(UITextField *)textField
{
NSLog(#"text began editing");
CGPoint MyPoint = self.MyTextField.center;
[UIView animateWithDuration:0.3
animations:^{
self.MyTextField.center = CGPointMake(MyPoint.x, MyPoint.y - 100);
}];
}
- (void)textFieldDidEndEditing:(UITextField *)textField
{
NSLog(#"text ENDED editing");
CGPoint MyPoint = self.MyTextField.center;
[UIView animateWithDuration:0.3
animations:^{
self.MyTextField.center = CGPointMake(MyPoint.x, MyPoint.y + 100);
}];
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
[self.view endEditing:YES];
}
I guess one way would be to move your whole views position from (x,y) to (x,y-keybaardHeight) when the textfield is clicked and put it back when the keyboard is dismissed , might look a little odd as the view just comes up (maybe it wouldnt be bad if you animate it).
- (void)textFieldDidBeginEditing:(UITextField *)textField
{
CGRect frame=self.view.frame;
frame.origin=CGPointMake(x...//set point here
self.view.frame=frame;
}
In addition to Amagrammer's solution, if you are using cocos2d in portrait mode change this line:
self.view.frame = CGRectOffset(self.view.frame, 0, movement);
to this:
[CCDirector sharedDirector].openGLView.frame = CGRectOffset([CCDirector sharedDirector].openGLView.frame, movement, 0);
If you are using cocos2d in landscape mode, make the above change and switch the up values in textFieldDidBeginEditing: and textFieldDidEndEditing:
- (void)textFieldDidBeginEditing:(UITextField *)textField {
[self animateTextField:textField up:NO];
}
- (void)textFieldDidEndEditing:(UITextField *)textField {
[self animateTextField:textField up:YES];
}
I had the same problem and found GTKeyboardHelper to be an easy way out.
After drag and drop the framework in your project, include the header file.
Download and open the example project, then drag the "Keyboard Helper" object from the objects section in the xib to the objects section in your project's interface builder.
Drag and drop all your views to be children of the "Keyboard Helper".
Drag and drop framework that I use in my projects. Supports automatic dismissal when you tap outside of a first responder or when you scroll.
GTKeyboardHelper
Just slide the view up and down as needed :
- (void)textFieldDidEndEditing:(UITextField *)textField {
self.currentTextField = nil;
[self animateTextField: textField up: NO];
}
- (BOOL)textFieldShouldReturn:(UITextField *)textField {
[self.currentTextField resignFirstResponder];
return YES;
}
- (void) animateTextField:(UITextField*) textField up:(BOOL)up {
const int movementDistance = 80; // tweak as needed
const float movementDuration = 0.3f; // tweak as needed
int movement = (up ? -movementDistance : movementDistance);
[UIView animateWithDuration:movementDuration animations:^{
self.view.frame = CGRectOffset(self.view.frame, 0, movement);
}];
}
Don't forget to set self as a UITextFieldDelegate and as the actual textField delegate.
(Thanks to Ammagrammer, this is just a shorter answer using blocks for animations)
I have something else if you want. The point here is that you want to set the center your UIView on the text field you are editing.
Before that, you have to save your INITIAL_CENTER, as a CGPoint, from self.view.center and your INITIAL_VIEW as a CGRect from self.view.frame in a const property.
You can create a method like this :
- (void) centerOn: (CGRect) fieldFrame {
// Set up the center by taking the original view center
CGPoint center = CGPointMake(INITIAL_CENTER.x,
INITIAL_CENTER.y - ((fieldFrame.origin.y + fieldFrame.size.height/2) - INITIAL_CENTER.y));
[UIView beginAnimations:#"centerViewOnField" context:nil];
[UIView setAnimationDuration:0.50];
if (CGRectEqualToRect(fieldFrame,INITIAL_VIEW)) {
self.view.frame = INITIAL_VIEW;
[self.view setCenter:INITIAL_CENTER];
} else {
[self.view setCenter:center];
}
[UIView commitAnimations];
}
Then, on your UITextFieldDelegate, you have to call centerOn:(CGRect) in following methods :
textFieldDidBeginEditing:(UITextField*) with, as a parameter, the frame of the text field you want to center on.
And you have to call it in your event handler, where you close your keyboard,
textFieldDidEndEditing:(UITextField*) can be one of the ways to do it, putting the INITIAL_VIEW as a parameter of centerOn:(CGRect).
I believe on newer versions of iOS (6.1+, possibly even earlier), the underlying view, at least for UITableView, auto-shrinks when the keyboard pops up. So you only need to make the text field visible in that view. In init:
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(keyboardWasShown:)
name:UIKeyboardDidShowNotification
object:nil];
then:
- (void)keyboardWasShown:(NSNotification*)notification
{
// Scroll the text field into view so it's not under the keyboard.
CGRect rect = [self.tableView convertRect:inputView.bounds fromView:inputView];
[self.tableView scrollRectToVisible:rect animated:YES];
}
https://github.com/ZulwiyozaPutra/Shift-Keyboard-Example I hope this solution helped. They are all Swift 3 written.
//
// ViewController.swift
// Shift Keyboard Example
//
// Created by Zulwiyoza Putra on 11/23/16.
// Copyright © 2016 Zulwiyoza Putra. All rights reserved.
//
import UIKit
class ViewController: UIViewController, UITextFieldDelegate {
//connecting textfield from storyboard
#IBOutlet weak var textField: UITextField!
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
subscribeToKeyboardNotifications()
}
override func viewDidAppear(_ animated: Bool) {
self.textField.delegate = self
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
unsubscribeFromKeyboardNotifications()
}
//Hide keyboard after finished editing
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
textField.resignFirstResponder()
return true
}
//Setup view before keyboard appeared
func keyboardWillAppear(_ notification:Notification) {
view.frame.origin.y = 0 - getKeyboardHeight(notification)
}
//Setup view before keyboard disappeared
func keyboardWillDisappear(_ notification: Notification) {
view.frame.origin.y = 0
}
//Getting keyboard height
func getKeyboardHeight(_ notification:Notification) -> CGFloat {
let userInfo = notification.userInfo
let keyboardSize = userInfo![UIKeyboardFrameEndUserInfoKey] as! NSValue // of CGRect
return keyboardSize.cgRectValue.height
}
//Subscribing to notifications to execute functions
func subscribeToKeyboardNotifications() {
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillAppear(_:)), name: .UIKeyboardWillShow, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillDisappear(_:)), name: .UIKeyboardWillHide, object: nil)
}
//Unsubscribing from notifications
func unsubscribeFromKeyboardNotifications() {
NotificationCenter.default.removeObserver(self, name: .UIKeyboardWillShow, object: nil)
NotificationCenter.default.removeObserver(self, name: .UIKeyboardWillHide, object: nil)
}
}