2014-11-19 2 views
28

Я использую значение «Next» для «Return Key», чтобы получить кнопку «Далее» вместо кнопки «Готово», но (очевидно) нажатие на нее не автоматически переходит к следующему UITextField на мой взгляд.Использование «Next» в качестве возвращаемого ключа

Каков правильный способ сделать это? Я видел много ответов, но у кого есть быстрое решение?

ответ

79

Убедитесь, что ваши текстовые поля имеют свой набор делегатов и реализуют метод textFieldShouldReturn. Это метод, который вызывается, когда пользователь удаляет ключ возврата (независимо от того, как он выглядит).

метод может выглядеть следующим образом:

func textFieldShouldReturn(textField: UITextField) -> Bool { 
    if textField == self.field1 { 
     self.field2.becomeFirstResponder() 
    } 

    return true 
} 

Фактическая логика здесь может меняться. Есть множество подходов, и я бы определенно советовал против массивной сети if/else, если у вас много текстовых полей, но суть в том, чтобы определить, какой вид в данный момент активен, чтобы определить, какой вид должен стать активным. После того, как вы определили, какой вид должен стать активным, вызовите этот метод вида becomeFirstResponder.


Для некоторых чистоты кода, вы могли бы рассмотреть UITextField расширение, которое выглядит примерно так:

private var kAssociationKeyNextField: UInt8 = 0 

extension UITextField { 
    var nextField: UITextField? { 
     get { 
      return objc_getAssociatedObject(self, &kAssociationKeyNextField) as? UITextField 
     } 
     set(newField) { 
      objc_setAssociatedObject(self, &kAssociationKeyNextField, newField, .OBJC_ASSOCIATION_RETAIN) 
     } 
    } 
} 

А затем изменить наш textFieldShouldReturn способ выглядеть следующим образом:

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

После вы сделали это, это просто вопрос настройки нового свойства нового текстового поля nextField в viewDidLoad :

self.field1.nextField = self.field2 
self.field2.nextField = self.field3 
self.field3.nextField = self.field4 
self.field4.nextField = self.field1 

Хотя, если мы действительно хотим, мы могли бы предварить собственность с @IBOutlet, и что позволит нам подключить наш «nextField» право собственности в интерфейсе строителя.

Измените расширение выглядеть следующим образом:

private var kAssociationKeyNextField: UInt8 = 0 

extension UITextField { 
    @IBOutlet var nextField: UITextField? { 
     get { 
      return objc_getAssociatedObject(self, &kAssociationKeyNextField) as? UITextField 
     } 
     set(newField) { 
      objc_setAssociatedObject(self, &kAssociationKeyNextField, newField, .OBJC_ASSOCIATION_RETAIN) 
     } 
    } 
} 

А теперь подключить к nextField свойство в интерфейсе строителя:

enter image description here

(Настройка вашего делегата в то время как вы здесь .)


И из co urse, если свойство nextField возвращает nil, клавиатура просто скрывается.

+0

Где разместить код расширения в файле? –

+0

Везде. Я бы сделал для него новый файл. – nhgrif

+0

Я продолжаю получать сообщение об ошибке с расширением. Он говорит, что это объявление действует только в источнике файла. –

8

Ниже приведен пример, в Swift:

Я создал экран с 6 UITextField с. Я назначил им теги с 1 по 6 в Interface Builder. Я также изменил ключ Return на Next в IB.Тогда я осуществил следующее:

import UIKit 

// Make your ViewController a UITextFieldDelegate  
class ViewController: UIViewController, UITextFieldDelegate { 

    // Use a dictionary to define text field order 1 goes to 2, 2 goes to 3, etc. 
    let nextField = [1:2, 2:3, 3:4, 4:5, 5:6, 6:1] 

    override func viewDidLoad() { 
     super.viewDidLoad() 
     // Do any additional setup after loading the view, typically from a nib. 

     // Make ourselves the delegate of the text fields so that textFieldShouldReturn 
     // will be called when the user hits the Next/Return key 
     for i in 1...6 { 
      if let textField = self.view.viewWithTag(i) as? UITextField { 
       textField.delegate = self 
      } 
     } 
    } 

    // This is called when the user hits the Next/Return key   
    func textFieldShouldReturn(textField: UITextField) -> Bool { 
     // Consult our dictionary to find the next field 
     if let nextTag = nextField[textField.tag] { 
      if let nextResponder = textField.superview?.viewWithTag(nextTag) { 
       // Have the next field become the first responder 
       nextResponder.becomeFirstResponder() 
      } 
     } 
     // Return false here to avoid Next/Return key doing anything 
     return false 
    } 

    override func didReceiveMemoryWarning() { 
     super.didReceiveMemoryWarning() 
     // Dispose of any resources that can be recreated. 
    } 
} 
+0

Должно быть, вероятно, 'textField.superView? .viewWithTag (nextTag), как ? UITextField', и почему вы возвращаете 'false' из' textFieldShouldReturn'? – nhgrif

+0

Если мы посвящаем себя тому, чтобы быть настолько дотошным в IB, что устанавливаем очень специфические теги для каждого текстового поля (что я рекомендую против этого кошмара для обслуживания), почему бы нам не настроить делегат textField в IB? – nhgrif

