Как перемещаться по текстовым полям (кнопки «Далее» / «Готово») - PullRequest
475 голосов
/ 28 августа 2009

Как я могу перемещаться по всем моим текстовым полям с помощью кнопки «Далее» на клавиатуре iPhone?

Последнее текстовое поле должно закрывать клавиатуру.

Я настроил IB кнопки (Далее / Готово), но теперь я застрял.

Я реализовал действие textFieldShouldReturn, но теперь кнопки Next и Done закрывают клавиатуру.

Ответы [ 34 ]

3 голосов
/ 01 июля 2016

Это простое решение в быстром, без использования тегов, без уловок раскадровки ...

Просто используйте это расширение:

extension UITextField{

    func nextTextFieldField() -> UITextField?{
        //field to return
        var returnField : UITextField?
        if self.superview != nil{
            //for each view in superview
            for (_, view) in self.superview!.subviews.enumerate(){
                //if subview is a text's field
                if view.isKindOfClass(UITextField){
                    //cast curent view as text field
                    let currentTextField = view as! UITextField
                    //if text field is after the current one
                    if currentTextField.frame.origin.y > self.frame.origin.y{
                        //if there is no text field to return already
                        if returnField == nil {
                            //set as default return
                            returnField = currentTextField
                        }
                            //else if this this less far than the other
                        else if currentTextField.frame.origin.y < returnField!.frame.origin.y{
                            //this is the field to return
                            returnField = currentTextField
                        }
                    }
                }
            }
        }
        //end of the mdethod
        return returnField
    }

}

И назовите это так (например) с вашим делегатом текстового поля:

func textFieldShouldReturn(textField: UITextField) -> Bool {
    textField.resignFirstResponder()
    textField.nextTextFieldField()?.becomeFirstResponder()
    return true
}
3 голосов
/ 26 августа 2013

Я предпочитаю:

@interface MyViewController : UIViewController
@property (nonatomic, retain) IBOutletCollection(UIView) NSArray *inputFields;
@end

В файле NIB я подключаю textFields в нужном порядке к этому массиву inputFields. После этого я делаю простой тест для индекса UITextField, который сообщает, что пользователь нажал return:

// for UITextField
-(BOOL)textFieldShouldReturn:(UITextField*)textField {
    NSUInteger index = [_inputFields indexOfObject:textField];
    index++;
    if (index < _inputFields.count) {
        UIView *v = [_inputFields objectAtIndex:index];
        [v becomeFirstResponder];
    }
    return NO;
}

// for UITextView
-(BOOL)textView:(UITextView*)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString*)text {
    if ([@"\n" isEqualToString:text]) {
        NSUInteger index = [_inputFields indexOfObject:textView];
        index++;
        if (index < _inputFields.count) {
            UIView *v = [_inputFields objectAtIndex:index];
            [v becomeFirstResponder];
        } else {
            [self.view endEditing:YES];
        }
        return NO;
    }
    return YES;
}
3 голосов
/ 16 февраля 2016

Это сработало для меня в Xamarin.iOS / Monotouch. Измените кнопку клавиатуры на Далее, передайте элемент управления следующему UITextField и скройте клавиатуру после последнего UITextField.

private void SetShouldReturnDelegates(IEnumerable<UIView> subViewsToScout )
{
  foreach (var item in subViewsToScout.Where(item => item.GetType() == typeof (UITextField)))
  {
    (item as UITextField).ReturnKeyType = UIReturnKeyType.Next;
    (item as UITextField).ShouldReturn += (textField) =>
    {
        nint nextTag = textField.Tag + 1;
        var nextResponder = textField.Superview.ViewWithTag(nextTag);
        if (null != nextResponder)
            nextResponder.BecomeFirstResponder();
        else
            textField.Superview.EndEditing(true); 
            //You could also use textField.ResignFirstResponder(); 

        return false; // We do not want UITextField to insert line-breaks.
    };
  }
}

Внутри ViewDidLoad у вас будет:

Если у ваших TextFields нет тега, установите его сейчас:

txtField1.Tag = 0;
txtField2.Tag = 1;
txtField3.Tag = 2;
//...

и просто звонок

