it-swarm.com.ru

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

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

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

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

Я реализовал действие textFieldShouldReturn, но теперь кнопки «Далее» и «Готово» закрывают клавиатуру.

469
phx

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

В любом случае это легко сделать с двумя допущениями:

  1. Все «табулируемые» UITextFields находятся в одном родительском представлении.
  2. Их «порядок табуляции» определяется свойством тега.

Предполагая это, вы можете переопределить 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!
557
PeyloW

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

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

Создайте подкласс 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). Прелесть этого дизайна в том, что теперь вы можете просто щелкнуть правой кнопкой мыши на любом textField и назначить выход nextField следующему объекту SOTextField, которым вы хотите стать следующим респондентом. 

Assigning nextField in IB

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

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

169
memmons

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

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

UITextField + Extended.h

@interface UITextField (Extended)

@property(retain, nonatomic)UITextField* nextTextField;

@end

UITextField + Extended.m

#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, каждый из которых знает, кто следующий в очереди.

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

78
Anth0

Вот один без делегации:

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

Работает с использованием (в основном неизвестного) действия UIControlEventEditingDidEndOnExitUITextField.

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

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

76
mxcl

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

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 <2.3

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)
        }
    }
45
Amos Joshua

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

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

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

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

enter image description here

25
mohamede1945

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

Другое простое решение - создать NSArray полей и искать следующее поле при нажатии кнопки next. Не 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;
}
  • Я всегда возвращаю НЕТ, потому что не хочу вставлять разрыв строки. Просто подумал, что я укажу на это, поскольку, когда я верну YES, он автоматически выйдет из последующих полей или вставит разрыв строки в моем TextView. Мне понадобилось немного времени, чтобы понять это.
  • activeField отслеживает активное поле в случае, если необходима прокрутка, чтобы скрыть поле с клавиатуры. Если у вас есть подобный код, убедитесь, что вы присвоили activeField перед сменой первого респондента. Смена первого респондента происходит немедленно и немедленно запускает событие KeyboardWasShown.
14
Marquee

Вот реализация вкладок с использованием категории на UIControl. Это решение обладает всеми преимуществами методов от Michael и Anth0, но работает для всех UIControls, а не только для UITextFields. Это также работает без проблем с Интерфейсным Разработчиком и раскадровками.

Источник и пример приложения: 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
11
picciano

Я перепробовал много кодов и, наконец, это сработало для меня в Swift 3.0 Latest [March 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 и т.д., В зависимости от того, как ваше приложение изменяет значения.

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

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
 }
10
BHUVANESH MOHANKUMAR

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

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

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

Очень простой способ отменить клавиатуру при нажатии кнопки «Готово»:

Создайте новый IBAction в шапке

- (IBAction)textFieldDoneEditing:(id)sender;

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

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

Затем, когда вы пришли, чтобы связать IBAction с текстовым полем - ссылка на событие «Закончено ли при выходе».

7
jcrowson

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

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

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

#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), но вместо этого вы можете управлять логикой на уровне контроллера, где, скажем честно, находится правильное место сделать это.

7
Christian Schnorr

Сначала установите клавишу возврата клавиатуры в 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;
}
6
mahesh chowdary

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

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

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

4
JakubKnejzlik

Решение в Swift 3.1. После подключения текстовых полей IBOutlets установите делегат текстовых полей в 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
        }

    }
4
Krishna Thakur

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

- (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;    
  }
}
4
rithik

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

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

4
Fabiano Francesconi

Я добавил к ответу 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
4
James Mertz

Это сработало для меня в 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
Daniele D.

Это простое решение в 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
    }

}

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

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

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

@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
Anticro
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]; // Инициализируем изменяемый массив в ViewDidLoad 
} 

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

 int tag = (int) textField.tag; 
 UITextField * txt = [self.array_Textfields objectAtIndex: tag]; 
 [txt intoFirstResponder]; 
 вернуть ДА; 
} 
3
Rebecca Curties

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

#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
3
Illegal Argument

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

 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 в последовательности, в которой вы хотите, чтобы реагировала следующая кнопка/prev. 

3
Mohammad Sadiq

Без использования тегов и без добавления свойства для 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
Cœur

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

2
Daniel

Я использую ответ Майкла Дж. Эммонса уже около года, отлично работает. Недавно я заметил, что вызов resignFirstResponder и затем сталFirstResponder немедленно может привести к «сбоям» клавиатуры, исчезновению и немедленному появлению. Я немного изменил его версию, чтобы пропустить 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] intoFirstResponder];}); 

 } 
 еще {
 [textField resignFirstResponder]; 
 } 
 } 
 еще {
 [textField resignFirstResponder]; 
 } 

 верните истину; 

} 
2
arinmorf

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

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
Justin Wright

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

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

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

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

2
jday

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

pod 'IQKeyboardManager'

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

2
Amit Shelgaonkar

Более безопасный и прямой путь, предполагая:

  • делегаты текстового поля установлены на вашем контроллере представления
  • все текстовые поля являются подпредставлениями одного и того же представления
  • в текстовых полях есть теги в том порядке, в котором вы хотите развиваться (например, textField2.tag = 2, textField3.tag = 3 и т. д.)
  • переход к следующему текстовому полю произойдет при нажатии кнопки return на клавиатуре (вы можете изменить это значение на next, done и т. д.)
  • вы хотите, чтобы клавиатура отклоняла после последнего текстового поля

Swift 4.1:

extension ViewController: UITextFieldDelegate {
    func textFieldShouldReturn(_ textField: UITextField) -> Bool {
        let nextTag = textField.tag + 1
        guard let nextTextField = textField.superview?.viewWithTag(nextTag) else {
            textField.resignFirstResponder()
            return false
        }

    nextTextField.becomeFirstResponder()

    return false

    }
}
1
jason z

Решение Swift 3 , используя упорядоченный массив UITextField's

func nextTextField() {
    let textFields = // Your textfields array

    for i in 0 ..< textFields.count{
        if let textfield = textFields[i], textfield.isFirstResponder{
            textfield.resignFirstResponder()
            if i+1 < textFields.count, let nextextfield = textFields[i+1]{
                nextextfield.becomeFirstResponder()
                return
            }
        }
    }
}
0
Maor