【发布时间】:2015-01-17 15:59:15
【问题描述】:
我使用“返回键”的“下一步”值来获取“下一步”按钮代替“完成”按钮,但(显然)按下它不会自动移动到我视图中的下一个 UITextField。
这样做的正确方法是什么?我已经看到了很多答案,但是有人有快速的解决方案吗?
【问题讨论】:
标签: ios swift uitextfield
我使用“返回键”的“下一步”值来获取“下一步”按钮代替“完成”按钮,但(显然)按下它不会自动移动到我视图中的下一个 UITextField。
这样做的正确方法是什么?我已经看到了很多答案,但是有人有快速的解决方案吗?
【问题讨论】:
标签: ios swift uitextfield
确保您的文本字段已设置其委托并实现 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
}
完成此操作后,只需在viewDidLoad 中设置每个文本字段的新nextField 属性即可:
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 属性:
(您也可以在这里设置您的委托。)
当然,如果nextField 属性返回nil,键盘就会隐藏起来。
【讨论】:
objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN 而不是UInt(OBJC_ASSOCIATION_RETAIN)
.OBJC_ASSOCIATION_RETAIN 也应该可以工作
这是 Swift 中的一个示例:
我创建了一个包含 6 个UITextFields 的屏幕。我在 Interface Builder 中为它们分配了标签 1 到 6。我还在 IB 中将 Return 键更改为 Next。然后我实现了以下内容:
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.
}
}
【讨论】:
textField.superView?.viewWithTag(nextTag) as? UITextField,你为什么从textFieldShouldReturn返回false?
textFieldShouldReturn 中的textField.superview?.viewWithTag(nextTag) 明确设置为self.tableView.viewWithTag(nextTag) 后,它起作用了。我在自定义单元格中添加了 1 个文本文件,并使用此自定义单元格创建了多行带有文本字段的行。
其他答案没有任何问题,这只是一种不同的方法,其好处是更专注于 OOP - 恕我直言(虽然这需要更多的工作,但可以重复使用)。在情节提要中,我开始添加具有不同范围(例如 800-810)的标签,这些标签定义了我想要在其间移动的字段的特定顺序。这样做的好处是可以跨主视图中的所有子视图工作,以便可以根据需要在 UITextField 和 UITextView(以及任何其他控件)之间导航。
通常 - 我通常尝试在视图和自定义事件处理程序对象之间使用视图控制器消息。所以我使用从自定义委托类传回视图控制器的消息(又名 NSNotification)。
(文本字段委托处理程序)
注意:在 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 接收到delegate的消息,并使用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)
}
....
}
【讨论】:
获得我认为有用的结果的另一种方法。我的应用程序有 11 个文本字段,后跟一个文本视图。我需要能够使用下一个键循环浏览所有字段,然后在 textview 之后退出键盘(即其他注释)。
在情节提要中,我在从 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
}
【讨论】: