Reputation: 205
I am working on a game in which the user has to type out the past tense of a verb. My view contains small textfield boxes that only accept one character. As of now I am trying to automatically jump to the next textfield when the former contains a letter.
I want to keep doing this until all the boxes are filled. The user should also be able to go back one box using the return button on the keyboard.
Below is the code I am currently using, but it is not jumping to the next textfield. What am I doing wrong?
var game: Game? {
didSet {
if var answerContent = game?.answer {
let views = (0..<answerContent.characters.count).map { _ in UITextField(frame: CGRect(x: 0, y: 0, width: 40, height: 40)) }
for textField in views {
textField.backgroundColor = UIColor.white
textField.textColor = Constants.MAIN_THEME_COLOR
textField.textAlignment = NSTextAlignment.center
textField.delegate = self
textField.returnKeyType = .next
textField.tag = views.index(of: textField)! + 1
self.container.addArrangedSubview(textField)
views.first?.becomeFirstResponder()
}
}
}
}
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
guard let text = textField.text else { return true }
let textLength = text.characters.count + string.characters.count - range.length
return textLength <= 1
}
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
if let nextField = textField.superview?.viewWithTag(textField.tag + 1) as? UITextField {
print("Test")
nextField.becomeFirstResponder()
} else {
print("Test2")
textField.resignFirstResponder()
}
return false
}
Updated code (24-04-2017) - Returns nil when trying to jump to the next textField
var game: Game? {
didSet {
if var answerContent = game?.answer {
let views = (0..<answerContent.characters.count).map { _ in UITextField(frame: CGRect(x: 0, y: 0, width: 40, height: 40)) }
for textField in views {
textField.backgroundColor = UIColor.white
textField.textColor = Constants.MAIN_THEME_COLOR
textField.textAlignment = NSTextAlignment.center
textField.delegate = self
textField.addTarget(self, action: #selector(textChanged), for: .editingChanged)
textField.tag = views.index(of: textField)! + 1
self.container.addArrangedSubview(textField)
}
views.first?.becomeFirstResponder()
}
}
}
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
guard let text = textField.text else { return true }
let textLength = text.characters.count + string.characters.count - range.length
return textLength <= 1
}
func textChanged(sender: UITextField) {
if (sender.text?.characters.count)! > 0 {
print("Entered")
let nextField = textField?.superview?.viewWithTag(textField.tag + 1) as UIResponder!
if (nextField != nil) {
nextField?.becomeFirstResponder()
} else {
print("Error: nil found")
}
} else {
print("Removed")
textField?.resignFirstResponder()
}
}
Answer:
var index: NSInteger = 0
for textField in views {
textField.backgroundColor = UIColor.white
textField.textColor = Constants.MAIN_THEME_COLOR
textField.textAlignment = NSTextAlignment.center
textField.autocapitalizationType = UITextAutocapitalizationType.none
textField.delegate = self
textField.addTarget(self, action: #selector(textChanged), for: .editingChanged)
textField.tag = index
self.container.addArrangedSubview(textField)
index+=1
}
func textChanged(sender: UITextField) {
if (sender.text?.characters.count)! > 0 {
let nextField = sender.superview?.viewWithTag(sender.tag + 1) as UIResponder!
nextField?.becomeFirstResponder()
} else {
sender.resignFirstResponder()
}
}
Upvotes: 3
Views: 3859
Reputation: 1447
Update for Swift 4
In viewDidLoad()
for each text field,
textField.addTarget(self, action: #selector(textChanged), for: .editingChanged)
Then, add this function,
@objc func textChanged(sender: UITextField) {
if (sender.text?.count)! > 0 {
let nextField = self.view.viewWithTag(sender.tag + 1) as? UITextField
nextField?.becomeFirstResponder()
}
}
Upvotes: 1
Reputation: 382
Follow the code:
import UIKit
// used this to set max characters of UITextField in the storyboard
private var __maxLengths = [UITextField: Int]()
extension UITextField {
@IBInspectable var maxLength: Int {
get {
guard let l = __maxLengths[self] else {
return 150 // (global default-limit. or just, Int.max)
}
return l
}
set {
__maxLengths[self] = newValue
addTarget(self, action: #selector(fix), for: .editingChanged)
}
}
func fix(textField: UITextField) {
let t = textField.text
textField.text = t?.safelyLimitedTo(length: maxLength)
}
}
extension String
{
func safelyLimitedTo(length n: Int)->String {
let c = self.characters
if (c.count <= n) { return self }
return String( Array(c).prefix(upTo: n) )
}
}
class ViewController: UIViewController {
@IBOutlet var input1: UITextField!
@IBOutlet var input2: UITextField!
@IBOutlet var input3: UITextField!
@IBOutlet var input4: UITextField!
@IBOutlet var input5: UITextField!
@IBOutlet var input6: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
setup()
// Do any additional setup after loading the view.
}
func setup() {
input1.tag = 1
input2.tag = 2
input3.tag = 3
input4.tag = 4
input5.tag = 5
input6.tag = 6
input1.addTarget(self, action: #selector(textChanged), for: .editingChanged)
input2.addTarget(self, action: #selector(textChanged), for: .editingChanged)
input3.addTarget(self, action: #selector(textChanged), for: .editingChanged)
input4.addTarget(self, action: #selector(textChanged), for: .editingChanged)
input5.addTarget(self, action: #selector(textChanged), for: .editingChanged)
input6.addTarget(self, action: #selector(textChanged), for: .editingChanged)
}
func textChanged(sender: UITextField) {
if (sender.text?.characters.count)! == 1 {
let nextField = sender.superview?.viewWithTag(sender.tag + 1) as UIResponder!
nextField?.becomeFirstResponder()
} else if (sender.text?.characters.count)! == 0 {
let nextField = sender.superview?.viewWithTag(sender.tag - 1) as UIResponder!
nextField?.becomeFirstResponder()
}
}
}
Upvotes: 2
Reputation: 271040
I suggest that you should add a target to the control event .valueChanged
:
// for each text field
textField.addTarget(self, action: #selector(textChanged), for: .valueChanged)
Implement textChanged
as follows:
func textChanged(sender: UITextField) {
if sender.text.characters.length > 0 {
let nextField = textField.superview?.viewWithTag(textField.tag + 1) as? UITextField
nextField?.becomeFistResponder()
}
}
Upvotes: 4