Moving Content That Is Located Under the Keyboard Beginners Guide - ios

I'm building a project based on the App on the lessons I learned in Apple's "Start Developing iOS Apps Today" documentation. I created a basic View Controller Class with a Text box and two buttons. However, when I started to type the keyboard covered my content.
I have search all over the internet for a clear concise explanation to implementing Apple's sample code provided in there "Moving Content That Is Located Under the Keyboard" section in there development guide:
Listing 5-1 Handling the keyboard notifications
// 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 received several errors immediately after I import this code into my implementation block of the class:
Use of undeclared identifier 'activeField'
Unknown type name 'scrollView'; did you mean 'UIScrollView'?
Property 'scrollView' not found on object of type 'SignInViewController *'
Following some research and back to basics programming I declared the identifiers in the header file:
UIScrollView *scrollView;
// The UIScrollView class provides support for displaying content that is larger than the size of the application’s window. It enables users to scroll within that content by making swiping gestures, and to zoom in and back from portions of the content by making pinching gestures.
UITextField *activeField;
// A UITextField object is a control that displays editable text and sends an action message to a target object when the user presses the return button. You typically use this class to gather small amounts of text from the user and perform some immediate action, such as a search operation, based on that text.
Also, I declared the property "scollView" for the object of type "SignInViewController" in the interface block of the class:
#property(nonatomic, readonly, retain) UIScrollView *scrollView;
All of the compiling errors cleared.
Unfortunately, following compiling and building this code the content located underneath my keyboard did not move. What did I do incorrectly in writing this code? Could you please explain it simply with examples if possible as I'm trying to get up to speed with Object Oriented C.
I'm including the finished code below. Thank you for your help!:
Header File: SignInViewController.h
#import <UIKit/UIKit.h>
#interface SignInViewController : UIViewController
#end
UIScrollView *scrollView;
// The UIScrollView class provides support for displaying content that is larger than the size of the application’s window. It enables users to scroll within that content by making swiping gestures, and to zoom in and back from portions of the content by making pinching gestures.
UITextField *activeField;
// A UITextField object is a control that displays editable text and sends an action message to a target object when the user presses the return button. You typically use this class to gather small amounts of text from the user and perform some immediate action, such as a search operation, based on that text.
Main file: SignInViewController.m
#import "SignInViewController.h"
#interface SignInViewController ()
//Declared property scrollView
#property(nonatomic, readonly, retain) UIScrollView *scrollView;
#end
#implementation SignInViewController
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
// Code to dismiss the keyboard
- (IBAction)dismissKeyboard:(id)sender
{
[activeField resignFirstResponder];
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
/*
#pragma mark - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
// Get the new view controller using [segue destinationViewController].
// Pass the selected object to the new view controller.
}
*/
//Moving Content That Is Located Under the Keyboard
// 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];
// Get the size of the keyboard
CGSize kbSize = [[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;
// Adjust the bottom content inset of your scroll view by the keyboard height.
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.
// Scroll the target text field into view.
// Check if the active text field is under the keyboard. If it isn’t under the keyboard, the scrollview won’t update the content offset. If the active text field is under the keyboard we update the scroll view’s content offset.
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;
}
- (void)textFieldDidBeginEditing:(UITextField *)textField
{
activeField = textField;
}
- (void)textFieldDidEndEditing:(UITextField *)textField
{
activeField = nil;
}
#end

Related

Keypad hides View (AutoLayout + StoryBoard)

Apologies for this basic question, I am new to iOS development. I have a UITextField in a View with AutoLayout that I would like to use to key in messages for a chat system. However when the keypad is displayed it hides the View containing the UITextField (the entire View is behind the keypad).
What should be done to move the View along with the keypad when the keypad transitions from the bottom? The UIView should be back in its original position (at the bottom of the screen) when the keypad is dismissed. My entire UI has been designed with AutoLayout in Storyboard.
Edit:
I looked up How do I scroll the UIScrollView when the keyboard appears? for a solution however there doesn't seem to be any indication AutoLayout has been used along constraints in this answer. How can the same be achieved using AutoLayout in Storyboard. Again, apologies for any lack of understanding as I am very new to iOS development.
Add a UIScrollView to your view controller and keep your UITextField over scrollview.
add UITextFieldDelegate
yourTextField.delegate = self;
you can set content offset of scrollview when touch on UITextField and reposition it to (0, 0) when keyboard resign.
-(void)viewWillAppear:(BOOL)animated
{
yourScrollView.contentSize = CGSizeMake(320, 500);
[super viewWillAppear:YES];
}
-(void)textFieldDidBeginEditing:(FMTextField *)textField
{
[yourScrollView setContentOffset:CGPointMake(0,textField.center.y-140) animated:YES];//you can set your y cordinate as your req also
}
-(BOOL)textFieldShouldReturn:(UITextField *)textField
{
[textField resignFirstResponder];
[yourScrollView setContentOffset:CGPointMake(0,0) animated:YES];
return YES;
}
This is what I did recently, may be it helps.
All my fields are inside a wrapper view with a top constraint. Since for me moving the wrapper view a few pixels up and down was enough I use this approach.
Here is an example with a scroll view.
I use a IBOutlet to reference this constraint
#property (weak, nonatomic) IBOutlet NSLayoutConstraint *topConstraint;
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
//register for keyboard notifications
_keyboardIsShowing = NO;
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyboardWillShow:) name:UIKeyboardWillShowNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyboardWillHide:) name:UIKeyboardWillHideNotification object:nil];
}
Then on the notification methods
#pragma mark - Keyboard notifications methods
- (void)keyboardWillShow:(NSNotification *) notification{
if (!_keyboardIsShowing) {
_keyboardIsShowing = YES;
[UIView animateWithDuration:0.4 animations:^{
//here update the top constraint to some new value
_topConstraint.constant = _topConstraint.constant - 30;
[self.view layoutIfNeeded];
}];
}
}
- (void)keyboardWillHide:(NSNotification *) notification{
if (_keyboardIsShowing) {
[UIView animateWithDuration:0.4 animations:^{
_topConstraint.constant = _topConstraint.constant + 30;
[self.view layoutIfNeeded];
}];
_keyboardIsShowing = NO;
}
}
There are tons of this kind of answers here on SO.
Good luck.