SetShouldReturnDelegates(yourViewWithTxtFields.Subviews.ToList());
//If you are not sure of which view contains your fields you can also call it in a safer way:
SetShouldReturnDelegates(txtField1.Superview.Subviews.ToList());
//You can also reuse the same method with different containerViews in case your UITextField are under different views.
3 голосов
/ 28 июля 2017

Если кто-то так хочет. Я думаю, что это наиболее близко к требованиям, о которых идет речь

enter image description here

Вот как я это реализовал

Добавить вспомогательное представление для каждого текстового поля, для которого вы хотите выполнить настройку, используя

func setAccessoryViewFor(textField : UITextField)    {
    let toolBar = UIToolbar()
    toolBar.barStyle = .default
    toolBar.isTranslucent = true
    toolBar.sizeToFit()

    // Adds the buttons

    // Add previousButton
    let prevButton = UIBarButtonItem(title: "<", style: .plain, target: self, action: #selector(previousPressed(sender:)))
    prevButton.tag = textField.tag
    if getPreviousResponderFor(tag: textField.tag) == nil {
        prevButton.isEnabled = false
    }

    // Add nextButton
    let nextButton = UIBarButtonItem(title: ">", style: .plain, target: self, action: #selector(nextPressed(sender:)))
    nextButton.tag = textField.tag
    if getNextResponderFor(tag: textField.tag) == nil {
        nextButton.title = "Done"
    }

    let spaceButton = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil)
    toolBar.setItems([prevButton,spaceButton,nextButton], animated: false)
    toolBar.isUserInteractionEnabled = true
    textField.inputAccessoryView = toolBar
}

Используйте следующие функции для обработки отводов

func nextPressed(sender : UIBarButtonItem) {
    if let nextResponder = getNextResponderFor(tag: sender.tag) {
        nextResponder.becomeFirstResponder()
    } else {
        self.view.endEditing(true)
    }

}

func previousPressed(sender : UIBarButtonItem) {
    if let previousResponder = getPreviousResponderFor(tag : sender.tag)  {
        previousResponder.becomeFirstResponder()
    }
}

func getNextResponderFor(tag : Int) -> UITextField? {
    return self.view.viewWithTag(tag + 1) as? UITextField
}

func getPreviousResponderFor(tag : Int) -> UITextField? {
    return self.view.viewWithTag(tag - 1) as? UITextField
}

Вам нужно будет указать теги textFields в последовательности, в которой вы хотите, чтобы реагировала следующая кнопка / предыдущая.

3 голосов
/ 01 августа 2014
if (cell == nil)
{
    cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier];
    txt_Input = [[ UITextField alloc] initWithFrame:CGRectMake(0, 10, 150, 30)];
    txt_Input.tag = indexPath.row+1;
    [self.array_Textfields addObject:txt_Input]; // Initialize mutable array in ViewDidLoad
}

-(BOOL)textFieldShouldReturn:(UITextField *)textField
{

    int tag = ( int) textField.tag ;
    UITextField * txt = [  self.array_Textfields objectAtIndex:tag ] ;
    [ txt becomeFirstResponder] ;
    return YES ;
}
2 голосов
/ 20 февраля 2012

Без использования тегов и без добавления свойства для nextField / nextTextField, вы можете попробовать это для эмуляции TAB, где «testInput» - ваше текущее активное поле:

if ([textInput isFirstResponder])
    [textInput.superview.subviews enumerateObjectsAtIndexes:
     [NSIndexSet indexSetWithIndexesInRange:
      NSMakeRange([textInput.superview.subviews indexOfObject:textInput]+1,
                  [textInput.superview.subviews count]-[textInput.superview.subviews indexOfObject:textInput]-1)]
                                                    options:0 usingBlock:^(UIView *obj, NSUInteger idx, BOOL *stop) {
                                                        *stop = !obj.hidden && [obj becomeFirstResponder];
                                                    }];
if ([textInput isFirstResponder])
    [textInput.superview.subviews enumerateObjectsAtIndexes:
     [NSIndexSet indexSetWithIndexesInRange:
      NSMakeRange(0,
                  [textInput.superview.subviews indexOfObject:textInput])]
                                                    options:0 usingBlock:^(UIView *obj, NSUInteger idx, BOOL *stop) {
                                                        *stop = !obj.hidden && [obj becomeFirstResponder];
                                                    }];
