George Host
George Host

Reputation: 988

BecomeFirstResponder and Multiple UITextField Focus

So I have a view -> UITextField & UILabel are the two children, I have multiple views that contain the children. I am using the delegate function below and I assign each of the UITextfields as delegates "textFieldShouldReturn(_ textField: UITextField) -> Bool". However, they do not seem to change the textField Focus when I press return. When use this focus technique with UITextFields without having them nested in the view. It allows me to change focus without issue. Why does nesting a UITextField, in a view cause the inability to have the next UITextField become the first responder ? I have read a few things about the first Responder and how it works, but it doesn't clearly explain how to work around this issue.

class ScrollingViewWithFields:UIViewController, UITextFieldDelegate {

let scrollView = UIScrollView()
let contentView = UIView()

var textFields:[UITextField] = []
var labeledTextField:[LabeledTextField] = []

override func viewDidLoad() {
    contentView.backgroundColor = UIColor.white
    contentView.translatesAutoresizingMaskIntoConstraints = false

    view.addSubview(scrollView)
    scrollView.addSubview(contentView)

    scrollView.translatesAutoresizingMaskIntoConstraints = false
    scrollView.backgroundColor = UIColor.white
    let top = view.safeAreaLayoutGuide.topAnchor
    let bottom = view.safeAreaLayoutGuide.bottomAnchor

    NSLayoutConstraint.activate([
        scrollView.topAnchor.constraint(equalTo: top),
        scrollView.bottomAnchor.constraint(equalTo: bottom),
        scrollView.leftAnchor.constraint(equalTo: view.leftAnchor),
        scrollView.rightAnchor.constraint(equalTo: view.rightAnchor)
    ])



    let ltf = LabeledTextField()
    ltf.translatesAutoresizingMaskIntoConstraints = false

    contentView.addSubview(ltf)
    ltf.populate(title: "Hello", font: UIFont.systemFont(ofSize: 14.0))
    ltf.textField.delegate = self
    ltf.textField.tag = 0
    let ltf2 = LabeledTextField()
    ltf2.translatesAutoresizingMaskIntoConstraints = false
    contentView.addSubview(ltf2)
    ltf2.populate(title: "What", font: UIFont.systemFont(ofSize: 14.0))
    ltf2.textField.tag = 1
    ltf2.textField.delegate = self



    self.textFields.append(ltf2.textField)
    self.textFields.append(ltf.textField)

    NSLayoutConstraint.activate([
        ltf.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 8.0),
        ltf.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 8.0),
        ltf.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: 8.0),
        ltf.heightAnchor.constraint(equalToConstant: 60)
    ])

    NSLayoutConstraint.activate([
        ltf2.topAnchor.constraint(equalTo: ltf.bottomAnchor, constant: 8.0),
        ltf2.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 8.0),
        ltf2.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: 8.0),
        ltf2.heightAnchor.constraint(equalToConstant: 60)
        ])

    NSLayoutConstraint.activate([
        contentView.topAnchor.constraint(equalTo: scrollView.topAnchor),
        contentView.widthAnchor.constraint(equalTo: scrollView.widthAnchor),
        contentView.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor),
        contentView.heightAnchor.constraint(equalToConstant:4000)
    ])

}

func textFieldShouldReturn(_ textField: UITextField) -> Bool {

    let tag = textField.tag
    let next = tag + 1

    if next < self.textFields.count {
        let textField = self.textFields[next]

        textField.becomeFirstResponder()
        self.scrollView.contentOffset = CGPoint(x: 0.0, y: textField.frame.origin.y - 8.0)

    } else {
        textField.resignFirstResponder()
    }

    return true
}

}

Upvotes: 0

Views: 1191

Answers (2)

Shehata Gamal
Shehata Gamal

Reputation: 100503

You can reserve memory instead of declaring an array to hod the textfeilds and try something like this

func textFieldShouldReturn(_ textField: UITextField) -> Bool
{
    if let next =  self.view.viewWithTag(textField.tag + 1) as? UITextField
    {            
        next.becomeFirstResponder()
    }         
    else
    {
        textField.resignFirstResponder()
    }
    return true

}

Upvotes: 0

Craig Siemens
Craig Siemens

Reputation: 13276

The issue is the way you're setting the tags on the textfield and putting them in the array.

ltf.textField.tag = 0
ltf2.textField.tag = 1

self.textFields.append(ltf2.textField)
self.textFields.append(ltf.textField)

The issue that the tags don't match the order in the array, since the array will end up being [ltf2.textField, ltf.textField]. I would totally skip using tags and just use the order in the array.

func textFieldShouldReturn(_ textField: UITextField) -> Bool {
    if let index = textFields.index(of: textField) {
        let nextIndex = index + 1
        let lastIndex = textFields.count - 1

        if nextIndex <= lastIndex {
            textFields[nextIndex].becomeFirstResponder()
        }
    }
    return true
}

Upvotes: 1

Related Questions