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.
Related
I am developing a chatting application. The application works fine but i have some problem in the UI design.
I have UiTextField in side the UIview, when ever the user want to type a message the keyboard opens, and UIView moves up.
Then when the user press the return button, i want to bring it back to the original position.
I tried but It looks like this I want it to support for all devices.
Here is my code
-(BOOL)textFieldShouldBeginEditing:(UITextField *)textField {
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyboardDidShow:) name:UIKeyboardDidShowNotification object:nil];
return YES;
}
- (BOOL)textFieldShouldEndEditing:(UITextField *)textField {
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyboardDidHide:) name:UIKeyboardDidHideNotification object:nil];
[self.view endEditing:YES];
return YES;
}
- (void)keyboardDidShow:(NSNotification *)notification
{
// Assign new frame to your view
[self.view setFrame:CGRectMake(0,-145,320,460)]; //here taken -110 for example i.e. your view will be scrolled to -110. change its value according to your requirement.
}
-(void)keyboardDidHide:(NSNotification *)notification
{
[self.view setFrame:CGRectMake(0,0,320,460)];
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
[self.tv_Message resignFirstResponder];
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
NSLog(#"selected index : %ld", (long)indexPath.row);
}
- (BOOL)textFieldShouldReturn:(UITextField *)aTextField {
[aTextField resignFirstResponder];
[self.view setFrame:CGRectMake(0,0,320,460)];
return YES;
}
Can someone help me to fix this.
You can use this link to manage your view according to device. Also you need to set constraints accordingly.
https://stackoverflow.com/a/11282535/6438500
It seems that you haven't set the trailing constraint of your main view correctly. Then in keyboardDidShow(:) method up your main view dynamically because your view will have height according to device, so make it work on all devices you should move the main view up by calculating the position of textfield and keyboard height and then set the frame of your view accordingly.
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.
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)];
}
}
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!
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).