2 голосов
/ 23 февраля 2017

Вот версия Swift 3 ответа Anth0 . Я публикую его здесь, чтобы помочь любым быстрым разработчикам в желании воспользоваться его великолепным ответом! Я позволил себе добавить ключ возврата типа «Далее», когда вы устанавливаете связанный объект.

extension UITextField {

  @nonobjc static var NextHashKey: UniChar = 0

  var nextTextField: UITextField? {
    get {
      return objc_getAssociatedObject(self, 
        &UITextField.NextHashKey) as? UITextField
    }
    set(next) {
     self.returnKeyType = UIReturnKeyType.next
     objc_setAssociatedObject(self,
      &UITextField.NextHashKey,next,.OBJC_ASSOCIATION_RETAIN_NONATOMIC)
    }
  }
}

Вот еще одно расширение, которое показывает возможность использования вышеуказанного кода для циклического перемещения по списку UITextFields.

extension UIViewController: UITextFieldDelegate {
 public func textFieldShouldReturn(_ textField: UITextField) -> Bool {
   guard let next = textField.nextTextField else {
     textField.resignFirstResponder()
     return true
   }

    next.becomeFirstResponder()
    return false
  }
}

А затем в вашем ViewController или где-либо еще вы можете настроить свои текстовые поля следующим образом ...

@IBOutlet fileprivate weak var textfield1: UITextField!
@IBOutlet fileprivate weak var textfield2: UITextField!
@IBOutlet fileprivate weak var textfield3: UITextField!

...

[textfield1, textfield2, textfield3].forEach{ $0?.delegate = self }

textfield1.nextTextField = textfield2
textfield2.nextTextField = textfield3
// We don't assign a nextTextField to textfield3 because we want 
// textfield3 to be the last one and resignFirstResponder when 
// the return button on the soft keyboard is tapped.
2 голосов
/ 22 июня 2015

вы можете использовать библиотеку IQKeyboardManager для этого. он обрабатывает все, вам не нужно никаких дополнительных настроек. IQKeyboardManager доступен через CocoaPods, для его установки просто добавьте следующую строку в ваш Podfile:

pod 'IQKeyboardManager'

или Просто перетащите каталог IQKeyBoardManager из демонстрационного проекта в ваш проект. Вот и все. Вы можете найти каталог IQKeyBoardManager из https://github.com/hackiftekhar/IQKeyboardManager

2 голосов
/ 18 марта 2015

Я использую ответ Майкла Дж. Эммонса уже около года, отлично работает. Недавно я заметил, что вызов resignFirstResponder и затем сталFirstResponder немедленно может привести к «сбоям» клавиатуры, исчезновению и немедленному появлению. Я немного изменил его версию, чтобы пропустить resignFirstResponder, если доступно следующее поле.

- (BOOL)textFieldShouldReturn:(UITextField *)textField
{ 

    if ([textField isKindOfClass:[NRTextField class]])
    {
        NRTextField *nText = (NRTextField*)textField;
        if ([nText nextField] != nil){
            dispatch_async(dispatch_get_main_queue(),
                           ^ { [[nText nextField] becomeFirstResponder]; });

        }
        else{
            [textField resignFirstResponder];
        }
    }
    else{
        [textField resignFirstResponder];
    }

    return true;

}
2 голосов
/ 27 января 2015

Это старая запись, но она имеет высокий рейтинг страницы, поэтому я поделюсь своим решением.

У меня была похожая проблема, и в итоге я создал подкласс UIToolbar для управления следующей / предыдущей / готовой функциональностью в динамическом табличном представлении с разделами: https://github.com/jday001/DataEntryToolbar

Вы устанавливаете панель инструментов как inputAccessoryView ваших текстовых полей и добавляете их в свой словарь. Это позволяет вам переключаться между ними вперед и назад, даже с динамическим контентом. Существуют методы делегирования, если вы хотите запускать свои собственные функции при навигации по textField, но вам не нужно управлять какими-либо тегами или статусом первого респондента.

По ссылке на GitHub есть фрагменты кода и пример приложения, чтобы помочь с деталями реализации. Вам понадобится ваша собственная модель данных, чтобы отслеживать значения внутри полей.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...