Как перемещаться по текстовым полям (кнопки Next / Done)

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

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

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

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

30 ответов


в Cocoa для Mac OS X у вас есть следующая цепочка ответчика, где вы можете спросить текстовое поле, какой элемент управления должен иметь фокус далее. Это то, что делает переход между текстовыми полями работает. Но поскольку iOS-устройства не имеют клавиатуры, только touch, эта концепция не пережила перехода на Cocoa Touch.

Это можно легко сделать в любом случае, с двумя предположениями:

  1. все "tabbable" UITextFields находятся на одном родительском представлении.
  2. их "tab-order" определяется свойством tag.

предполагая, что вы можете переопределить textFieldShouldReturn: как это:

-(BOOL)textFieldShouldReturn:(UITextField*)textField
{
  NSInteger nextTag = textField.tag + 1;
  // Try to find next responder
  UIResponder* nextResponder = [textField.superview viewWithTag:nextTag];
  if (nextResponder) {
    // Found next responder, so set it.
    [nextResponder becomeFirstResponder];
  } else {
    // Not found, so remove keyboard.
    [textField resignFirstResponder];
  }
  return NO; // We do not want UITextField to insert line-breaks.
}

добавьте еще немного кода, и предположения также можно игнорировать.

Swift 4.0

 func textFieldShouldReturn(_ textField: UITextField) -> Bool {
    let nextTag = textField.tag + 1
    // Try to find next responder
    let nextResponder = textField.superview?.viewWithTag(nextTag) as UIResponder!

    if nextResponder != nil {
        // Found next responder, so set it
        nextResponder?.becomeFirstResponder()
    } else {
        // Not found, so remove keyboard
        textField.resignFirstResponder()
    }

    return false
}

если супервизор текстового поля будет UITableViewCell, то следующий ответчик будет

let nextResponder = textField.superview?.superview?.superview?.viewWithTag(nextTag) as UIResponder!

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

  • ближе к реализации текстового поля OSX, где текстовое поле знает, где фокус должен идти дальше
  • не полагается на установку или использование тегов - которые, IMO хрупкие для этого случая использования
  • может быть расширен для работы с обоими UITextField и UITextView элементы управления -- или любой элемент управления клавиатуры UI
  • не захламлять ваш контроллер вида с шаблоном UITextField делегат кода
  • прекрасно интегрируется с IB и может быть настроен через знакомый вариант-перетаскивание для подключения розеток.

создайте подкласс UITextField, который имеет IBOutlet свойство называется nextField. Вот заголовок:

@interface SOTextField : UITextField

@property (weak, nonatomic) IBOutlet UITextField *nextField; 

@end

и вот реализация:

@implementation SOTextField

@end

в контроллере вида вы создадите -textFieldShouldReturn: делегат метод:

- (BOOL)textFieldShouldReturn:(UITextField *)textField {
    if ([textField isKindOfClass:[SOTextField class]]) {
        UITextField *nextField = [(SOTextField *)textField nextField];

        if (nextField) {
            dispatch_async(dispatch_get_current_queue(), ^{
                [nextField becomeFirstResponder];
            });
        }
        else {
            [textField resignFirstResponder];
        }
    }

    return YES;
}

В IB измените UITextFields, чтобы использовать SOTextField класса. Затем, также В IB, установите делегат для каждого из "SOTextFields" в "владелец файла" (который находится там, где вы помещаете код для метода делегата - textFieldShouldReturn). Красота этого дизайна заключается в том, что теперь вы можете просто щелкнуть правой кнопкой мыши на любом текстовом поле и назначить nextfield outlet следующему SOTextField объект, который вы хотите быть следующей ответчика.

Assigning nextField in IB

кроме того, вы можете делать классные вещи, такие как цикл текстовых полей, так что после того, как последний теряет фокус, первый снова получит фокус.

это можно легко расширить, чтобы автоматически назначить returnKeyType на SOTextField до UIReturnKeyNext если назначено nextField -- одна вещь вручную настраивается меньше.


вот мое решение этой проблемы.

чтобы решить эту проблему (и потому, что я ненавижу полагаться на теги), я решил добавить пользовательское свойство в объект UITextField. Другими словами, Я создал категорию В UITextField следующим образом:

