Is there any possible ways/delegates to identify the state of input accessory view of a UITextView's keyboard - ios

In my native iOS app, I have a screen that contains a simple textview. I need to adjust the size/frame of the text view when keyboard appears. I've succeeded it with UIKeyboardDidShowNotification as below:
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(myKeyBoardIsOnScreen:) name:UIKeyboardDidShowNotification object:nil];
And setting the frame on:
- (void)myKeyBoardIsOnScreen:(NSNotification*)notification {
NSDictionary* keyboardInfo = [notification userInfo];
NSValue* keyboardFrameBegin = [keyboardInfo valueForKey:UIKeyboardFrameBeginUserInfoKey];
CGRect keyboardFrameBeginRect = [keyboardFrameBegin CGRectValue];
self.textView.frame = CGRectMake(self.textView.frame.origin.x, self.textView.frame.origin.y, self.textView.frame.size.width, self.view.frame.size.height-keyboardFrameBeginRect.size.height-self.textView.frame.origin.y);
}
Problem: This looks ok for the first moment. But later I realised that the frame of the keyboard is with the height of its accessory view included. So when I hide the accessory view by dragging it down, the textview appears to be broken.
Hence can anyone suggest me any possible ways/delegates to identify the state of input accessory view of a textview's keyboard (like: Input accessory view is shown/hidden,etc.)
NB: I need the accessory view. Hence I don't need to remove it.

Register as an observer for the UIKeyboardDidChangeFrameNotification to update your view's frame.
Apple Docs

Related

iPhone keyboard sizes

Is there a way to get the keyboard size programmatically before the keyboard is presented? In Objective-C
I need to set view.height constraint to be the same as keyboard.height programmatically. And it needs to happen before the keyboard is presented, so the view don't get this ugly constrain animation after the ViewController is presented.
I assume you present the keyboard by calling becomeFirstResponder on some UI component.
If the keyboard appears after your view is presented, you should check where that call is performed. Calling it in viewDidLoad or similarly early should cause the keyboard to be shown as the view animates in.
Your layout should also handle the keyboard changes properly. The keyboard size can change even after it's presented. For example the emoji/quick type keyboards are taller than the default keyboard.
You should perform your constraint changes in a combination of UIKeyboard[Will/Did]ShowNotification, UIKeyboard[Will/Did]HideNotification and UIKeyboardWillChangeFrameNotification. In your case, UIKeyboardWillShowNotification should do the trick.
The userInfo dictionary contains a lot of information about the keyboard. You find the final frame of the keyboard in UIKeyboardFrameEndUserInfoKey. If you animate the changes in your layout, you can use values in UIKeyboardAnimationCurveUserInfoKey and UIKeyboardAnimationDurationUserInfoKey to animate with the same animation as the keyboard.
- (void)viewDidLoad {
[super viewDidLoad];
// Don't forget to remove the observer when appropriate.
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(keyboardWillShow:)
name:UIKeyboardWillShowNotification
object:nil];
[self.textField becomeFirstResponder];
}
- (void)keyboardWillShow:(NSNotification *)notification {
CGFloat keyboardHeight = [notification.userInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue].size.height;
[self.viewHeightConstraint setConstant:keyboardHeight];
// You can also animate the constraint change.
}
Such setup will also work if the keyboard is presented from the get-go.

How to detect keyboard frame changes when dismiss the keyboard interactively?

