In my app I have a pretty large form to be filled out. The form itself is made with a table view and static cells mode. Each of the cells containing a label and a textfield. I wanted to add a toolbar above the keyboard to enable navigating between the text fields. I did that, however it is behaving very strange (at least I think).
I have no problems jumping to the next text field while jumping to the previous textfield is only possible if it is visible. If it is not visible the old textfield stays first responder, however the moment I scroll my table view and the intended text field becomes visible, it becomes the first responder (I do not click into it!). Well this is of course not the behavior that I wanted.
My question is: Is this kind of behavior normal? Could you somehow circumvent it?
If you want to see this kind of behavior yourself I have uploaded an Xcode project that illustrates the problem. You can download the zipped project here: download. The main parts of the code are explained below.
I set up a grouped table view with static cell. Each of them containing a label and a textfield. I created outlets for every textfield to gain access to them. In my viewDidLoad method I create the toolbar and its buttons, store the textfields in an array and set the controller to be the textfields delegate.
- (void)viewDidLoad
{
[super viewDidLoad];
// create the keyboard toolbar with navigation elements
self.keyboardToolbar = [[UIToolbar alloc] initWithFrame:CGRectMake(0, 0, self.view.bounds.size.width, 44)];
UIBarButtonItem *prevButton = [[UIBarButtonItem alloc] initWithTitle:#"Previous" style:UIBarButtonItemStyleBordered target:self action:#selector(prevClicked:)];
UIBarButtonItem *nextButton = [[UIBarButtonItem alloc] initWithTitle:#"Next" style:UIBarButtonItemStyleBordered target:self action:#selector(nextClicked:)];
[self.keyboardToolbar setItems:[NSArray arrayWithObjects:prevButton, nextButton, nil]];
// create the field chain
self.fields = [NSArray arrayWithObjects:self.aField, self.bField, self.cField, self.dField, self.eField, self.fField, nil];
// for scrolling and keeping track of currently active field
NSInteger max = [self.fields count];
for(NSInteger i = 0; i < max; i++) {
UITextField *curr = (UITextField *)[self.fields objectAtIndex:i];
curr.delegate = self;
}
}
The text field delegate methods store a reference of the active text field and prevent line breaks from being inserted.
- (BOOL)textFieldShouldBeginEditing:(UITextField *)textField
{
textField.inputAccessoryView = self.keyboardToolbar;
return YES;
}
- (void)textFieldDidBeginEditing:(UITextField *)textField
{
// set the current active textfield
self.currentField = textField;
// scroll this textfield to top
UITableViewCell *cell = (UITableViewCell *)textField.superview.superview;
[self.tableView scrollToRowAtIndexPath:[self.tableView indexPathForCell:cell] atScrollPosition:UITableViewScrollPositionTop animated:YES];
}
- (BOOL)textFieldShouldReturn:(UITextField *)textField
{
[textField resignFirstResponder];
self.currentField = nil;
return NO;
}
And finally there are the selectors for the toolbar buttons and the logic to get the next textfield.
- (void)moveResponderByStep:(NSInteger)step
{
// only move if a textfield is the current responder
if(![self.currentField isEditing]) {
return;
}
// where we are and where to move
NSInteger max = [self.fields count];
NSInteger moveToNumber;
for(NSInteger i = 0; i < max; i++) {
if(self.currentField == [self.fields objectAtIndex:i]) {
moveToNumber = i + step;
}
}
// stay in bounds
if(moveToNumber >= max || moveToNumber < 0) {
[self.currentField resignFirstResponder];
return;
}
// move on
UITextField *next = [self.fields objectAtIndex:moveToNumber];
[next becomeFirstResponder];
}
- (void)prevClicked:(id)sender
{
[self moveResponderByStep:-1];
}
- (void)nextClicked:(id)sender
{
[self moveResponderByStep:1];
}
Thank you!
Try checking first to see if the row is visible. Scroll the row to visible if it isn't. Then set the text field as first responder.
Related
Here is image of view
I have following problem.
I created a sample user information form for user. He fills some details and submits. See the first sub image in above screenshot.
I am entering first name and last name here, normal keyboard will appear when I switch first name to last name manually by scrolling a view or by using keyboard next button on keyboard.
Then, when click code textfield, a uipickerview/UIActionsheet will appear. Plase see previous image.
It is working fine. When I move one firstname textfield to last name text field and from lastname to code text field.
Problem : My problem is that, suppose currently I am lastname textfield i.e I am entering my last name and I clicked on code textfield following issue is coming. seen the below image.Here is my problem
So here, keyboard is appearing as well as uipickerview/UIactionsheet is also coming but in behing keyboard. I don't know it is coming.
Not only last name to code textfield form first name tp code textfield or email textfield to code textfield...same issue is coming only when I move to one textfield to next field by scrolling/switching. If I use next button from keyboard it is moving and working file.
Anybody help me to sort out this problem.
Here my code,
#interface CreateTicketViewController ()
- (void)countryCodeWasSelected:(NSNumber *)selectedIndex element:(id)element;
#end
#implementation CreateTicketViewController
- (IBAction)countryCodeClicked:(id)sender {
[self removeKeyboard];
[ActionSheetStringPicker showPickerWithTitle:NSLocalizedString(#"Select CountryCode",nil) rows:_countryArray initialSelection:0 target:self successAction:#selector(countryCodeWasSelected:element:) cancelAction:#selector(actionPickerCancelled:) origin:sender];
}
- (void)actionPickerCancelled:(id)sender {
NSLog(#"Delegate has been informed that ActionSheetPicker was cancelled");
}
- (void)countryCodeWasSelected:(NSNumber *)selectedIndex element:(id)element{
// self.selectedIndex = [selectedIndex intValue];
//may have originated from textField or barButtonItem, use an IBOutlet instead of element
self.codeTextField.text = (_codeArray)[(NSUInteger) [selectedIndex intValue]];
}
#pragma mark - UITextFieldDelegate
- (void)textFieldDidBeginEditing:(UITextField *)textField {
if (textField==_codeTextField) {
[_codeTextField resignFirstResponder];
_codeTextField.tintColor = [UIColor clearColor];
[self removeKeyboard];
[ActionSheetStringPicker showPickerWithTitle:NSLocalizedString(#"Select CountryCode",nil) rows:_countryArray initialSelection:0 target:self successAction:#selector(countryCodeWasSelected:element:) cancelAction:#selector(actionPickerCancelled:) origin:self.view];
//return NO;
}
Here is method for removeKeyboard.
UIToolbar *toolBar= [[UIToolbar alloc]initWithFrame:CGRectMake(0, 0, 320, 44)];
UIBarButtonItem *removeBtn=[[UIBarButtonItem alloc]initWithTitle:#"Done" style:UIBarButtonItemStylePlain target:self action:#selector(removeKeyBoard)];
-(void)removeKeyboard{
[_emailTextField resignFirstResponder];
// [_mobileTextField resignFirstResponder];
// [_msgTextField resignFirstResponder];
[_subjectTextField resignFirstResponder];
[_firstNameTextField resignFirstResponder];
}
again same question, check my solution..! I already posted answer for this.
https://stackoverflow.com/a/46903445/8174920
You have to do like this :
_codeTextField.inputView = YOURPICKER
I have managed to add a UIPickerView as an input view to UITextField in viewDidLoad, but now I want to make the UITextField as first responder when another textfield (with tag =4) ends editing. This is the code I'm using:
This is in the viewdidload, and all the other code needed to initialize the picker view is there:
UIPickerView *myPickerView = [[UIPickerView alloc] init];
myPickerView.showsSelectionIndicator = YES;
myPickerView.delegate = self;
myPickerView.dataSource = self;
myPickerView.backgroundColor = [UIColor colorWithRed:76/255.0 green:76/255.0 blue:76/255.0 alpha:1];
textfieldInput.inputView = myPickerView;
This is in my text field did end editing method:
-(void)textFieldDidEndEditing:(UITextField *)textField{
if(textField.tag == 4){
[textfieldInput becomeFirstResponder];
}
The problem is that when the textfieldInput becomes the first responder, the picker view isn't shown, instead the keyboard is presented.
Also, it should be mentioned that i have another textfield on top of this that gets resigned if it became the first responder and becomes hidden. This textfield is the textfield that has a tag of 4. (This textfield has been added for design reasons).
This code works for me... are you sure that call the correct reference of textfield?
try: [self.textfield becomeFirstResponder];
I'm trying to move my first responder on using tags in a tableview cell. I've set _txtFieldActive to pick up the active UITextFields tag. I can see this when I press the next button on the keyboard via NSLog. Now however I can't seem to figure out how to resignfirstresponder on that tag, and then move my first responder onto tag 102?? I get an error on the line of code trying to assign tag 102 to *tmp.
- (BOOL)textFieldShouldReturn:(UITextField *)textField {
switch (textField.tag)
{
case 101:
//Do Nothing do not want to close keyboard but move on to next UITextField
if (_txtFieldActive.tag == 101)
{NSLog(#"Tag = 101");
UITextField *tmp = [textField.tag == 102];
[tmp becomeFirstResponder];
}
break;
case 102:
[textField resignFirstResponder];
}
return YES;
}
Many thanks all for your help in advance for any pointers.
Jon.
To get a reference to a view in the current view's hierarchy with a given tag, we need to call viewWithTag:.
if (_txtFieldActive.tag == 101) {
NSLog(#"Tag = 101");
UITextField *tmp = [self.view viewWithTag:102];
[tmp becomeFirstResponder];
}
Try that on for size.
If this is a UITableViewController subclass rather than a UIViewController subclass, you might need [self.tableView viewWithTag:102];, but self.view should work in either case.
iOS unfortunately doesn't have a dropdown picker like html does with the tag. I decided that I was finally going to create one for my app, and it looks and works great. My dropdown object is a subclass of UITextField. However, I changed something and now it only works some of the time.
User interaction is enabled, but I don't want the textfield to be editable. The class in which my dropdown subclass resides is UITextField delegate, and should receive delegate methods for UITextField.
I have - (BOOL)textFieldShouldBeginEditing:(UITextField *)textField{ where I check to see if the textfield in question is a dropdown menu, and if it is, I call a method to instantiate a popover and disable editing, but the dropdown only appears on every other tap.
For example, i'll tap the "textfield" and my popover displays. I tap out so the popover goes away, then I tap on the "textfield" and nothing happens. I tap on the textfield once again and the popover appears. No idea why this is happening, here is what i'm doing:
.h
subclass : UIViewController<UITextFieldDelegate>
.m
dropdownTextField.delegate = self;
...
- (BOOL)textFieldShouldBeginEditing:(UITextField *)textField{
if(textField == self.measurementSelect){
NSLog(#"IM CALLED");
[self showPopover:textField];
return NO;
}
return YES;
}
-(void)showPopover:(id)sender{
if (_measurementPicker == nil) {
_measurementPicker = [[iPadMeasurementSelect alloc] initWithStyle:UITableViewStylePlain];
_measurementPicker.delegate = self;
}
if (_measurementPopover == nil) {
_measurementPopover = [[UIPopoverController alloc] initWithContentViewController:_measurementPicker];
[_measurementPopover presentPopoverFromRect:self.measurementSelect.frame inView:self.conversionView permittedArrowDirections:UIPopoverArrowDirectionLeft animated:YES];
}
else {
[_measurementPopover dismissPopoverAnimated:YES];
_measurementPopover = nil;
}
}
Every tap gets nslogged, so I assume my popover method is the culprit of this problem. Any ideas?
Let's rewrite by teasing apart existence of the UI elements and the visible state of the popover:
// canonical lazy getters for UI elements
- (iPadMeasurementSelect *)measurementPicker {
if (!_measurementPicker) {
_measurementPicker = [[iPadMeasurementSelect alloc] initWithStyle:UITableViewStylePlain];
_measurementPicker.delegate = self;
}
return _measurementPicker;
}
- (UIPopoverController *)measurementPopover {
if (!_measurementPopover) {
_measurementPopover = [[UIPopoverController alloc] initWithContentViewController:self.measurementPicker];
}
return _measurementPopover;
}
// now the show/hide method makes sense. it can take a bool about whether to show or hide
-(void)showPopover:(BOOL)show {
if (show) {
[self.measurementPopover presentPopoverFromRect:self.measurementSelect.frame inView:self.conversionView permittedArrowDirections:UIPopoverArrowDirectionLeft animated:YES];
} else {
[self.measurementPopover dismissPopoverAnimated:NO];
// if you want/need to create a new one each time it is shown, nil the popover here, like this:
// self.measurementPopover = nil;
}
}
When the textField begins editing, show the popover like this:
[self showPopover:YES];
And when the delegate gets the didEndEditing message:
[self showPopover:NO];
What I have :
TextView
NSArray (string)
AVAudioplayer (not yet implemented)
When I select a word in TextView :
• Check if word exist in Array
• Start Audioplayer with associated sound
Unfortunately when I tap twice to select a word inside TextView, textViewDidChangeSelection is called twice. I don’t know why I see "Youpie" twice.
I just changed inputView to hide keyboard because I only need TextView to be used in selecting mode.
- (void)textViewDidChangeSelection:(UITextView *)tve;
{
NSString *selectedText = [tve textInRange:tve.selectedTextRange];
if(selectedText.length > 0)
{
for (NSString *text in textArray)
{
if ([selectedText isEqualToString:text])
NSLog(#"Youpie");
tve.selectedTextRange = nil;
if (ps1.playing == YES)
{
[self stopEveryPlayer];
[self updateViewForPlayerState:ps1];
}
else if ([ps1 play])
{
[self updateViewForPlayerState:ps1];
fileName.text = [NSString stringWithFormat: #"%# (%d ch.)", [[ps1.url relativePath] lastPathComponent], ps1.numberOfChannels, nil];
}
else
NSLog(#"Could not play %#\n", ps1.url);
break;
}
}
}
}
- (void)awakeFromNib
{
textArray = [[NSArray alloc] initWithObjects:#"dog",#"cat",#"person",#"bird",#"mouse", nil];
textView.inputView = [[[UIView alloc] initWithFrame:CGRectZero] autorelease];
textView.delegate = self;
// ...
}
I noticed something when I was double tapping on each good word in my text.
textViewDidChangeSelection
If a word is already selected and no action choosen, I have 1 "Youpie".
If not, I have 2 "Youpie".
I found a simple solution. I removed selectedRange after getting value. textViewDidChangeSelection called once.
What I have changed
tve.selectedTextRange = nil;
I use a subclass of UITextView to disable menu.
-(BOOL)canPerformAction:(SEL)action withSender:(id)sender
{
return NO;
return [super canPerformAction:action withSender:sender];
}
I added an implementation for AVAudioPlayer (ps1) too.
My "autoplay" is working if a known word is selecting :)
I don't have an answer for why the method gets called twice or how to prevent this, but an alternative solution might be to display an additional item in the edit menu that pops up in a text view when a word is double clicked. Then, your action for initiating a sound based on the word could be triggered from the action selector defined in that additional menu item. In this design, you'd remove your textViewDidChangeSelection and thus would not get called twice. See http://developer.apple.com/library/ios/#documentation/StringsTextFonts/Conceptual/TextAndWebiPhoneOS/AddingCustomEditMenuItems/AddingCustomEditMenuItems.html for some additional info about modifying the standard menu.