Использование "Следующий" в качестве возвращаемого ключа
Я использую значение "Next" для "Return Key", чтобы получить кнопку "Далее" вместо кнопки "Готово", но (очевидно) нажатие на нее не переключается автоматически на следующий UITextField в моем представлении.
Какой правильный способ сделать это? Я видел много ответов, но у кого есть быстрое решение?
Ответы
Ответ 1
Убедитесь, что ваши текстовые поля имеют свой набор делегатов и реализуют метод 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
, клавиатура просто скрывается.
Ответ 2
Вот пример в Swift:
Я создал экран с 6 UITextField
s. Я назначил им теги с 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.
}
}
Ответ 3
Нет ничего плохого в других ответах, это просто другой подход с выгодой для того, чтобы быть более сосредоточенным на ООП-имхо (хотя это немного больше работы впереди, его можно использовать повторно). В раскадровке я начинаю добавлять теги с определенным диапазоном (например, 800-810), которые определяют конкретный порядок полей, которые я хочу переместить между ними. Это полезно для работы во всех подзонах в главном представлении, так что при необходимости можно перемещаться между UITextField и UITextView (и любым другим элементом управления).
Как правило, я обычно пытаюсь получить сообщение диспетчера представлений между представлениями и настраиваемыми объектами обработчика событий. Поэтому я использую сообщение (ака, NSNotification), переданное обратно контроллеру представления из пользовательского класса делегата.
(Обработчик делегатов TextField)
Примечание. В приложении 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)
}
....
}
Ответ 4
Еще один способ добиться результата, который я нашел полезным. В моем приложении было 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
}