Consider this scenario, I have a textview with keyboard Dismiss interactively set in storyboard, so when user scroll down and able to dismiss keyboard interactively.
I have constraints on the textview to bottom to make sure it is always fully displayed on the view.
Current problem is, when user gradually scroll down to dismiss the keyboard, I can not detect the keyboard frame changes. I tried UIKeyboardWillHideNotification and UIKeyboardWillChangeFrameNotification, they were only called after the keyboard dismissed.
So my question is, how can we detect keyboard frame changes simultaneously when dismiss the keyboard interactively?
If you want to observe keyboard frame changes even when the keyboard is being dragged you can use this: https://github.com/brynbodayle/BABFrameObservingInputAccessoryView
Basically you create a placeholder input view for keyboard (which sticks to the keyboard all the time, even while dragging) and you observe it's frame changes. Those changes are being returned in a block, so you get current frame of the keyboard all the time.
You shouldn't change the textView height to fit all view. Instead - you should change contentInset field so your textView will stay the same height and you won't have to bother about tracking frame of the interactive keyboard.
See answer here:
How do I scroll the UIScrollView when the keyboard appears?
In your viewDidLoad method add these line:
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(keyboardWillShow:)
name:UIKeyboardWillShowNotification
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(keyboardWillHide:)
name:UIKeyboardWillHideNotification
object:nil];
the add these methods to your viewController
- (void)keyboardWillShow:(NSNotification *)notification
{
NSDictionary *userInfo = [notification userInfo];
CGSize keyboardSize = [[userInfo objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;
[UIView animateWithDuration:0.30
delay:0.0
options:(7 << 16) // This curve ignores durations
animations:^{
self.buttonsBottomConstraint.constant = keyboardSize.height - self.footerView.bounds.size.height + 4.0;
[self.view layoutIfNeeded];
}
completion:nil];
}
- (void)keyboardWillHide:(NSNotification *)notification
{
[UIView animateWithDuration:0.30
delay:0.0
options:(7 << 16) // This curve ignores durations
animations:^{
self.buttonsBottomConstraint.constant = 0.0;
[self.view layoutIfNeeded];
}
completion:nil];
}

center UIActivityIndicator when keyboard is active

I have a UITableView with a search bar and a search result display controller:
[self.searchDisplayController.searchResultsTableView addSubview:searchIndicator];
searchIndicator.center = CGPointMake(self.view.frame.size.width / 2.0, self.view.frame.size.height / 2.0);
The code above places the indicator at the center of the screen. However I want to place the indicator at the center of the frame excluding the keyboard. How can I do this?
The indicator is defined like this:
#property (retain, nonatomic) IBOutlet UIActivityIndicatorView *searchIndicator;
#SaurabhPrajapati has the right idea in his comment. You'll need to subscribe to one of the keyboard willShow/didShow notifications (UIKeyboardDidShowNotification or
UIKeyboardWillShowNotification) and when you get a keyboard notification, collect information about the keyboard height from the Keyboard Notification User Info Keys (Search in the Xcode help system on that string for more information.) Save the keyboard height to an instance variable, and then when you get ready to display your activity indicator, use the keyboard height to adjust the position of the indicator as outlined in Saurabh's comment.
add this in viewDidLoad
//KeyBoard Helpers
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyBoardWillShow:) name:UIKeyboardWillShowNotification object:nil];
Then implement this method
-(void)keyBoardWillShow:(NSNotification*)notification
{
NSDictionary *userInfo = [notification userInfo];
CGRect keyboardFrame = [[userInfo objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue];
CGFloat keyBoardHeight = keyboardFrame.size.height;
searchIndicator.center = CGPointMake(self.view.frame.size.width / 2.0, self.view.frame.size.height - keyBoardHeight/ 2.0);
}
You could just add a constraint to the top of your UIActivityIndicatorView and inside the to notifications of your keyboard. When keyboard will be shown, you shrink it by the keyboard height, if keyboard will be hide, you add the keyboard height again to the top constraint.

Objective-C: detect keyboard size/origin *before* it opens?

How is this done? I'm looking for iOS7/8 solutions. keyboardWillShow is not satisfactory because I need to resize a view based on the keyboard height before the keyboard actually shows.
keyboardWillShow is fired before the keyboard is shown. If that's not satisfactory for you, then you'll need to be smart about the keyboard size.
If the keyboard has never been shown on screen in your app before, you can make an educated guess by first checking for the device type and orientation and then having a quick lookup table of the default keyboard sizes. This will cover you 99% of the time.
In the event that the user has a custom keyboard in use that is not a standard size, you can use the keyboard size from keyboardWillShow, store it and the orientation (NSUserDefaults would work well here) and then reference the stored value the next time you need the size.
This wouldn't cover your needs every time because you wouldn't know which keyboard is going to be pulled up until keyboardWillShow is called. For example, you could replace the inputView on two different UITextField's with your own custom views; those views could be different sizes. You wouldn't know which one was going to be shown until keyboardWillShow would be called.
EDIT
There is another possibility...if you know the view that you want to show the keyboard for explicitly.
I added this to viewDidLoad:
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(keyboardWillShowFirstTimeNotification:)
name:UIKeyboardWillShowNotification
object:nil];
[self.view addSubview:self.textField];
[self.textField becomeFirstResponder];
Then, add a method for handling that notification. This method should only be called once and then inside of it remove the notification so it's never called again.
- (void)keyboardWillShowFirstTimeNotification:(NSNotification*)notification {
NSDictionary* keyboardInfo = [notification userInfo];
NSValue* keyboardFrameBegin = [keyboardInfo valueForKey:UIKeyboardFrameBeginUserInfoKey];
CGRect keyboardFrameBeginRect = [keyboardFrameBegin CGRectValue];
NSLog(#"keyboardFrameBeginRectHeight: %f", keyboardFrameBeginRect.size.height);
[[NSNotificationCenter defaultCenter] removeObserver:self];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(keyboardWillShow)
name:UIKeyboardWillShowNotification
object:nil];
[self.textField resignFirstResponder];
}
This will log the keyboard height without ever showing it on screen.
If you wanted to extend this further, you could subclass UITextField and UITextView to have properties for keyboard height for different orientations and then you could store that value directly in the text fields and text views. Then, you'd be able to have multiple input view sizes and know what they will be prior to showing them.
Currently the time for the keyboard to show is 0.3 seconds, but Apple may change that at any time. This is also true for the keyboard size. The default keyboard in portait mode is 216px in height and in landscape it is 162px, but that may also change at any time. If (for any reason) you need to find out the keyboard size you can do that pretty easily.
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(keyboardWillShow:)
name:UIKeyboardWillShowNotification
object:nil];
// Read the userInfo for the key UIKeyboardFrameBeginUserInfoKey
-(void)keyboardWillShow:(NSNotification*)notification {
NSDictionary* keyboardInfo = [notification userInfo];
NSValue* keyboardFrameBegin = [keyboardInfo valueForKey:UIKeyboardFrameBeginUserInfoKey];
CGRect keyboardFrameBeginRect = [keyboardFrameBegin CGRectValue];
NSLog(#"%#", NSStringFromCGRect(keyboardFrameBeginRect));
}

Managing vertical scrolling in a UIWebView

I try to get a content editable UIWebView with a "normal" scrolling operation, so that when the text cursor is going to be hidden by the keyboard, the UIWebView scrolls to prevent that. I read the "Managing the Keyboard" document from iOS developer library and got no result, using the scrollView property of my UIWebView. I also found numerous tricks on the web, using ScrollView properties/methods or javascript commands but cannot obtain a normal scrolling operation, like any NSTextView does on MacOS for example. Do you know any solution to this problem?
[EDITED] Got the solution, and created a method fired by a NSTimer. To get the caret Y position, one should get the selection of the active element, and insert a dummy node. Then getting the node.offsetTop property gives the caretY. Do not forget the remove the node...
Ok, here is the way Apple does it in their examples (I no longer remember what project name that was):
1.You register for keyboard notifications(the keyboard send a notification every time it shows up or it gets hidden):
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(keyboardWasShown:)
name:UIKeyboardDidShowNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(keyboardWillBeHidden:)
name:UIKeyboardWillHideNotification object:nil];
2.Get the keyboard's size (you are interested in the height) and scroll if that point is not contained in the view:
- (void)keyboardWasShown:(NSNotification*)aNotification
{
NSDictionary* info = [aNotification userInfo];
CGSize kbSize = [[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;
// If active text field is hidden by keyboard, scroll it so it's visible
CGRect aRect = self.view.frame;
aRect.size.height -= kbSize.height;
CGPoint point;
point = CGPointMake(0, activeTextField.frame.origin.y + activeTextField.frame.size.height);
if (!CGRectContainsPoint(aRect, point ))
{
CGPoint scrollPoint = CGPointMake(0.0, activeTextField.frame.origin.y+activeTextField.frame.size.height-kbSize.height);
[scrollView setContentOffset:scrollPoint animated:YES];
}
Don't forget do reverse things when hiding keyboard and remove the observers if you push to another view controller.
This example is used with text fields on a UIScrollView but I'm sure you can easily adapt it.
Personally if I also manage the web side, I prefer to create the webpages 320px wide, set my webView sizeToFit, add it on top of a UIScrollView and manage the scrolling from there. For textFields, I simply get the container for the selected textField using javascript and get it's coordinates.

Resources