Adjusting Contraints for iOS Keyboard Height inside UIWebView

I have a simple browser view controller used as part of a storyboard. It starts out looking great. My UIToolBar is anchored to the bottom of the UIView via a Vertical Space constraint of 0.
When you hit something on the webpage that brings up the keyboard. The UIToolBar is hidden. So I added a listener for the keyboard visibility change and adjusted the constraint based on the height of the keyboard. This seems to work well.
However, if the user then hits the minimize button on the keyboard, the keyboard doesn't totally go away. The top bar of arrow keys to allow for tabbing between input fields (I don't know what to call it) will remain visible. So I cannot set my constraint back to 0, I have to set it again based on the height of the visible keyboard (which I would think would include that top bar).
However when my UIKeyboardDidHideNotification fires, the keyboard height is still the same so I end up like this.
My logic for moving the constraint is based on acquiring the keyboard height this way:
// get height of visible keyboard
NSDictionary* keyboardInfo = [aNotification userInfo];
NSValue* keyboardFrameBegin = [keyboardInfo valueForKey:UIKeyboardFrameBeginUserInfoKey];
CGRect keyboardFrameBeginRect = [keyboardFrameBegin CGRectValue];
_toolbarBottomVerticalSpaceConstraint.constant = -1 * keyboardFrameBeginRect.size.height;
Is UIKeyboardFrameBeginUserInfoKey not the based value to use in the case of the keyboard being hidden?
The entire source for this view controller is actually really simple at the moment so I'll include all of it in case someone asks for it later.
#import "LEPopupBrowserViewController.h"
#interface LEPopupBrowserViewController ()
#property (weak, nonatomic) IBOutlet UIWebView *webView;
#property (weak, nonatomic) IBOutlet NSLayoutConstraint *toolbarBottomVerticalSpaceConstraint;
#end
#implementation LEPopupBrowserViewController
- (void)viewDidLoad {
[super viewDidLoad];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyboardWillShow:) name:UIKeyboardWillShowNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyboardDidHide:) name:UIKeyboardDidHideNotification object:nil];
if (_url != nil) {
NSMutableURLRequest * request =[NSMutableURLRequest requestWithURL:[NSURL URLWithString:_url]];
[_webView loadRequest:request];
}
}
- (void) viewDidUnload {
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillShowNotification object:nil];
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardDidHideNotification object:nil];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
}
// Called when the UIKeyboardDidShowNotification is sent.
- (void)keyboardWillShow:(NSNotification*)aNotification
{
// get height of visible keyboard
NSDictionary* keyboardInfo = [aNotification userInfo];
NSValue* keyboardFrameBegin = [keyboardInfo valueForKey:UIKeyboardFrameBeginUserInfoKey];
CGRect keyboardFrameBeginRect = [keyboardFrameBegin CGRectValue];
_toolbarBottomVerticalSpaceConstraint.constant = -1 * keyboardFrameBeginRect.size.height;
}
// Called when the UIKeyboardWillHideNotification is sent
- (void)keyboardDidHide:(NSNotification*)aNotification
{
// get height of visible keyboard
NSDictionary* keyboardInfo = [aNotification userInfo];
NSValue* keyboardFrameBegin = [keyboardInfo valueForKey:UIKeyboardFrameBeginUserInfoKey];
CGRect keyboardFrameBeginRect = [keyboardFrameBegin CGRectValue];
_toolbarBottomVerticalSpaceConstraint.constant = -1 * keyboardFrameBeginRect.size.height;
}
/*
#pragma mark - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
// Get the new view controller using [segue destinationViewController].
// Pass the selected object to the new view controller.
}
*/
- (IBAction)doneButtonPressed:(id)sender {
// close keyboard if present
[self.view endEditing:YES];
// dismiss ourselves
[self dismissViewControllerAnimated:YES completion:nil];
}
#end
Update
I found through some additional research it appears this additional view is called the accessoryView. I see lots of posts of people trying to remove it but haven't found anything where you could find it's height easily. The bothersome part about removing it to me is it seems Apple may reject your app.

