I have a ScrollView in which there are TableViews. I set when the keyboard appears, the content above the keyboard should move, so the keyboard won't obscure anything. How can I scroll what is above the keyboard, only when the keyboard is up?
You can add add observer to check keyboard is showing or not.
NotificationCenter.default.addObserver(self,
selector: #selector(viewController.keyboardWillShow(notification:)),
name: NSNotification.Name.UIKeyboardWillShow, object: nil)
NotificationCenter.default.addObserver(self,
selector: #selector(viewController.keyboardWillHide(notification:)),
name: NSNotification.Name.UIKeyboardWillHide, object: nil)
func keyboardWillShow(notification: NSNotification) {
if let keyboardSize = (notification.userInfo? [UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue {
print("Show")
}
}
func keyboardWillHide(notification: NSNotification) {
if let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue {
print("Hide")
}
}
And then you need to do calculation for content to show when keyboard will appear and set content offset of scrollview.
You need to manage the height of UITableView when the keyboard is up and down. Notification will help to manage state of wheather keyboard is up or not. According to state, we can udpate UITableView's height.
-(void) keyboardWillShow:(NSNotification *)note{
// get keyboard size and loctaion
CGRect keyboardBounds;
[[note.userInfo valueForKey:UIKeyboardFrameEndUserInfoKey] getValue: &keyboardBounds];
NSNumber *duration = [note.userInfo objectForKey:UIKeyboardAnimationDurationUserInfoKey];
NSNumber *curve = [note.userInfo objectForKey:UIKeyboardAnimationCurveUserInfoKey];
// Need to translate the bounds to account for rotation.
keyboardBounds = [self.view convertRect:keyboardBounds toView:nil];
// animations settings
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationBeginsFromCurrentState:YES];
[UIView setAnimationDuration:[duration doubleValue]];
[UIView setAnimationCurve:[curve intValue]];
// set views with new info
_containerBottom.constant = keyboardBounds.size.height;
CGRect rectTable = _tblMessages.frame;
rectTable.size.height -= keyboardBounds.size.height;
_tblMessages.frame = rectTable;
[self.view layoutIfNeeded];
[self scrollToBottom];
// commit animations
[UIView commitAnimations];
}
-(void) keyboardWillHide:(NSNotification *)note{
CGRect keyboardBounds;
[[note.userInfo valueForKey:UIKeyboardFrameEndUserInfoKey] getValue: &keyboardBounds];
keyboardBounds = [self.view convertRect:keyboardBounds toView:nil];
NSNumber *duration = [note.userInfo objectForKey:UIKeyboardAnimationDurationUserInfoKey];
NSNumber *curve = [note.userInfo objectForKey:UIKeyboardAnimationCurveUserInfoKey];
// animations settings
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationBeginsFromCurrentState:YES];
[UIView setAnimationDuration:[duration doubleValue]];
[UIView setAnimationCurve:[curve intValue]];
CGRect rectTable = _tblMessages.frame;
rectTable.size.height += keyboardBounds.size.height;
_tblMessages.frame = rectTable;
// commit animations
[UIView commitAnimations];
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
// register for keyboard notifications
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(keyboardWillShow:)
name:UIKeyboardWillShowNotification
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(keyboardWillHide:)
name:UIKeyboardWillHideNotification
object:nil];
}
- (void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
// unregister for keyboard notifications while not visible.
[[NSNotificationCenter defaultCenter] removeObserver:self
name:UIKeyboardWillShowNotification
object:nil];
[[NSNotificationCenter defaultCenter] removeObserver:self
name:UIKeyboardWillHideNotification
object:nil];
}
I am enabling the autocorrection option of UITextField to show the suggestion text. Here my proble is want to identify the suggestion text view is in OPEN state or CLOSE state.
or
Please any one give me an idea about this.
We can detect this change using keyboard height change notification as follows,
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(didReceiveKeyboardWillChangeFrameNotification:)
name:UIKeyboardWillChangeFrameNotification
object:nil];
- (void)didReceiveKeyboardWillChangeFrameNotification:(NSNotification *)notification
{
NSDictionary *userInfo = [notification userInfo];
CGRect keyboardEndFrame = [userInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue];
if (CGRectIsNull(keyboardEndFrame)) {
return;
}
UIViewAnimationCurve animationCurve = [userInfo[UIKeyboardAnimationCurveUserInfoKey] integerValue];
NSInteger animationCurveOption = (animationCurve << 16);
double animationDuration = [userInfo[UIKeyboardAnimationDurationUserInfoKey] doubleValue];
[UIView animateWithDuration:animationDuration
delay:0.0
options:animationCurveOption
animations:^{
}
completion:^(BOOL finished) {
}];
}
I have a standard UINavigationViewController with a standard UIInteractivePopGestureRecognizer for iOS 7. I also have these well known and widely-used methods:
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyboardWillShow:) name:UIKeyboardWillShowNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyboardWillHide:) name:UIKeyboardWillHideNotification object:nil];
They work like this:
- (void)keyboardWillShow:(NSNotification *)notification{
CGFloat keyboardSlideDuration = [[[notification userInfo] objectForKey: UIKeyboardAnimationDurationUserInfoKey] floatValue];
UIViewAnimationOptions keyboardAnimationCurve = [[notification userInfo][UIKeyboardAnimationCurveUserInfoKey] integerValue]<<16;
if (keyboardIsUp || isAnimating) return;
isAnimating = YES;
[UIView animateWithDuration:keyboardSlideDuration delay:0 options:(keyboardAnimationCurve | UIViewAnimationOptionBeginFromCurrentState) animations:^{
_topConstraint.constant -= kTopConstraintR4Ratio;
[self.view layoutIfNeeded];
}completion:^(BOOL finished){
keyboardIsUp = YES;
isAnimating = NO;
}];
}
- (void)keyboardWillHide:(NSNotification *)notification{
CGFloat keyboardSlideDuration = [[[notification userInfo] objectForKey: UIKeyboardAnimationDurationUserInfoKey] floatValue];
UIViewAnimationOptions keyboardAnimationCurve = [[notification userInfo][UIKeyboardAnimationCurveUserInfoKey] integerValue]<<16;
if (isAnimating) return;
isAnimating = YES;
[UIView animateWithDuration:keyboardSlideDuration delay:0 options:(keyboardAnimationCurve | UIViewAnimationOptionBeginFromCurrentState) animations:^{
_topConstraint.constant = topConstraintOriginalValue;
[self.view layoutIfNeeded];
} completion:^(BOOL finished) {
keyboardIsUp = NO;
isAnimating = NO;
}];
}
The thing is that UIView animateWithDuration is triggered without any respect to duration when I swipe the view back with a gesture. The whole view turns into a mess - it animates slowly when I pop the view, but when the gesture isn't finished - the view jumps up.
It looks like this:
How do I get rid of this strange behavior? I don't want the view to move during the transition and I want it to stay still.
EDIT:
You can download the sample project using the link here:
https://www.dropbox.com/s/034lfy49ru4pelf/SampleProject.zip
Easy hack:
BOOL needAdjustContstrain;
Set needAdjustContstrain to YES in keyboardWillShow:
- (void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
if (needAdjustContstrain) {
_topContstraint.constant -=40;
}
}
you need to call the layoutIfneeded method before the animation block . Check out the code below :
- (void)keyboardWillHide:(NSNotification *)notification{
CGFloat keyboardSlideDuration = [[[notification userInfo] objectForKey: UIKeyboardAnimationDurationUserInfoKey] floatValue];
UIViewAnimationOptions keyboardAnimationCurve = [[notification userInfo][UIKeyboardAnimationCurveUserInfoKey] integerValue]<<16;
if (isAnimating) return;
isAnimating = YES;
[self.view layoutIfNeeded];
[UIView animateWithDuration:keyboardSlideDuration delay:0 options:(keyboardAnimationCurve | UIViewAnimationOptionBeginFromCurrentState) animations:^{
_topContstraint.constant = 89;
//
} completion:^(BOOL finished) {
keyboardIsUp = NO;
isAnimating = NO;
}];}
Please check the link
I have a form which has multiple textfields and textviews. When keyboad is shown I update scrollview frame to prevent textfields hiding under keyboard.
I did follow this question and implement a similar solution.
It works with uitextfields actually but not for uitextviews.
When keyboard is shown scrollview height's is reduced as much as keyboard height and then scroll to focused uitextfield. But if the focused item is uitextview it just doesnt scroll while height of uiscrollview is reduced.
IB screenshots for my scrollview;
https://www.dropbox.com/s/a1iblh6nsdqy34t/Screenshot%202014-05-30%2021.44.32.png
https://www.dropbox.com/s/o2h8avt5w82r5eo/Screenshot%202014-05-30%2021.45.19.png
Here is the code;
// register for keyboard notifications
[[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(keyboardWillHide:)
name:UIKeyboardWillHideNotification
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(keyboardDidHide:)
name:UIKeyboardDidHideNotification
object:nil];
Notification handler;
-(void)keyboardWillShow:(NSNotification *)n {
if(keyboardUp)
return;
NSDictionary* userInfo = [n userInfo];
// get the size of the keyboard
CGSize keyboardSize = [[userInfo objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;
const int movementDistance = -keyboardSize.height - keyboardPaddingToScrollView;
const float movementDuration = 0.30f;
int movement = movementDistance;
[UIView beginAnimations: #"anim" context: nil];
[UIView setAnimationBeginsFromCurrentState: YES];
[UIView setAnimationDuration: movementDuration];
CGRect contentSize = self.view.frame;
contentSize.size.height += movementDistance;
[self.view setFrame:contentSize];
[UIView commitAnimations];
}
-(void)keyboardDidShow:(NSNotification *)n {
keyboardUp = true;
NSLog(NSStringFromCGRect(self.view.frame));
}
-(void)keyboardWillHide:(NSNotification *)n {
if(!keyboardUp)
return;
NSDictionary* userInfo = [n userInfo];
// get the size of the keyboard
CGSize keyboardSize = [[userInfo objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;
const int movementDistance = keyboardSize.height + keyboardPaddingToScrollView;
const float movementDuration = 0.30f;
int movement = movementDistance;
[UIView beginAnimations: #"anim" context: nil];
[UIView setAnimationBeginsFromCurrentState: YES];
[UIView setAnimationDuration: movementDuration];
CGRect contentSize = self.view.frame;
contentSize.size.height += movementDistance;
self.view.frame = contentSize;
[UIView commitAnimations];
}
-(void)keyboardDidHide:(NSNotification *)n {
keyboardUp = false;
}
This question has been asked a couple of times, but I wasn't really able to find an answer...
In iOS6 I used the following to resize an UITextView whenever the keyboard appeared. Under iOS7 behavior is not as it should be (in my case, it seems like nothing is resizing at all). I suspect the cause to be the auto-layout / constraint behavior of iOS7. Any suggestions? ("notePad" is my UITextView)?
- (void)keyboardWasShown:(NSNotification*)aNotification
{
NSDictionary* info = [aNotification userInfo];
CGSize kbSize = [[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;
//NSLog(#"KeyboardSize: %f.%f", kbSize.width, kbSize.height);
UIEdgeInsets contentInsets = UIEdgeInsetsMake(0.0, 0.0, (kbSize.width > kbSize.height ?
kbSize.height : kbSize.width), 0);
self.notePad.contentInset = contentInsets;
self.notePad.scrollIndicatorInsets = contentInsets;
}
If you're using auto-layout at your views the following method may help you.
First define a IBOutlet for your bottom layout guide constraint and link with storyboard element.
#property (weak, nonatomic) IBOutlet NSLayoutConstraint *textViewBottomConst;
Second add observers for keyboard notifications.
- (void)observeKeyboard {
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyboardWillShow:) name:UIKeyboardWillShowNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyboardWillHide:) name:UIKeyboardWillHideNotification object:nil];
}
Finally the methods that handles keyboard changes.
- (void)keyboardWillShow:(NSNotification *)notification {
NSDictionary *info = [notification userInfo];
NSValue *kbFrame = [info objectForKey:UIKeyboardFrameEndUserInfoKey];
NSTimeInterval animationDuration = [[info objectForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue];
CGRect keyboardFrame = [kbFrame CGRectValue];
CGRect finalKeyboardFrame = [self.view convertRect:keyboardFrame fromView:self.view.window];
int kbHeight = finalKeyboardFrame.size.height;
int height = kbHeight + self.textViewBottomConst.constant;
self.textViewBottomConst.constant = height;
[UIView animateWithDuration:animationDuration animations:^{
[self.view layoutIfNeeded];
}];
}
- (void)keyboardWillHide:(NSNotification *)notification {
NSDictionary *info = [notification userInfo];
NSTimeInterval animationDuration = [[info objectForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue];
self.textViewBottomConst.constant = 10;
[UIView animateWithDuration:animationDuration animations:^{
[self.view layoutIfNeeded];
}];
}
This method supports orientation changes and different keyboard sizes. Hope it helps.
Your code is logically correct. When keyboard appear you shouldn't almost never change the frame of an object with the scrollview behaviour, but you should only change the insets.
The insets should change relative to the current version because iOS7 take care of adjust for navigation bar. If you provide a new insets probably you will broke something in UI.
Your code is broken on iOS7 for two main reason:
You must add auto layout constraint to textview container. (Probably your text view is bigger then you expect)
You shouldn't change insets in absolute way.
Here are the steps to properly configure a textview:
In xib (or storyboard) add constraint to top, left, right, bottom to the container (in my case {0, 0, 0, 0} as shown below
Register for keyboard notifications
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyboardWillShow:) name:UIKeyboardWillShowNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyboardWillHide:) name:UIKeyboardWillHideNotification object:nil];
In keyboardWillShow and keyboardWillHide don't change the frame, but change the insets relatively to the existing one.
- (void)keyboardWillShow:(NSNotification *)notification
{
// Take frame with key: UIKeyboardFrameEndUserInfoKey because we want the final frame not the begin one
NSValue *keyboardFrameValue = [notification.userInfo objectForKey:UIKeyboardFrameEndUserInfoKey];
CGRect keyboardFrame = [keyboardFrameValue CGRectValue];
UIEdgeInsets contentInsets = self.textView.contentInset;
contentInsets.bottom = CGRectGetHeight(keyboardFrame);
self.textView.contentInset = contentInsets;
self.textView.scrollIndicatorInsets = contentInsets;
}
- (void)keyboardWillHide:(NSNotification *)notification
{
UIEdgeInsets contentInsets = self.textView.contentInset;
contentInsets.bottom = .0;
self.textView.contentInset = contentInsets;
self.textView.scrollIndicatorInsets = contentInsets;
}
Then remember to remove observers
#BoranA has the correct answer, but requires tweaking for full functionality for ALL keyboards.
Follow the code below:
Attach the below to your Vertical Space - Bottom Layout Guide - TextField
#property (weak, nonatomic) IBOutlet NSLayoutConstraint *textViewBottomConst;
Second add observers for keyboard notifications.
- (void)observeKeyboard {
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyboardWillShow:) name:UIKeyboardWillShowNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyboardWillHide:) name:UIKeyboardWillHideNotification object:nil];
}
Add this to your viewDidLoad
[self observeKeyboard];
Finally the methods that handles keyboard changes.
- (void)keyboardWillShow:(NSNotification *)notification {
//THIS WILL MAKE SURE KEYBOARD DOESNT JUMP WHEN OPENING QUICKTYPE/EMOJI OR OTHER KEYBOARDS.
kbHeight = 0;
height = 0;
self.textViewBottomConst.constant = height;
self.btnViewBottomConst.constant = height;
NSDictionary *info = [notification userInfo];
NSValue *kbFrame = [info objectForKey:UIKeyboardFrameEndUserInfoKey];
NSTimeInterval animationDuration = [[info objectForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue];
CGRect keyboardFrame = [kbFrame CGRectValue];
CGRect finalKeyboardFrame = [self.view convertRect:keyboardFrame fromView:self.view.window];
int kbHeight = finalKeyboardFrame.size.height;
int height = kbHeight + self.textViewBottomConst.constant;
self.textViewBottomConst.constant = height;
[UIView animateWithDuration:animationDuration animations:^{
[self.view layoutIfNeeded];
}];
}
- (void)keyboardWillHide:(NSNotification *)notification {
NSDictionary *info = [notification userInfo];
NSTimeInterval animationDuration = [[info objectForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue];
self.textViewBottomConst.constant = 10;
[UIView animateWithDuration:animationDuration animations:^{
[self.view layoutIfNeeded];
}];
}
I found I had to call [self layoutIfNeeded] in order my insets to take effect.
My keyboard notification method looks like this (I prefer to animate the change):
-(void)keyboardWillShow:(NSNotification*)notification;
{
NSDictionary *userInfo = [notification userInfo];
NSValue *keyboardBoundsValue = [userInfo objectForKey:UIKeyboardFrameEndUserInfoKey];
CGFloat keyboardHeight = [keyboardBoundsValue CGRectValue].size.width;
CGFloat duration = [[userInfo objectForKey:UIKeyboardAnimationDurationUserInfoKey] floatValue];
NSInteger animationCurve = [[userInfo objectForKey:UIKeyboardAnimationCurveUserInfoKey] integerValue];
[UIView animateWithDuration:duration delay:0. options:animationCurve animations:^{
[[self textView] setContentInset:UIEdgeInsetsMake(0., 0., keyboardHeight, 0.)];
[[self view] layoutIfNeeded];
} completion:nil];
}
You need to resize your UITextView when your keyboard appears.
So have a look to a previous answer I made here.
You need to call the following method to resize your UITextView depending of the width of your keyboard and the text :
- (CGFloat)textViewHeightForAttributedText:(NSAttributedString*)text andWidth:(CGFloat)width
{
UITextView *calculationView = [[UITextView alloc] init];
[calculationView setAttributedText:text];
CGSize size = [calculationView sizeThatFits:CGSizeMake(width, FLT_MAX)];
return size.height;
}
Your code using my method :
- (void)keyboardWasShown:(NSNotification*)aNotification
{
NSDictionary* info = [aNotification userInfo];
CGSize kbSize = [[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;
// Get your text to NSAttributedString
NSAttributedString *as = [[NSAttributedString alloc] initWithString:self.notePad.text];
// Resize UITextView
self.notePad.frame = CGRectMake(0, 0, CGRectGetWidth(self.notePad.frame), [self textViewHeightForAttributedText:as andWidth:kbSize.width)]);
}
I’d been battling with this for a week and I found that adding the keyboard size’s height to the bottom contentInset didn’t work.
What worked was subtracting it from the top, like so:
UIEdgeInsets insets = UIEdgeInsetsMake(-(kbSize.height), 0.0, 0.0, 0.0);
[self.textView setContentInset:insets];