I have a Tableview in inside a viewcontroller. I have added following code to get keyboard notifications
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(handleKeyboardWillShow:)
name:UIKeyboardWillShowNotification
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(handleKeyboardWillHide:)
name:UIKeyboardWillHideNotification
object:nil];
And on keyboard show i am scrolling my table to bottom.
- (void)handleKeyboardWillShow:(NSNotification *)notification
{
[self scrollToBottomAnimated:YES];
}
But i have a textview in my view controller as well. So when i click on textview the handleKeyboardWillShow method is called as well resulting unnecessary scrolling my tableview which i do not need if textview is clicked.
Can some one please help me figure out how to detect from which sender handleKeyboardWillShow is called.
Thanks
You can do it by checking who is first responder.
- (void)handleKeyboardWillShow:(NSNotification *)notification
{
if ([textFieldForScrolling isFirstResponder]) {
[self scrollToBottomAnimated:YES];
} else {
NSLog(#"Is a different text input");
}
}
Let me know if you need more explanation.
I would register for keyboardWillChange - which covers both showing and hiding. You can get the keyboard rect and adjust your content offset based on the keyboard's location. Note: You can animate the change in content offset - I just didn't do it in this answer.
In your textfield delegate methods willBeginEditing and didEndEditing, you can set the state variable called currentTextField.
-(void)keyboardWillChange:(NSNotification *)notification {
keyboardRect = [notification.userInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue];
keyboardRect = [self.view convertRect:keyboardRect fromView:nil];
CGPoint currentFieldPoint = [currentTextField convertPoint:currentTextField.frame.origin toView:self.view];
if(keyboardRect.origin.y < currentFieldPoint.y + currentTextField.frame.size.height){
//move stuff here
[[self tableView] setContentOffset:CGPointMake(0, [self tableView].contentOffset.y + offsetValue)];
}
}
Related
Here is the some piece of code I written for moving textview when user start typing in textview.
Here is the code,
-(BOOL)textViewShouldBeginEditing:(UITextView *)textView{
if(textView == _textViewMsg || textView == _subjectView ){
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyboardDidShow:) name:UIKeyboardDidShowNotification object:nil];
// return YES;
}
return YES;
}
- (BOOL)textViewShouldEndEditing:(UITextView *)textView{
if(textView == _textViewMsg || textView == _subjectView ){
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyboardDidHide:) name:UIKeyboardDidHideNotification object:nil];
[self.view endEditing:YES];
}
return YES;
}
- (void)keyboardDidShow:(NSNotification *)notification
{
[self.view setFrame:CGRectMake(0,-50,320,460)];
}
-(void)keyboardDidHide:(NSNotification *)notification
{
[self.view setFrame:CGRectMake(0,60,320,520)];
}
When I click on textview (in Subject) view in moving and I am able to type in textview. It is working fine. (Image 1)
When I click on done button, while hiding i.e at the time of keyboard hide one black view is coming (check Image 2) for few seconds, and then again normal view come.
Edit:
Solution : #michael gives me solution just now
changing UIKeyboardDidHideNotification to UIKeyboardWillHideNotification it works for me.
New Problem Occurred :
When I start typing in first 2 textviews i.e Requester, firstname & lastname...I am not able to type in it, because it is moving up.
It happened because you changing frame of your view after keyboard did disappear, when system animation is completed. Quick fix: change UIKeyboardDidHideNotification to UIKeyboardWillHideNotification
But I want to point few other items in you code, if you don't mind.
1:
You are subscribing for notifications each time user begin editing. It means that when user start typing second time, you code will be triggered twice. It is much more more appropriate to subscribe for notifications on viewDidAppear and unsubscribe on viewWillDisappear:
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear: animated];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyboardDidShow:) name:UIKeyboardDidShowNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyboardWillHide:) name:UIKeyboardDidHideNotification object:nil];
}
- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
2:
Then you changing your frame to constant value. But now apple support different keyboards with different size not to mention that keyboards size vary on different devices and in different possible modes. So it is much more wise to get the size of keyboard from notification:
- (void)keyboardDidShow:(NSNotification *)aNotification
{
CGRect keyboardFrame = [[aNotification.userInfo valueForKey: UIKeyboardFrameEndUserInfoKey] CGRectValue];
CGRect overlap = CGRectIntersection( self.view.frame, keyboardFrame);
// Setup new frmae according to overlap, for example reduce size by half of overlap, and move up by another half
CGRect newFrame = self.view.frame;
newFrame.origin.y -= overlap.size.height / 2.0
newFrame.size.height -= overlap.size.height / 2.0
self.view.frame = newFrame;
}
3:
Animations: You can fetch keyboard animation duration from notification as well:
UIKeyboardAnimationDurationUserInfoKey:
NSTimeInterval duration = [[aNotification.userInfo valueForKey: UIKeyboardAnimationDurationUserInfoKey] doubleValue];
[UIView animateWithDuration:duration animations:^{
self.view.frame = newFrame;
}];
4
As it seems your working with scroll view (UITableView and UICollectioView are scroll view), you can instead of changing frame, change content inset (add empty space to bottom scroll view)
[self.view setContentInset:UIEdgeInsetsMake(0, 0, overlap.size.height, 0)];
5
Consider using AutoLayout.
adding observers should be there in viewdidload. try moving the code of adding observers for keyboard to viewdidload.
Edit
Instead of did... notification type check with will...
so replace UIKeyboardDidShowNotification with UIKeyboardWillShow and UIKeyboardDidHideNotification with UIKeyboardWillHide.
Use of IQKeyboardManager framework will help to handle keyboard hide and unhide issues,
Most efficient way for keyboard handling: IQKeyboardManager
Method 1:
You can use a Cocoa Pod file for IQKeyboardManager Library.
init the pod file.
pod 'IQKeyboardManager'
install Pod file.
Method 2:
Download IQKeyboardManger SDK from github and drag & drop inside your project file.
Swift code snippet:
import IQKeyboardManagerSwift
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
IQKeyboardManager.sharedManager().enable = true
return true
}
}
Objective-C Snippet:
#import <IQKeyboardManager/IQKeyboardManager.h>
#implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
//ONE LINE OF CODE.
//Enabling keyboard manager(Use this line to enable managing distance between keyboard & textField/textView).
[[IQKeyboardManager sharedManager] setEnable:YES];
return true;
}
Hope this will help you to resolve your keyboard hide and unhide UI issues.
When I click on textfield, execute this method keyboardDidShow and when I started to typing, the view will be go down. What is the reason for this ? Here is my code,
else if (textField.tag==2) {
[self.viewUi setFrame:CGRectMake(0,-110,320,460)];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyboardDidShow:) name:UIKeyboardDidShowNotification object:nil];
}
return YES;
}
- (void)keyboardDidShow:(NSNotification *)notification
{
// Assign new frame to your view
[self.viewUi setFrame:CGRectMake(0,-110,320,460)];
}
Alwin, this particular post might help you in getting this done
How to make a UITextField move up when keyboard is present?
Or trying adding the NSNotification observer at the time of loading of View Controller as in viewDidLoad.
- (void)keyboardDidShow:(NSNotification *)notification
{
// Assign new frame to your view
[self.viewUi setFrame:CGRectMake(0,-110,320,460)];
}
I've got a UIToolBar with a UITextField in it, along with a Label. I'm trying to get the label to update when the user types so they know how many characters they've typed.
Currently the UIToolBar returns to its original position when I try and update the label counter. Here is a gif showing the issue I'm having.
All I'm doing is the following:
-(IBAction)CharCount:(id)sender{
NSString *substring = textField.text;
NSString *limitHit;
limitHit = substring;
int maxChar = 160;
if (limitHit.length > 0) {
TextCounter.hidden = NO;
TextCounter.text = [NSString stringWithFormat:#"%d/160", limitHit.length];
}
}
How would I go about updating the label without reversing the animation to move the toolbar along with the keyboard?
======================== Edit ========================
Not using auto-layout means my view on an iPhone 4S is wrong. Their's an example below. The menu at the bottom hangs off. How do I set it so that doesn't happen?
Don't turn off auto layout, just change constraints instead of frames. Changing frames with auto layout does not work because of layoutSubviews method. This method is called by system in many cases. You need:
Add a bottom constraint to your toolbar:
Subscribe for keyboard notifications.
Change bottom constraint of your toolbar when keyboard will show or hide.
Code sample:
- (void)dealloc {
[self unsubscribeForKeyboardNotifications];
}
- (void)viewDidLoad {
[super viewDidLoad];
[self subscribeForKeyboardNotifications];
}
#pragma mark - Keyboard notifications
- (void)subscribeForKeyboardNotifications {
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(keyboardWillAppear:)
name:UIKeyboardWillShowNotification
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(keyboardWillDisappear:)
name:UIKeyboardWillHideNotification
object:nil];
}
- (void)unsubscribeForKeyboardNotifications {
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillShowNotification object:nil];
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillHideNotification object:nil];
}
- (void)keyboardWillAppear:(NSNotification *)notification {
CGFloat keyboardHeight = [notification.userInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue].size.height;
[self changeToolbarBottomConstraintWithConstant:keyboardHeight];
}
- (void)keyboardWillDisappear:(NSNotification *)notification {
[self changeToolbarBottomConstraintWithConstant:0];
}
- (void)changeToolbarBottomConstraintWithConstant:(CGFloat)constant {
[self.toolBar.superview.constraints enumerateObjectsUsingBlock:
^(NSLayoutConstraint *constraint, NSUInteger idx, BOOL *stop) {
if (constraint.secondItem == self.toolBar && constraint.secondAttribute == NSLayoutAttributeBottom)
constraint.constant = constant;
}];
[UIView animateWithDuration:0.5
animations:^{
[self.view layoutIfNeeded];
}];
}
Result:
Every part of this looks like it could be simplified and solved by setting the UIToolbar as the UITextview's inputAccessoryView. This will attach the toolbar to the keyboard as it animates up and down. If you want it to remain at the bottom of the view in the View Controller you can overwrite the inputAccessoryView of the View Controller and then add this method to your View Controller's implementation file:
- (BOOL)canBecomeFirstResponder {
return YES;
}
Here is a handy intro to using an inputAccessoryView on a view controller.
no need to remove autolayout just add two constraint trailing space to toolbarview and fix width constraint
hope this will help you i have similar problem and i resolve with this way so.
You can do it without auto layout also by setting frames. Take textField and label in a view called InputView and add it in self.view and your textField as tfInput.
now set delegate for textfield in your view controller.
Then, Just change the Y position of view according to requirement.
- (BOOL)textFieldShouldBeginEditing:(UITextField *)textField
{
if(textField== tfInput)
{
InputView.frame = CGRectMake(InputView.frame.origin.x,self.view.frame.size.height - 216 - InputView.frame.size.height,InputView.frame.size.width,InputView.frame.size.height);
}
return YES;
}
and
- (BOOL)textFieldShouldReturn:(UITextField *)textField
{
if(textField== tfInput)
{
InputView.frame = CGRectMake(InputView.frame.origin.x,self.view.frame.size.height - 49 InputView.frame.size.height,InputView.frame.size.width,InputView.frame.size.height);
}
return YES;
}
here I set 49 as a toolbar size, it may be custom size by you.
And also you can do some animation while frame set.
this is a one option by frame set.
second option is put it in a scrollview and in same textfield delegate method textFieldShouldBeginEditing you have to just set content offset to your needed place and make it 0 in textFieldShouldReturn.
I have view to type message as shown below.
Now, when I type message, the keyboard appears and the box should move just above keyboard as shown in figure below.
my problem
They keyboard animation and view animation occur at different time. Keyboard appears first and then view appears. Even if i tried to set animation time to any, they occur at different time.
How should I solve my problem?
Please, suggest me way to solve it so that keyboard and view animates to show as if they are of same view. Both animation should occur at exact time so that they look like same view appeared at a time.
what i tried
my view did load has following code
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyboardWillShow:)
name:UIKeyboardWillShowNotification object:nil];
now keyboard show function look like
- (void)keyboardWillShow:(NSNotification *)note{
NSDictionary* keyboardInfo = [note userInfo];
CGFloat duration = [[keyboardInfo objectForKey:UIKeyboardAnimationDurationUserInfoKey] floatValue];
[UIView animateWithDuration:duration animations:
^{
//chat_typingView is name for typing view
chat_typingView.frame = CGRectMake(chat_typingView.frame.origin.x,
238,
chat_typingView.frame.size.width,
chat_typingView.frame.size.height);
}
maybe you should check if your chat_typinfView is First responder before do the animation and disable Autolayout (very important).
if ([chat_typingView isFirstResponder]) {
// Do the animation
}
PS is recommendable to subscribe to the notification on viewWillApper instead of viewDidLoad
I have a similar setup in one of my apps and I do the following:
- (void)viewDidLoad
{
[super viewDidLoad];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(keyboardShowed:)
name:UIKeyboardWillShowNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(keyboardHidden:)
name:UIKeyboardWillHideNotification object:nil];
}
- (void)viewDidLayoutSubviews {
[super viewDidLayoutSubviews];
/*** FOR AUTOLAYOUT MODIFICATIONS & ADDITIONS # RUNTIME ***/
self.defaultViewFrame = self.myView.frame
}
- (void) keyboardShowed:(NSNotification*)notification {
//GET KEYBOARD FRAME
CGRect keyboardFrame = [notification.userInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue];
//CONVERT KEYBOARD FRAME TO MATCH THE OUR COORDINATE SYSTEM (FOR UPSIDEDOWN ROTATION)
CGRect convertedFrame = [self.view convertRect:keyboardFrame fromView:self.view.window];
//..... do something with the convertedFrame (in your case convertedFrame.origin.y)
}
- (void) keyboardHidden:(NSNotification*)notification {
//RESTORE ORIGINAL STATE
[UIView transitionWithView:self.view
duration:.3f
options:UIViewAnimationOptionCurveLinear
animations:^{
self.myView.frame = self.defaultViewFrame;
}
completion:nil];
}
I'm trying to set up tableview to scroll when a text field that would be hidden behind a keyboard is selected. I've tried multiple methods that I've found around including using keyboard notifications, textFieldDid/ShouldBegin/EndEditing etc, but none of them seem to work every time.
Here's a screenshot of what I'm working with:
I have two issues:
First, I'm using a date picker in place of a keyboard for my bottom text field (off screen in the screenshot but you can get the idea). Since this isn't technically the keyboard, the methods I've used for setting the keyboard offset aren't working for this text field. I'm sure I can get the height of the date picker and adjust accordingly if that is the currently selected item, but I was wondering if there was an easier way of incorporating this into the keyboard methods.
Second, when more cells are added, the offsets become incorrect. The way this view is set up is a table view divided into sections. When the user taps the "Add further support" button, it inserts a row into the support section. It seems like the height change that happens because of this is not being registered when I try to set the table scroll offset. Is there a way I can get the height to register properly?
Here's some relevant code
- (void)textFieldDidBeginEditing:(UITextField *)textField
{
self.activeField = textField;
[self setOffsetForKeyboard];
}
- (void)textFieldDidEndEditing:(UITextField *)textField
{
if (self.activeField == self.dateTextField) {
[self datePickerValueChanged:nil];
}
self.activeField = nil;
}
- (void)registerForKeyboardNotifications
{
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(keyboardWillShow:)
name:UIKeyboardWillShowNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(keyboardWillBeHidden:)
name:UIKeyboardWillHideNotification object:nil];
}
- (void)keyboardWillShow:(NSNotification*)aNotification
{
NSDictionary* info = [aNotification userInfo];
self.keyboardSize = [[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;
[self setOffsetForKeyboard];
}
- (void)keyboardWillBeHidden:(NSNotification*)aNotification
{
[self.myTable setContentOffset:CGPointMake(0.0, -(self.navigationController.navigationBar.frame.size.height + kStatusBarHeight))animated:YES];
}
- (void)setOffsetForKeyboard{
CGPoint location =[self.activeField.superview convertPoint:self.activeField.frame.origin toView:nil];
if (location.y > self.view.frame.size.height - self.keyboardSize.height-kKeyboardOffset) {
[self.myTable setContentOffset:CGPointMake(0.0, location.y-self.keyboardSize.height-kKeyboardOffset) animated:YES];
}
}
This is your solution , you will love it->
https://github.com/michaeltyson/TPKeyboardAvoiding
So I ended up just subclassing UITableViewController which has this functionality built in instead of UIViewController which is what I was using before. Works like a charm!