Hide inputAccessoryView if hardware keyboard is connected

Similar to this question: iPad: Detecting External Keyboard, I am developing an iPad app that is using text fields with a custom inputAccessoryView to provide additional functionality for the virtual keyboard.
However, if a hardware keyboard (e.g. bluetooth keyboard) is connected to the device, the software keyboard is not shown as expected, but for some reason the inputAccessoryView is still visible at the bottom of the screen. Additionally, this seems to cause firing the UIKeyboardDidShowNotification(and therefore moving my view up to avoid occlusion by the keyboard which is actually not present) even if the hardware keyboard is used for input.
I found several solutions to detect if a hardware keyboard is connected, but all of them check the state after receiving a UIKeyboardDidShowNotification, at which point the inputAccessoryView is already visible (e.g. How can I detect if an external keyboard is present on an iPad?).
I am looking for a way to only show a inputAccessoryView if there is no hardware keyboard connected. Therefore I need to know if a hardware keyboard is connected before a UIKeyboardDidShowNotification is fired.
The accepted solutions provided here How can I detect if an external keyboard is present on an iPad? are no option for me as they use private APIs which may cause my app to get rejected.
This is just an enhancement of the answer by #arlomedia. What I did was watch for the willShow and didShow.
The willShow I use to move my textview into position so that it moves at the same rate as the keyboard.
The didShow I use to check the apparent size of the keyboard using the aforementioned technique and hide/show the accessoryInputView accordingly.
It's important that I also set that view to be hidden by default, and that when a willHide event is received, it is hidden again then.
- (void) addKeyboardObserver {
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyboardWillShow:) name:UIKeyboardWillShowNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyboardDidShow:) name:UIKeyboardDidShowNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyboardHidden:) name:UIKeyboardWillHideNotification object:nil];
}
- (void) removeKeyboardObserver {
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardDidShowNotification object:nil];
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillShowNotification object:nil];
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillHideNotification object:nil];
}
- (void)keyboardWillShow:(NSNotification*)notification {
CGSize keyboardSize = [[[notification userInfo] objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;
// If we're on iOS7 or earlier and landscape then the height is in the
// width.
//
if ((IS_LANDSCAPE == YES) && (IS_IOS8_OR_LATER == NO)) {
keyboardSize.height = keyboardSize.width;
}
NSNumber *rate = notification.userInfo[UIKeyboardAnimationDurationUserInfoKey];
CGRect textFieldFrame = self.textField.frame;
textFieldFrame.origin.y = ([Util screenHeight] - keyboardSize.height) - textFieldFrame.size.height - [Util scaledHeight:10.0];
// Move the text field into place.
//
[UIView animateWithDuration:rate.floatValue animations:^{
self.answerTextField.frame = textFieldFrame;
}];
keyboardShown = YES;
}
- (void)keyboardDidShow:(NSNotification*)notification {
CGRect keyboardBeginFrame = [[[notification userInfo] objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue];
CGRect keyboardEndFrame = [[[notification userInfo] objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue];
CGSize keyboardSize = keyboardBeginFrame.size;
// If we're on iOS7 or earlier and landscape then the height is in the
// width.
//
if ((IS_LANDSCAPE == YES) && (IS_IOS8_OR_LATER == NO)) {
keyboardSize.height = ABS(keyboardBeginFrame.origin.x - keyboardEndFrame.origin.x); // the keyboard will move by an amount equal to its height when it appears; ABS is needed for upside-down orientations
} else {
keyboardSize.height = ABS(keyboardBeginFrame.origin.y - keyboardEndFrame.origin.y); // the keyboard will move by an amount equal to its height when it appears; ABS is needed for upside-down orientations
}
NSNumber *rate = notification.userInfo[UIKeyboardAnimationDurationUserInfoKey];
[UIView animateWithDuration:rate.floatValue animations:^{
if (keyboardSize.height <= self.accessoryBar.frame.size.height) {
self.textField.inputAccessoryView.hidden = YES;
self.answerTextField.inputAccessoryView.userInteractionEnabled = NO;
} else {
self.textField.inputAccessoryView.hidden = NO;
self.answerTextField.inputAccessoryView.userInteractionEnabled = YES;
}
}];
keyboardShown = YES;
}
- (void)keyboardHidden:(NSNotification*)notification {
NSNumber *rate = notification.userInfo[UIKeyboardAnimationDurationUserInfoKey];
// Remove/hide the accessory view so that next time the text field gets focus, if a hardware
// keyboard is used, the accessory bar is not shown.
//
[UIView animateWithDuration:rate.floatValue animations:^{
self.textField.inputAccessoryView.hidden = YES;
self.answerTextField.inputAccessoryView.userInteractionEnabled = NO;
}];
keyboardShown = NO;
}
NOTE Edited to add change to userInteractionEnabled so that a hidden accessoryView doesn't eat taps.
IIRC, views don't resize themselves when the software keyboard appears. I am resizing my views in a keyboardDidShow method triggered by a UIKeyboardDidShow notification. So it should be enough to detect the hardware vs. software keyboard in that method and then you could skip the table resizing and hide the input accessory view (or adjust the table resizing to accommodate the input accessory view, if you prefer to leave that visible).
To resize the views correctly whether or not a hardware keyboard is present, I adapted the code from this answer:
- (void)keyboardDidShow:(NSNotification *)aNotification {
CGRect keyboardBeginFrame = [[[aNotification userInfo] objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue];
CGRect keyboardEndFrame = [[[aNotification userInfo] objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue];
float keyboardHeight = ABS(keyboardBeginFrame.origin.y - keyboardEndFrame.origin.y); // the keyboard will move by an amount equal to its height when it appears; ABS is needed for upside-down orientations
// now you can resize your views based on keyboardHeight
// that will be the height of the inputAccessoryView if a hardware keyboard is present
}
That's all you need if you want to leave the inputAccessoryView visible. To also hide that, I think you will need to set an instance variable so you can access it in keyboardDidShow:
UIView *currentInputAccessoryView;
- (void)textFieldDidBeginEditing:(UITextField *)textField {
self.currentInputAccessoryView = textField.inputAccessoryView;
}
- (void)textViewDidBeginEditing:(UITextView *)textView {
self.currentInputAccessoryView = textView.inputAccessoryView;
}
- (void)keyboardDidShow:(NSNotification *)aNotification {
CGRect keyboardBeginFrame = [[[aNotification userInfo] objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue];
CGRect keyboardEndFrame = [[[aNotification userInfo] objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue];
float keyboardHeight = ABS(keyboardBeginFrame.origin.y - keyboardEndFrame.origin.y); // the keyboard will move by an amount equal to its height when it appears; ABS is needed for upside-down orientations
if (keyboardHeight == 44) {
self.currentInputAccessoryView.hidden = YES;
keyboardHeight = 0;
}
// now you can resize your views based on keyboardHeight
// that will be 0 if a hardware keyboard is present
}
My final way to solve this issue was to simply add an observer for the UIKeyboardWillShowNotification ...
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(keyboardWillShow:)
name:UIKeyboardWillShowNotification object:nil];
.. and hide the inputAccessoryView previously stored in an instance variable.
// Called when the UIKeyboardWillShowNotification is sent.
- (void)keyboardWillShow:(NSNotification*)notification
{
NSLog(#"keyboardWillShow");
// get the frame end user info key
CGRect kbEndFrame = [[[notification userInfo] objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue];
// calculate the visible portion of the keyboard on the screen
CGFloat height = [[UIScreen mainScreen] bounds].size.height - kbEndFrame.origin.y;
// check if there is a input accessorry view (and no keyboard visible, e.g. hardware keyboard)
if (self.activeTextField && height <= self.activeTextField.inputAccessoryView.frame.size.height) {
NSLog(#"hardware keyboard");
self.activeTextField.inputAccessoryView.hidden = YES;
} else {
NSLog(#"software keyboard");
self.activeTextField.inputAccessoryView.hidden = NO;
}
}
It turned out the problem was caused by me dynamically creating the inputAccessoryView of my custom UITextField subclass in its getter method. I inadvertently recreated the view with every call of the getter instead of reusing an instance variable with lazy instantiation. This resulted in all my assignments to the view being ignored since apparently the getter method will be called multiple times when the text field is being accessed and the keyboard shown and therefore the view kept being overridden after my assignments. Reusing the view by saving it to an instance variable fixed this issue.
This is an old thread, but as of iOS 14 we now have proper APIs for tracking hardware keyboards via GameController framework using GCKeyboard and GCKeyboardDidConnect/GCKeyboardDidDisconnect notifications.
In this particular case of hiding and showing inputAccessoryView, you could do something like this:
import GameController
class ViewController: UIViewController {
...
var isHardwareKeyboardConnected: Bool {
didSet {
enableInputAccessoryIfNeeded()
}
}
func enableInputAccessoryIfNeeded() {
textField.inputAccessoryView = isHardwareKeyboardConnected ? nil : textFielInputAccessoryView
textField.reloadInputViews()
}
init() {
isHardwareKeyboardConnected = GCKeyboard.coalesced != nil
super.init(nibName: nil, bundle: nil)
startObservingHardwareKeyboard()
}
override func viewDidLoad() {
super.viewDidLoad()
enableInputAccessoryIfNeeded()
}
func startObservingHardwareKeyboard() {
NotificationCenter.default.addObserver(self, selector: #selector(hardwareKeyboardDidConnect), name: .GCKeyboardDidConnect, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(hardwareKeyboardDidDisconnect), name: .GCKeyboardDidDisconnect, object: nil)
}
#objc func hardwareKeyboardDidConnect(_ notification: Notification) {
print("[Keyboard] Hardware keyboard did connect")
isHardwareKeyboardConnected = true
}
#objc func hardwareKeyboardDidDisconnect(_ notification: Notification) {
print("[Keyboard] Hardware keyboard did disconnect")
isHardwareKeyboardConnected = false
}
}

Keyboard does not disappear after viewDidDisappear on iOS 7

In our app, there's a situation where the user enters something in a textbox and then presses the back button to go back to the master screen.
If we run that on iOS 7, the keyboard does not disappear, it just stays there. The user can still navigate through the app, but all text fields are disabled, meaning you can't enter text anywhere. The only option the user has is killing the app and starting fresh.
We tried to add resignFirstResponder messages, but that didn't help anything.
There's much code involved, and we're actively working on the issue. Meantime, has anyone experienced that problem too, and maybe found a way to make it go away?
I had the same issue like you when I compiled the app for iOS 7 and I did the following changes:
Make sure you add [textfield resignFirstResponder] before dismissing the viewController for example:
[_passwordInput resignFirstResponder];
[_emailInput resignFirstResponder];
[self performSegueWithIdentifier:#"forgotPassword" sender:self];
Just to be sure the keyboard disappears add [textfield resignFirstResponder] in viewWillDisappear for example :
- (void) viewWillDisappear:(BOOL)animated
{
[_passwordInput resignFirstResponder];
[_emailInput resignFirstResponder];
}
If your viewController is presented using UIModalPresentationFormSheet add this to your viewController just to make sure the textfields will respond resignFirstResponder:
- (BOOL)disablesAutomaticKeyboardDismissal
{
return NO;
}
In your case, override the back button action or just use viewWillDisappear to check when the user pressed the back button and then call resignFirstResponder before [super viewWillDisappear] something like this:
-(void) viewWillDisappear:(BOOL)animated
{
[_passwordInput resignFirstResponder];
[_emailInput resignFirstResponder];
[super viewWillDisappear:animated];
}
Try [self.view resignFirstResponder], instead of [textfield resignFirstResponder] on viewWillDisappear.
[textfield resignFirstResponder] should do the job, but to make sure and for not to loop through all your textFields you can use:
[self.view endEditing:YES];
From the doc:
use to make the view or any subview that is the first responder resign
(optionally force).
in general I find this useful
[[UIApplication sharedApplication] sendAction:#selector(resignFirstResponder) to:nil from:nil forEvent:nil];
you can add this in viewWillDisappear: or viewDidDisappear:
this will hide the keyboard without a reference to the currently focused text field
I had the same problem only with MoreViewController in UITabBarController (iOS 8.3). Maybe this solution not very 'nice' and little complicated, but seems like it works, hope it will help you too.
#interface ViewController ()
#property (nonatomic) BOOL needToHideKeyboard;
#property (nonatomic, strong) IBOutlet UITextField *txtField;
#property (nonatomic, strong) IBOutlet UIScrollView *scrollView;
#end
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyboardWasShown:) name:UIKeyboardDidShowNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyboardWillBeHidden:) name:UIKeyboardWillHideNotification object:nil];
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
self.needToHideKeyboard = NO;
}
- (void)viewWillLayoutSubviews
{
[super viewWillLayoutSubviews];
[self hideKeayboard];
}
- (void)hideKeayboard
{
if (self.needToHideKeyboard) {
[self.txtField resignFirstResponder];
}
}
- (void)keyboardWasShown:(NSNotification *)notification
{
self.needToHideKeyboard = YES;
NSDictionary *info = [notification userInfo];
CGSize keyboardSize = [[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;
// Shift scroll view content insets on the keyboard height
UIEdgeInsets contentInsets = self.scrollView.contentInset;
contentInsets.bottom = keyboardSize.height;
self.scrollView.contentInset = contentInsets;
}
- (void)keyboardWillBeHidden:(NSNotification *)notification
{
self.needToHideKeyboard = NO;
// Reset keyboard content insets
UIEdgeInsets contentInsets = self.scrollView.contentInset;
contentInsets.bottom = [self.bottomLayoutGuide length];
self.scrollView.contentInset = contentInsets;
}
#end
If your view controller implements textFieldDidEndEditing, make sure you don't set another view to be the first responder if the view is disappearing. textFieldDidEndEditing will get invoked when you call resignFirstResponder, or [self.view endEditing:YES].
[self.view endEditing:YES];
stopped working on my device iOS9.x
We can also do this in the viewWillDisappear method
for (UIView *subview in self.view.subviews) {
if ([subview canPerformAction:#selector(endEditing:) withSender:nil]) {
[subview endEditing:YES];
}
if ([subview canResignFirstResponder]) {
[subview resignFirstResponder];
}
}
This will loop through the responders and resign the responder status.

is this a valid workaround to keyboard obscuring content

I have a Tableview where the user can enter values into a textField as one of the custom cells
Apple have some documentation about how to adjust view content by repositioning the view clear of the keyboard's vertical dimension ( Here ) but it relies upon one placing that view into a UIScrollView. I cant do this with a tableview.
I could redesign the app so that the entry gets done in a separate detail view using the usual navigation controller, but i'd rather the user not have to perform an extra touch ( and be ferried off into yet another screen ) if possible. I like the idea of doing the deed "right where we are"
so my workaround to have a few extra tableview cells at the bottom containing a %20 or so, normal usage shouldn't register the oddity, as they are only focussed on what is visible.
I'd have to store the spaces in my datasource array and then sort descending, but that's OK
the question is, is this good practice? and even more possibly, could it be against Apple's HIG sufficient for refusal?
UITableView is a subclass of UIScrollView, so should be able to adjust the content and scroll view insets just like in the example you linked.
The way I've solved this issue is to subclass UITableView. Here's what I've done:
// AOTableView.h file
typedef enum
{
AOKeyboardStateUnknown = 0,
AOKeyboardStateShowing,
AOKeyboardStateHidden
} AOKeyboardState;
#import <UIKit/UIKit.h>
#import "AOKeyboardState.h"
#interface AOTableView : UITableView
#property (nonatomic) BOOL observeKeyboardNotifications;
#property (nonatomic) AOKeyboardState keyboardState;
#end
// AOTableView.m file
#import "AOTableView.h"
#interface AOTableView(Private)
#property (nonatomic) CGRect frame0;
- (void)setup;
- (void)keyboardWillShow:(NSNotification *)notification;
- (void)keyboardWillHide:(NSNotification *)notification;
#end
#implementation AOTableView
#pragma mark - Object lifecycle
- (void)awakeFromNib
{
[self setup];
}
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self)
{
[self setup];
}
return self;
}
- (void)setup
{
self.contentSize = self.frame.size;
self.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
_keyboardState = AOKeyboardStateUnknown;
_frame0 = self.frame;
_observeKeyboardNotifications = NO;
}
- (void)dealloc
{
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
#pragma mark - Custom setters
- (void)setObserveKeyboardNotifications:(BOOL)observeKeyboardNotifications
{
if (_observeKeyboardNotifications == observeKeyboardNotifications)
return;
_observeKeyboardNotifications = observeKeyboardNotifications;
if (_observeKeyboardNotifications)
{
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyboardWillShow:) name:UIKeyboardWillShowNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyboardWillHide:) name:UIKeyboardWillHideNotification object:nil];
}
else
{
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillShowNotification object:nil];
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillHideNotification object:nil];
}
}
#pragma mark - UIKeyboard Notifications
- (void)keyboardWillShow:(NSNotification *)notification
{
if (self.keyboardState == AOKeyboardStateShowing)
return;
self.frame0 = self.frame;
self.keyboardState = AOKeyboardStateShowing;
NSDictionary* info = [notification userInfo];
CGRect keyboardFrame = CGRectZero;
[[info objectForKey:UIKeyboardFrameEndUserInfoKey] getValue:&keyboardFrame];
CGRect frame = self.frame0;
frame.size.height = CGRectGetMinY(keyboardFrame) - CGRectGetMinY(frame);
self.frame = frame;
[self scrollToRowAtIndexPath:self.indexPathForSelectedRow atScrollPosition:UITableViewScrollPositionMiddle animated:YES];
[self deselectRowAtIndexPath:self.indexPathForSelectedRow animated:NO];
}
- (void)keyboardWillHide:(NSNotification *)notification
{
if (self.keyboardState == AOKeyboardStateHidden)
return;
self.keyboardState = AOKeyboardStateHidden;
self.frame = self.frame0;
}
#end
After creation (or loading the view from an IBOutlet), you call this method to tell the class to start listening for keyboard notifications:
[tableViewInstance setObserveKeyboardNotifications:YES];
Whenever a user clicks on a cell, it becomes the self.indexPathForSelectedRow cell... so its scrolled to by the AOTableView instance automatically.
For this to work, though, I've had to turn off userInteraction on the UITextField within the cell (otherwise, the device can get confused about if the user is clicking on the cell or on the text field). Instead, when a user selects a cell that has a text field, I tell the text field to the become first responder, like this:
[cell.textField becomeFirstResponder];
I hope this helps.
You don't need the extra cells or anything fancy.
Since your text fields are inside the table view cells, you can use the following:
- (BOOL)textFieldShouldBeginEditing:(UITextField *)textField
{
UITableViewCell *cell = (UITableViewCell *)textField.superview.superview;
NSIndexPath *indexPath = [self.tableView indexPathForCell:cell];
[self.tableView scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionMiddle animated:YES];
return YES;
}
This means that the keyboard will scroll appropriately each time a text field becomes first responder. This takes advantage of the table view being a scroll view subclass.
Note that this assumes:
Your (table) view controller is the text fields' delegate.
Your text field is a subview of the cell's content view, not the cell itself.
If the text field is a subview of the cell, the first line of the method above should reference only one superview (i.e., textField.superview).

Resources