it-roy-ru.com

Использование "Далее" в качестве ключа возврата

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

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

29
Octavio Antonio Cedeño

Убедитесь, что в ваших текстовых полях установлен делегат, и реализуйте метод 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

(Настройте своего делегата, пока вы здесь тоже.)


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

82
nhgrif

Вот пример в Swift:

Я создал экран с 6 UITextFields. Я назначил им теги с 1 по 6 в Интерфейсном Разработчике. Я также изменил ключ возврата на следующий в 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.
    }
}
8
vacawama

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

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

(Обработчик делегата TextField)

Примечание. В AppDelegate.Swift: let 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)
    }

    ....
}
2
Tommie C.

Просто еще один способ добиться результата, который я нашел полезным. У моего приложения было 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
}
1
sarac