+0

Как вы назначили каждый UITextField с тегом? –

2

Там нет ничего плохого с другими ответами, это просто другой подход с выгодой быть более сосредоточенным на ООП - имхо (хотя это немного больше работы впереди, его можно использовать повторно). В раскадровке я начинаю добавлять теги с определенным диапазоном (например, 800-810), которые определяют конкретный порядок полей, которые я хочу переместить между ними. Это позволяет работать во всех подзонах в главном представлении, чтобы можно было перемещаться между UITextField и UITextView (и любым другим элементом управления) по мере необходимости.

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

(TextField Делегат Handler)

Примечание: В AppDelegate.swift: пусть defaultCenter = NSNotificationCenter.defaultCenter()

//Globally scoped 
struct MNGTextFieldEvents { 
    static let NextButtonTappedForTextField = "MNGTextFieldHandler.NextButtonTappedForTextField" 
} 

class MNGTextFieldHandler: NSObject, UITextFieldDelegate { 

    var fields:[UITextField]? = [] 

    func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool { 
     return true 
    } 
    func textFieldDidBeginEditing(textField: UITextField) { 
     textField.backgroundColor = UIColor.yellowColor() 
    } 
    func textFieldDidEndEditing(textField: UITextField) { 
     textField.backgroundColor = UIColor.whiteColor() 
    } 
    func textFieldShouldBeginEditing(textField: UITextField) -> Bool { 
     return true 
    } 
    func textFieldShouldClear(textField: UITextField) -> Bool { 
     return false 
    } 
    func textFieldShouldEndEditing(textField: UITextField) -> Bool { 
     return true 
    } 
    func textFieldShouldReturn(textField: UITextField) -> Bool { 

     //passes the message and the textField (with tag) calling the method 
     defaultCenter.postNotification(NSNotification(name: MNGTextFieldEvents.NextButtonTappedForTextField, object: textField)) 

     return false 
    } 
} 

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

(View Controller получает сообщение от делегата и передает инструкцию с помощью функции advanceToNextField)

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

class MyViewController: UIViewController { 
    @IBOutlet weak var tagsField: UITextField! { didSet { 
     (tagsField.delegate as? MNGTextFieldHandler)!.fields?.append(tagsField) 
     } 
    } 
    @IBOutlet weak var titleField: UITextField!{ didSet { 
     (titleField.delegate as? MNGTextFieldHandler)!.fields?.append(titleField) 
     } 
    } 
    @IBOutlet weak var textView: UITextView! { didSet { 
    (textView.delegate as? MNGTextViewHandler)!.fields?.append(textView) 

     } 
    } 

    private struct Constants { 
    static let SelectorAdvanceToNextField = Selector("advanceToNextField:") 
} 
    override func viewWillAppear(animated: Bool) { 
     super.viewWillAppear(animated) 
     registerEventObservers() 
    } 
    override func viewDidDisappear(animated: Bool) { 
     super.viewDidDisappear(animated) 
     deRegisterEventObservers() 
    } 

    func advanceToNextField(notification:NSNotification) { 
     let currentTag = (notification.object as! UIView).tag 

     for aView in self.view.subviews { 
      if aView.tag == currentTag + 1 { 
       aView.becomeFirstResponder() 
      } 
     } 
    } 

    func registerEventObservers() { 

     defaultCenter.addObserver(self, selector: Constants.SelectorAdvanceToNextField, name: MNGTextFieldEvents.NextButtonTappedForTextField, object: nil) 
    } 

    func deRegisterEventObservers() { 

     defaultCenter.removeObserver(self, name: MNGTextFieldEvents.NextButtonTappedForTextField, object: nil) 
    } 

    .... 
} 
1

Еще один способ достижения результата, который я нашел полезным. В моем приложении было 11 текстовых полей, а затем текстовое представление. Мне нужно было прокручивать все поля с помощью следующего ключа, а затем сбрасывать клавиатуру после текстового просмотра (т. Е. Другие заметки).

В раскадровке я установил тег во все поля (как текст, так и текст), начиная с 1 по 12, 12, являясь текстовым просмотром.

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

В коде, я написал следующее:

func textFieldShouldReturn(textField: UITextField) -> Bool { 

    let nextTag = textField.tag + 1 

    //Handle Textview transition, Textfield programmatically 
    if textField.tag == 11 { 
     //Current tag is 11, next field is a textview 
     self.OtherNotes.becomeFirstResponder() 

    } else if nextTag > 11 { 
     //12 is the end, close keyboard 
     textField.resignFirstResponder() 

    } else { 
     //Between 1 and 11 cycle through using next button 
     let nextResponder = self.view.viewWithTag(nextTag) as? UITextField 
     nextResponder?.becomeFirstResponder() 

    } 

    return false 
} 

func textFieldDidEndEditing(textField: UITextField) { 

    textField.resignFirstResponder() 
} 

func textView(textView: UITextView, shouldChangeTextInRange range: NSRange, replacementText text: String) -> Bool { 

    //Remove keyboard when clicking Done on keyboard 
    if(text == "\n") { 
     textView.resignFirstResponder() 
     return false 
    } 
    return true 
} 
Смежные вопросы