【问题标题】:Using "Next" as a Return Key使用“Next”作为返回键
【发布时间】:2015-01-17 15:59:15
【问题描述】:

我使用“返回键”的“下一步”值来获取“下一步”按钮代替“完成”按钮,但(显然)按下它不会自动移动到我视图中的下一个 UITextField。

这样做的正确方法是什么?我已经看到了很多答案,但是有人有快速的解决方案吗?

【问题讨论】:

    标签: ios swift 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
    }
    

    完成此操作后,只需在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,键盘就会隐藏起来。

    【讨论】:

    • 任何地方。我会为它制作一个新文件。
    • 我不断收到“扩展名”错误。它说这个声明只在文件源有效。
    • 不要开设新课程。只需将扩展名粘贴到文件顶部即可。扩展名中的 11 行代码应该是整个文件中唯一的文本。
    • 需要这个objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN 而不是UInt(OBJC_ASSOCIATION_RETAIN)
    • @Jonauz Simply .OBJC_ASSOCIATION_RETAIN 也应该可以工作
    【解决方案2】:

    这是 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
    • 如果我们致力于在 IB 中一丝不苟地为每个文本字段设置非常具体的标签(我建议不要这样维护噩梦),那么我们为什么不也设置textField 在 IB 中的代理也是?
    • 你是如何为每个 UITextField 分配标签的?
    • 在 Interface Builder 中,点击一个 UITextField,然后在右侧的 Attributes Inspector 中,如果向下滚动到 View 的设置,就会有一个 Tag 字段。
    • 谢谢。在我将textFieldShouldReturn 中的textField.superview?.viewWithTag(nextTag) 明确设置为self.tableView.viewWithTag(nextTag) 后,它起作用了。我在自定义单元格中添加了 1 个文本文件,并使用此自定义单元格创建了多行带有文本字段的行。
    【解决方案3】:

    其他答案没有任何问题,这只是一种不同的方法,其好处是更专注于 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)
        }
    
        ....
    }
    

    【讨论】:

      【解决方案4】:

      获得我认为有用的结果的另一种方法。我的应用程序有 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
      }
      

      【讨论】:

        【解决方案5】:

        另一种方法,如果您使用情节提要,您可以更改 Return Key 的文本字段属性。

        目前您有以下选项:Default (Return)GoGoogleJoinNextRouteSearchSendYahoo、@987654 Emergency Call, Continue

        【讨论】:

          猜你喜欢
          • 2013-05-24
          • 1970-01-01
          • 1970-01-01
          • 2021-08-05
          • 1970-01-01
          • 1970-01-01
          • 2021-06-02
          • 2020-09-25
          • 2021-08-22
          相关资源
          最近更新 更多