UITextField + Расширенный.h

@interface UITextField (Extended)

@property(retain, nonatomic)UITextField* nextTextField;

@end

UITextField + Расширенный.м

#import "UITextField+Extended.h"
#import <objc/runtime.h>

static char defaultHashKey;

@implementation UITextField (Extended)

- (UITextField*) nextTextField { 
    return objc_getAssociatedObject(self, &defaultHashKey); 
}

- (void) setNextTextField:(UITextField *)nextTextField{
    objc_setAssociatedObject(self, &defaultHashKey, nextTextField, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 
}

@end

теперь, вот как я его использую:

UITextField *textField1 = ...init your textfield
UITextField *textField2 = ...init your textfield
UITextField *textField3 = ...init your textfield

textField1.nextTextField = textField2;
textField2.nextTextField = textField3;
textField3.nextTextField = nil;

и реализуйте метод textFieldShouldReturn:

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

    UITextField *next = theTextField.nextTextField;
    if (next) {
        [next becomeFirstResponder];
    } else {
        [theTextField resignFirstResponder];
    }

    return NO; 
}

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

надеюсь, это поможет.


здесь один, без делегации:

tf1.addTarget(tf2, action: #selector(becomeFirstResponder), for: .editingDidEndOnExit)
tf2.addTarget(tf3, action: #selector(becomeFirstResponder), for: .editingDidEndOnExit)

ObjC:

[tf1 addTarget:tf2 action:@selector(becomeFirstResponder) forControlEvents:UIControlEventEditingDidEndOnExit];
[tf2 addTarget:tf3 action:@selector(becomeFirstResponder) forControlEvents:UIControlEventEditingDidEndOnExit];

работает с использованием (в основном неизвестных) UIControlEventEditingDidEndOnExit UITextField действие.

вы также можете легко подключить это в раскадровке, так что без делегирования или код.

Edit: на самом деле я не могу понять, как подключить это в раскадровке. becomeFirstResponder не представляется предлагаемым действием для этого контрольного события, что жаль. Тем не менее, вы можете подключить все свои текстовые поля до одного действия в вашем ViewController, который затем определяет, какое текстовое поле becomeFirstResponder на основе отправителя (хотя тогда это не так элегантно, как вышеупомянутое программное решение, поэтому IMO делает это с вышеуказанным кодом в viewDidLoad).


быстрое расширение, которое применяет ответ mxcl, чтобы сделать это особенно легко (адаптировано к swift 2.3 путешественником):

extension UITextField {
    class func connectFields(fields:[UITextField]) -> Void {
        guard let last = fields.last else {
            return
        }
        for i in 0 ..< fields.count - 1 {
            fields[i].returnKeyType = .Next
            fields[i].addTarget(fields[i+1], action: "becomeFirstResponder", forControlEvents: .EditingDidEndOnExit)
        }
        last.returnKeyType = .Done
        last.addTarget(last, action: #selector(UIResponder.resignFirstResponder), forControlEvents: .EditingDidEndOnExit)
    }
}

это простой в использовании:

UITextField.connectFields([field1, field2, field3])

расширение установит кнопку возврата в " Далее "для всех, кроме последнего поля и" готово " для последнего поля, и сдвинет фокус / отклонит клавиатуру, когда они будут нажаты.

Swift

extension UITextField {
    class func connectFields(fields:[UITextField]) -> Void {
        guard let last = fields.last else {
            return
        }
        for var i = 0; i < fields.count - 1; i += 1 {
            fields[i].returnKeyType = .Next
            fields[i].addTarget(fields[i+1], action: "becomeFirstResponder", forControlEvents: .EditingDidEndOnExit)
        }
        last.returnKeyType = .Done
        last.addTarget(last, action: "resignFirstResponder", forControlEvents: .EditingDidEndOnExit)
    }
}

SWIFT 3: используйте вот так -

UITextField.connectFields(fields: [field1, field2])

Extension:
    extension UITextField {
        class func connectFields(fields:[UITextField]) -> Void {
            guard let last = fields.last else {
                return
            }
            for i in 0 ..< fields.count - 1 {
                fields[i].returnKeyType = .next
                fields[i].addTarget(fields[i+1], action: #selector(UIResponder.becomeFirstResponder), for: .editingDidEndOnExit)
            }
            last.returnKeyType = .go
            last.addTarget(last, action: #selector(UIResponder.resignFirstResponder), for: .editingDidEndOnExit)
        }
    }

более последовательным и надежным способом является использование NextResponderTextField Вы можете настроить его полностью из interface builder без необходимости установки делегата или использования view.tag.

все, что вам нужно сделать, это

  1. выберите тип класс UITextField на NextResponderTextField enter image description here
  2. затем установите розетки nextResponderField чтобы указать на следующий ответчик, это может быть что угодно UITextField и UIResponder подкласс. Это может быть также UIButton и библиотека достаточно умна, чтобы вызвать TouchUpInside событие кнопки, только если оно включено. enter image description hereenter image description here

вот библиотека в действии:

enter image description here


Мне нравятся решения OO, которые уже были предложены Anth0 и Answerbot. Тем не менее, я работал над быстрым и маленьким POC, поэтому я не хотел загромождать вещи подклассами и категориями.

другим простым решением является создание NSArray полей и поиск следующего поля при нажатии кнопки Далее. Не решение OO, а быстрое, простое и простое в реализации. Кроме того, вы можете увидеть и изменить порядок с первого взгляда.

вот мой код (построенный на другом ответы в этой теме):

@property (nonatomic) NSArray *fieldArray;

- (void)viewDidLoad {
    [super viewDidLoad];

    fieldArray = [NSArray arrayWithObjects: firstField, secondField, thirdField, nil];
}

- (BOOL) textFieldShouldReturn:(UITextField *) textField {
    BOOL didResign = [textField resignFirstResponder];
    if (!didResign) return NO;

    NSUInteger index = [self.fieldArray indexOfObject:textField];
    if (index == NSNotFound || index + 1 == fieldArray.count) return NO;

    id nextField = [fieldArray objectAtIndex:index + 1];
    activeField = nextField;
    [nextField becomeFirstResponder];

    return NO;
}
  • Я всегда возвращаю нет, потому что я не хочу вставлять разрыв строки. Просто подумал, что я укажу на это, так как, когда я вернусь, да, он автоматически выйдет из последующих полей или вставит разрыв строки в моем TextView. Мне потребовалось некоторое время, чтобы понять это.
  • активного поля отслеживает активное поле в случае прокрутки необходимо unobscure поле с клавиатуры. Если у вас есть аналогичный код, убедитесь, что вы назначили activeField перед изменением первого ответчика. Изменение первого ответчика немедленно и немедленно запустит событие KeyboardWasShown.

вот реализация табуляции с использованием категории В UIControl. Это решение имеет все преимущества методов от Michael и Anth0, но работает для всех UIControls, а не только UITextFields. Он также легко работает с Interface Builder и раскадровками.

источник и образец приложение: репозиторий GitHub для UIControlsWithTabbing

использование:

- (BOOL)textFieldShouldReturn:(UITextField *)textField
{
    [textField transferFirstResponderToNextControl];
    return NO;
}

Assigning nextControl in Interface Builder

:
//
// UIControl+NextControl.h
// UIControlsWithTabbing
//

#import <UIKit/UIKit.h>

@interface UIControl (NextControl)

@property (nonatomic, weak) IBOutlet UIControl *nextControl;

- (BOOL)transferFirstResponderToNextControl;

@end

реализация:

#import "UIControl+NextControl.h"
#import <objc/runtime.h>

static char defaultHashKey;

@implementation UIControl (NextControl)

- (UIControl *)nextControl
{
    return objc_getAssociatedObject(self, &defaultHashKey);
}

- (void)setNextControl:(UIControl *)nextControl
{
    objc_setAssociatedObject(self, &defaultHashKey, nextControl, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

- (BOOL)transferFirstResponderToNextControl
{
    if (self.nextControl)
    {
        [self.nextControl becomeFirstResponder];

        return YES;
    }

    [self resignFirstResponder];

    return NO;
}

@end

после выхода из одного текстового поля вы вызываете [otherTextField becomeFirstResponder] и следующее поле получает фокус.

на самом деле это может быть сложная проблема, так как часто вы также захотите прокрутить экран или иным образом настроить положение текстового поля, чтобы его было легко увидеть при редактировании. Просто убедитесь, что вы много тестируете, входя и выходя из текстовых полей по-разному, а также уходя рано (всегда дайте пользователю возможность отклонить клавиатура вместо перехода к следующему полю, обычно с "готово" в навигационной панели)


 -(BOOL)textFieldShouldReturn:(UITextField *)textField
{
   [[self.view viewWithTag:textField.tag+1] becomeFirstResponder];
   return YES;
}

Я пробовал много кодов и, наконец, это сработало для меня в Swift 3.0 Последний [Март 2017]

на ViewController класс должен быть унаследован от UITextFieldDelegate для того, чтобы этот код работал.

class ViewController: UIViewController,UITextFieldDelegate  

добавьте текстовое поле с соответствующим номером тега, и этот номер тега используется для передачи элемента управления в соответствующее текстовое поле на основе добавочного номера тега, назначенного ему.

override func viewDidLoad() {
    userNameTextField.delegate = self
    userNameTextField.tag = 0
    userNameTextField.returnKeyType = UIReturnKeyType.next
    passwordTextField.delegate = self
    passwordTextField.tag = 1
    passwordTextField.returnKeyType = UIReturnKeyType.go
}

в приведенном выше коде returnKeyType = UIReturnKeyType.next где сделать ключ клавиша возврата на экран в виде Next у вас также есть другие варианты как Join/Go etc, на основе вашего приложения изменить значения.

этой textFieldShouldReturn является методом uitextfielddelegate контролируется, и здесь у нас есть следующий выбор поля на основе значения тега incrementation

func textFieldShouldReturn(_ textField: UITextField) -> Bool {
    if let nextField = textField.superview?.viewWithTag(textField.tag + 1) as? UITextField {
        nextField.becomeFirstResponder()
    } else {
        textField.resignFirstResponder()
        return true;
    }
    return false
 }

Я удивлен тем, сколько ответов здесь не понимают одну простую концепцию: навигация по элементам управления в вашем приложении-это не то, что должны делать сами представления. Это контроллер'ы работа, чтобы решить, какой элемент управления, чтобы сделать следующий первый ответчик.

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

Так вот что я придумал. Ваша форма должна управляться контроллером представления, и контроллеры вида являются частью цепочки ответчиков. Таким образом, вы можете реализовать следующие методы:

#pragma mark - Key Commands

- (NSArray *)keyCommands
{
    static NSArray *commands;

    static dispatch_once_t once;
    dispatch_once(&once, ^{
        UIKeyCommand *const forward = [UIKeyCommand keyCommandWithInput:@"\t" modifierFlags:0 action:@selector(tabForward:)];
        UIKeyCommand *const backward = [UIKeyCommand keyCommandWithInput:@"\t" modifierFlags:UIKeyModifierShift action:@selector(tabBackward:)];

        commands = @[forward, backward];
    });

    return commands;
}

- (void)tabForward:(UIKeyCommand *)command
{
    NSArray *const controls = self.controls;
    UIResponder *firstResponder = nil;

    for (UIResponder *const responder in controls) {
        if (firstResponder != nil && responder.canBecomeFirstResponder) {
            [responder becomeFirstResponder]; return;
        }
        else if (responder.isFirstResponder) {
            firstResponder = responder;
        }
    }

    [controls.firstObject becomeFirstResponder];
}

- (void)tabBackward:(UIKeyCommand *)command
{
    NSArray *const controls = self.controls;
    UIResponder *firstResponder = nil;

    for (UIResponder *const responder in controls.reverseObjectEnumerator) {
        if (firstResponder != nil && responder.canBecomeFirstResponder) {
            [responder becomeFirstResponder]; return;
        }
        else if (responder.isFirstResponder) {
            firstResponder = responder;
        }
    }

    [controls.lastObject becomeFirstResponder];
}

дополнительная логика для прокрутки экранных ответчиков, видимых заранее, может применяться.

Другим преимуществом этого подхода является то, что вам не нужно подкласс всех видов элементов управления, которые вы можете захотеть отобразить (например,UITextFields), но может вместо этого управлять логикой на уровне контроллера, где, будем честными, является в нужном месте сделать так.


очень простой способ отклонения клавиатуры при нажатии кнопки "Готово":

создайте новое IBAction в заголовке

- (IBAction)textFieldDoneEditing:(id)sender;

в файле реализации (.файл м) добавьте следующий метод:

- (IBAction)textFieldDoneEditing:(id)sender 
{ 
  [sender resignFirstResponder];
}

затем, когда вы приходите, чтобы связать IBAction с textfield-link с событием "did End On Exit".


Сначала установите клавишу возврата клавиатуры в xib, иначе вы можете написать код в viewdidload:

passWord.returnKeyType = UIReturnKeyNext;

-(BOOL)textFieldShouldReturn:(UITextField *)textField
{
    if(textField == eMail) {
        [textField resignFirstResponder];
        [userName becomeFirstResponder];
    }
    if (textField==userName) {
        [textField resignFirstResponder];
        [passWord becomeFirstResponder];
    }
    if (textField==passWord) {
        [textField resignFirstResponder];
        [country becomeFirstResponder];
    }
    if (textField==country) {
        [textField resignFirstResponder];
    }
    return YES;
}

я добавил к ответу PeyloW в случае, если вы хотите реализовать предыдущую / следующую функциональность кнопки:

- (IBAction)moveThroughTextFields:(UIBarButtonItem *)sender 
{
    NSInteger nextTag;
    UITextView *currentTextField = [self.view findFirstResponderAndReturn];

    if (currentTextField != nil) {
        // I assigned tags to the buttons.  0 represent prev & 1 represents next
        if (sender.tag == 0) {
            nextTag = currentTextField.tag - 1;

        } else if (sender.tag == 1) {
            nextTag = currentTextField.tag + 1;
        }
    }
    // Try to find next responder
    UIResponder* nextResponder = [self.view viewWithTag:nextTag];
    if (nextResponder) {
        // Found next responder, so set it.
        // I added the resign here in case there's different keyboards in place.
        [currentTextField resignFirstResponder];
        [nextResponder becomeFirstResponder];
    } else {
        // Not found, so remove keyboard.
        [currentTextField resignFirstResponder];

    }
}

где вы подкласс UIView, как это:

@implementation UIView (FindAndReturnFirstResponder)
- (UITextView *)findFirstResponderAndReturn
{
    for (UITextView *subView in self.subviews) {
        if (subView.isFirstResponder){
            return subView;
        }
    }
    return nil;
}
@end

Привет всем, Посмотрите пожалуйста этот

- (void)nextPrevious:(id)sender
{

  UIView *responder = [self.view findFirstResponder];   

  if (nil == responder || ![responder isKindOfClass:[GroupTextField class]]) {
    return;
  }

  switch([(UISegmentedControl *)sender selectedSegmentIndex]) {
    case 0:
      // previous
      if (nil != ((GroupTextField *)responder).previousControl) {
        [((GroupTextField *)responder).previousControl becomeFirstResponder];
        DebugLog(@"currentControl: %i previousControl: %i",((GroupTextField *)responder).tag,((GroupTextField *)responder).previousControl.tag);
      }
      break;
    case 1:
      // next
      if (nil != ((GroupTextField *)responder).nextControl) {
        [((GroupTextField *)responder).nextControl becomeFirstResponder];
        DebugLog(@"currentControl: %i nextControl: %i",((GroupTextField *)responder).tag,((GroupTextField *)responder).nextControl.tag);
      }     
      break;    
  }
}

Я попытался решить эту проблему, используя более сложный подход, основанный на назначении каждой ячейки (или UITextField) в UITableView уникальное значение тега, которое можно получить позже: активировать-next-uitextfield-in-uitableview-ios

надеюсь, это поможет!


Я только что создал новый Pod при работе с этим материалом GNTextFieldsCollectionManager. Он автоматически обрабатывает следующую / последнюю проблему текстового поля и очень прост в использовании:

[[GNTextFieldsCollectionManager alloc] initWithView:self.view];

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


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

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

в файле NIB я подключаю текстовые поля в нужном порядке в этот массив 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;
}

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 ;
}

У меня было около 10 + UITextField в моей раскадровке, и я включил следующую функциональность, создав массив UITextField и сделав следующий UITextField первым ответчиком. Вот файл реализации:

#import "RegistrationTableViewController.h"

@interface RegistrationTableViewController ()
@property (weak, nonatomic) IBOutlet UITextField *fullNameTextField;
@property (weak, nonatomic) IBOutlet UITextField *addressTextField;
@property (weak, nonatomic) IBOutlet UITextField *address2TextField;
@property (weak, nonatomic) IBOutlet UITextField *cityTextField;
@property (weak, nonatomic) IBOutlet UITextField *zipCodeTextField;
@property (weak, nonatomic) IBOutlet UITextField *urlTextField;
@property (weak, nonatomic) IBOutlet UITextField *usernameTextField;
@property (weak, nonatomic) IBOutlet UITextField *emailTextField;
@property (weak, nonatomic) IBOutlet UITextField *passwordTextField;
@property (weak, nonatomic) IBOutlet UITextField *confirmPWTextField;

@end
NSArray *uiTextFieldArray;
@implementation RegistrationTableViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    NSLog(@"view did load");
    uiTextFieldArray = @[self.fullNameTextField,self.addressTextField,self.address2TextField,self.cityTextField,self.zipCodeTextField,self.urlTextField,self.usernameTextField,self.emailTextField,self.passwordTextField,self.confirmPWTextField];
    for(UITextField *myField in uiTextFieldArray){
        myField.delegate = self;
    }


}
-(BOOL)textFieldShouldReturn:(UITextField *)textField{
    long index = [uiTextFieldArray indexOfObject:textField];
    NSLog(@"%ld",index);
    if(index < (uiTextFieldArray.count - 1)){
        [uiTextFieldArray[++index] becomeFirstResponder];
    }else{
        [uiTextFieldArray[index] resignFirstResponder];
    }
    return YES;
}
- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

@end

Это сработало для меня в Xamarin.для iOS есть некоторые подробности. Измените кнопку клавиатуры на Next, передайте управление следующему 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 вы будете:

Если ваши текстовые поля не имеют тега, установите его сейчас:

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.

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

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

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
    }

}

и назовите его так (например) с помощью делегата textfield:

func textFieldShouldReturn(textField: UITextField) -> Bool {
    textField.resignFirstResponder()
    textField.nextTextFieldField()?.becomeFirstResponder()
    return true
}

решение в Swift 3.1, после подключения textfields IBOutlets установите делегат textfields в viewDidLoad, а затем перейдите к действию в textFieldShouldReturn

class YourViewController: UIViewController,UITextFieldDelegate {

        @IBOutlet weak var passwordTextField: UITextField!
        @IBOutlet weak var phoneTextField: UITextField!

        override func viewDidLoad() {
            super.viewDidLoad()
            self.passwordTextField.delegate = self
            self.phoneTextField.delegate = self
            // Set your return type
            self.phoneTextField.returnKeyType = .next
            self.passwordTextField.returnKeyType = .done
        }

        func textFieldShouldReturn(_ textField: UITextField) -> Bool{
            if textField == self.phoneTextField {
                self.passwordTextField.becomeFirstResponder()
            }else if textField == self.passwordTextField{
                // Call login api
                self.login()
            }
            return true
        }

    }

в textFieldShouldReturn вы должны проверить, что текстовое поле, в котором вы находитесь, не является последним, когда они нажимают next, и если его n ot не отклоняет клавиатуру..


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

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

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

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


без использования тегов и без добавления свойства для nextField / nextTextField вы можете попробовать это, чтобы эмулировать вкладку, где "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];
                                                    }];

Я использую ответ Майкла г. Эммонса уже около года, отлично работает. Недавно я заметил, что вызов resignFirstResponder, а затем becomeFirstResponder немедленно может привести к "Глюку" клавиатуры, исчезая, а затем немедленно появляясь. Я немного изменил его версию, чтобы пропустить resignFirstResponder, если nextField доступен.

- (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;

}

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

pod 'IQKeyboardManager'

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


вот Swift 3 версия Anth0'ы ответ. Я размещаю его здесь, чтобы помочь любым разработчикам swift в желании воспользоваться его отличным ответом! Я взял на себя смелость добавить тип ключа возврата "Next" при установке связанного объекта.

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{ ?.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.