UITextView Right Alignment in swift - swift

i am trying text enter in textview right to left alignment but at that time textview starting position enter space not taking textview in swift
#IBOutlet weak var textview: UITextView!
#IBOutlet weak var sampleTF: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
textview.textAlignment = .right
textview.isScrollEnabled = true
textview.heightAnchor.constraint(equalToConstant: 52.0).isActive = true
textview.delegate = self
}
#IBAction func sampleButton(_ sender: Any) {
}
func textViewDidChange(_ textView: UITextView) {
let size = CGSize(width: textView.frame.width, height: 200)
let estimateSize = textView.sizeThatFits(size)
guard textView.contentSize.height < 100.0 else { textview.isScrollEnabled = true; return}
textview.isScrollEnabled = false
textview.constraints.forEach { (constriant) in
if constriant.firstAttribute == .height {
constriant.constant = estimateSize.height
}
}
}

As seen on other posts it has to do with how spaces are handled (). You can add the following delegate code to replace spaces with non breaking spaces :
func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText string: String) -> Bool {
if (textView == self.textView) {
let oldString = textView.text!
let newStart = oldString.index(oldString.startIndex, offsetBy: range.location)
let newEnd = oldString.index(oldString.startIndex, offsetBy: range.location + range.length)
let newString = oldString.replacingCharacters(in: newStart..<newEnd, with: string)
textView.text = newString.replacingOccurrences(of: " ", with: "\u{00a0}")
return false;
} else {
return true;
}
}

Related

UITextView dynamically change height based on content?

I have been stumped by this issue for a bit so I hoping to get some help. I have a SwiftUI application that is using UITextView but when I put content in it the text does not wrap. This SwiftUI view I am making both displays and edits text but it currently works for neither.
The text in the second box is much longer but the height does not change.
func makeUIView(context: Context) -> UITextView {
view.isScrollEnabled = false
view.isEditable = editable
view.isUserInteractionEnabled = true
view.contentInset = UIEdgeInsets(top: 0, left: 10, bottom: 0, right: 0)
view.text = self.text
// For debugging
view.layer.borderColor = UIColor.black.cgColor
view.layer.borderWidth = 1.0
var fontPref = UIFont.preferredFont(forTextStyle: render.font)
if render.bold && render.italic {
fontPref = fontPref.bolditalic()
} else if render.bold {
fontPref = fontPref.bold()
} else if render.italic {
fontPref = fontPref.italic()
}
view.font = fontPref
view.textColor = render.color
view.textAlignment = render.align
view.setContentCompressionResistancePriority(.defaultLow, for: .horizontal)
view.textContainer.lineBreakMode = .byWordWrapping
view.delegate = context.coordinator
return view
}
I have tried a few solutions but none of them are working for my use case.
Thanks!
here is a working custom textview that wraps the text and adjusts height:
import SwiftUI
struct NoteTextView: UIViewRepresentable {
func makeCoordinator() -> Coordinator {
Coordinator(self)
}
#Binding var text: String
var onEndEditing: () -> Void
typealias UIViewType = UITextView
var configuration = { (view: UIViewType) in }
func makeUIView(context: UIViewRepresentableContext<Self>) -> UIViewType {
let textField = UIViewType()
textField.delegate = context.coordinator
textField.font = UIFont.preferredFont(forTextStyle: .body)
textField.text = text
textField.isScrollEnabled = true
textField.isEditable = true
textField.isUserInteractionEnabled = true
return textField
}
func updateUIView(_ uiView: UIViewType, context: UIViewRepresentableContext<Self>) {
uiView.text = text
configuration(uiView)
}
class Coordinator : NSObject, UITextViewDelegate {
var parent: NoteTextView
init(_ uiTextView: NoteTextView) {
self.parent = uiTextView
}
func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
return true
}
func textViewDidChange(_ textView: UITextView) {
self.parent.text = textView.text
}
}
}

App displays text with different characters swift

I am making a chat app. When i send my messages or receive from other user they end up being displayed like this. What might be the issue? It works alright but sometimes it just changes the way the texts are displayed. Am not sure what am missing in this. Can anyone take a look at it. Kindly. Thanks in advance
Below is my code
class ChatController: UIViewController,UITextViewDelegate,UITableViewDataSource,UITableViewDelegate,UIGestureRecognizerDelegate{
#IBOutlet weak var txtViewBottomConstraints: NSLayoutConstraint!
#IBOutlet weak var viewTextViewContainer: ViewCustom!
#IBOutlet weak var txtViewContainerHeightConstraints: NSLayoutConstraint!
#IBOutlet weak var txtViewHeightConstraints: NSLayoutConstraint!
#IBOutlet var lblUserName: UILabel!
#IBOutlet var userImg: UIImageView!
#IBOutlet weak var txtView: IQTextView!
#IBOutlet weak var tblViewChat: UITableView!
#IBOutlet weak var bottomViewBottomConstraints: NSLayoutConstraint!
#IBOutlet weak var btnSend: UIButton!
var grpId = String()
var getMessageTimer: Timer!
var scrollEnable : Bool = false
var imagePicker : UIImagePickerController? = nil
var imageData : Data?
var groupName = String()
var groupImage = String()
var isFromNotification = Bool()
var strId = String()
var objChatVM = ChatViewModel()
var getMessageId = String()
var userImage:URL? = nil
var userName = String()
override func viewDidLoad() {
super.viewDidLoad()
popWithSwipe()
txtView.autocorrectionType = .no
lblUserName.text = userName
/* if userImage != nil
{
userImg.kf.setImage(with:userImage)
}
else
{
userImg.image = UIImage(named: "user")
}*/
userImg.kf.setImage(with:userImage, completionHandler: {
(image, error, cacheType, imageUrl) in
if image != nil{
self.userImg.image = image
}
else{
self.userImg.image = #imageLiteral(resourceName: "user")
}
})
IQKeyboardManager.shared.enable = false
IQKeyboardManager.shared.enableAutoToolbar = false
tblViewChat.dataSource = self
tblViewChat.delegate = self
tblViewChat.estimatedRowHeight = 70.0
tblViewChat.rowHeight = UITableViewAutomaticDimension
txtView.delegate = self
// txtView.textContainerInset = UIEdgeInsets(top: 0, left: 2, bottom: 0, right: 2)
let tapGestuer = UITapGestureRecognizer(target: self, action: #selector(handleTap(sender:)))
view.addGestureRecognizer(tapGestuer)
tapGestuer.delegate = self
// Do any additional setup after loading the view.
}
override func viewWillAppear(_ animated: Bool) {
if getMessageTimer != nil{
getMessageTimer.invalidate()
}
getMessageTimer = Timer.scheduledTimer(timeInterval: 2, target: self, selector: #selector(getMessageApi), userInfo: nil, repeats: true)
IQKeyboardManager.shared.enable = false
IQKeyboardManager.shared.enableAutoToolbar = false
NotificationCenter.default.addObserver(self, selector: #selector(self.keyboardWillShow), name: NSNotification.Name.UIKeyboardDidShow, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(self.keyboardWillHide), name: NSNotification.Name.UIKeyboardWillHide, object: nil)
}
override func viewWillDisappear(_ animated: Bool) {
getMessageTimer.invalidate()
NotificationCenter.default.removeObserver(self)
}
// MARK:- Get messages from server
#objc func getMessageApi(){
objChatVM.getMessage(param:strId) {status in
if status{
self.tblViewChat.reloadData()
if(self.objChatVM.getNumberOfMessage() != 0){
self.tblViewChat.scrollToRow(at: IndexPath(item: self.objChatVM.getNumberOfMessage()-1, section: 0), at: .bottom, animated: false)
}
}
}
}
#objc func handleTap(sender: UITapGestureRecognizer) {
txtView.resignFirstResponder()
}
// Enable IQKEYBoard manager here for handle keyboard at other controller which has disabled in viewdidload or viewwillappear
override func viewDidDisappear(_ animated: Bool) {
IQKeyboardManager.shared.enable = true
IQKeyboardManager.shared.enableAutoToolbar = true
}
// MARK:- Gesutrue Delegate Methods
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
return true
}
// Handle here tap on table view and inside cell for dismiss keyboard while tap outside on the screen
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {
if (touch.view is SenderTblCell || touch.view is ReceiverTblCell ) {
return false
}
if (touch.view?.superview is SenderTblCell || touch.view?.superview is ReceiverTblCell) {
return false
}
if (touch.view?.superview?.superview is SenderTblCell || touch.view?.superview?.superview is ReceiverTblCell) {
return false
}
if (touch.view?.superview?.superview?.superview is SenderTblCell || touch.view?.superview?.superview?.superview is ReceiverTblCell) {
return false
}
if(touch.view?.superview?.isDescendant(of: SenderTblCell().contentView))! || (touch.view?.superview?.isDescendant(of: ReceiverTblCell().contentView))!{
return false
}
return true // handle the touch
}
// MARK:- KeyBoard will show
#objc func keyboardWillShow(notification: NSNotification) {
if let keyboardSize = (notification.userInfo?[UIKeyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue {
var safeArea = 0
if UIDevice().userInterfaceIdiom == .phone || UIDevice().userInterfaceIdiom == .pad{
switch UIScreen.main.nativeBounds.height {
case 2436:
bottomViewBottomConstraints.constant = -keyboardSize.height + 30
self.view.layoutIfNeeded()
default:
if #available(iOS 11.0, *) {
let window = UIApplication.shared.keyWindow
safeArea = Int(window?.safeAreaInsets.bottom ?? 0.0)
}
bottomViewBottomConstraints.constant = -keyboardSize.height + CGFloat(safeArea) - 10
self.view.layoutIfNeeded()
}
}
}
}
// MARK:- KeyBoard will hide
#objc func keyboardWillHide(notification: NSNotification) {
bottomViewBottomConstraints.constant = -30
self.view.layoutIfNeeded()
}
#IBAction func btnSendAction(_ sender: Any) {
let param = ["userId":strId,"message":txtView.text!]
objChatVM.sendMessage(param: param) { (status) in
self.txtView.text = ""
self.textViewDidChange(self.txtView)
}
}
//MARK:- TextView Delegate Methods
func textViewDidChange(_ textView: UITextView) {
if textView.text == ""{
//textView.translatesAutoresizingMaskIntoConstraints = true
// txtViewHeightConstraints.constant = 100.0
// btnSend.setImage(#imageLiteral(resourceName: "attachment"), for: .normal)
}else{
// btnSend.setImage(#imageLiteral(resourceName: "sendMsg"), for: .normal)
}
var frame : CGRect = textView.bounds
frame.size.height = textView.contentSize.height
print(frame)
if(frame.height >= 100.0){
textView.isScrollEnabled = true
}
else{
textView.isScrollEnabled = false
txtView.frame.size = frame.size
}
if textView.text == ""{
txtViewContainerHeightConstraints.constant = 50.0
txtViewBottomConstraints.constant = 5.0
txtView.updateConstraints()
viewTextViewContainer.updateConstraintsIfNeeded()
viewTextViewContainer.updateConstraints()
viewTextViewContainer.layoutIfNeeded()
self.view.layoutIfNeeded()
}
}
func textViewDidEndEditing(_ textView: UITextView) {
}
func textViewShouldEndEditing(_ textView: UITextView) -> Bool {
return true
}
// MARK:- TableView DataSource and Delegate Methods
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return objChatVM.getNumberOfMessage()
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let messageUserId = objChatVM.getMessageUserFromId(index: indexPath.row)
print(UserViewModel.Shared().getUserId())
if(messageUserId == UserViewModel.Shared().getUserId()){
let cell = tblViewChat.dequeueReusableCell(withIdentifier: "senderCell") as! SenderTblCell
cell.lblMessage.text = objChatVM.getMessage(index: indexPath.row)
cell.lblDate.text = objChatVM.getDateTime(index: indexPath.row)
return cell
}
let cell = tblViewChat.dequeueReusableCell(withIdentifier: "receiverCell") as! ReceiverTblCell
cell.lblMessage.text = objChatVM.getMessage(index: indexPath.row)
cell.lblDate.text = objChatVM.getDateTime(index: indexPath.row)
cell.lblName.text = objChatVM.getFullNameOfUserFrom(index: indexPath.row)
let url = URL(string:objChatVM.getUserFromImage(index:indexPath.row))
cell.imgView.kf.indicatorType = .activity
cell.imgView.kf.setImage(with:url)
return cell
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return UITableViewAutomaticDimension
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
txtView.resignFirstResponder()
}
// MARK: Side Menu Button Action
#IBAction func btnSideMenuActn(_ sender: UIButton) {
self.pushViewControl(ViewControl:"SideMenuController")
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
This is how the message from my server in my logs looks like
message = "Am+good.+How+are+you+my+student%3F";
Use removingPercentEncoding and some of the text such as + doesn't seem to generated by urlEncoding if they are created by code then use both in combination:
message = "Am+good.+How+are+you+my+student%3F"
let decodedMessage = message.removingPercentEncoding?.replacingOccurrences(of: "+", with: " ")
print(decodedMessage)

TextView capitalizes first two characters instead of one

So I've added placeholder functionality to UITextView, and now it capitalizes the first two characters of input text instead of one as it's supposed to.
I would appreciate you guys' help in solving this problem if you can help in any way. I've been trying, but I seem to be getting nowhere.
Here's the code:
class ViewController: UIViewController {
#IBOutlet weak var textView: UITextView!
override func viewDidLoad() {
super.viewDidLoad()
textView.delegate = self
textView.text = "Type something"
textView.textColor = UIColor.rgb(197, 197, 199)
}
}
extension ViewController: UITextViewDelegate {
// Put cursor at the beginning of TextView when editing begins
func textViewDidBeginEditing(_ textView: UITextView) {
textView.selectedTextRange = textView.textRange(from: textView.beginningOfDocument, to: textView.beginningOfDocument)
}
func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
// Sets placeholder for UITextView
let updatedText = (textView.text as NSString).replacingCharacters(in: range, with: text)
if updatedText.isEmpty {
textView.text = "Type something"
textView.textColor = UIColor.rgb(197, 197, 199)
textView.selectedTextRange = textView.textRange(from: textView.beginningOfDocument, to: textView.beginningOfDocument)
} else if textView.textColor == UIColor.rgb(197, 197, 199) && !text.isEmpty {
textView.textColor = UIColor.black
textView.text = text
} else {
return true
}
return false
}
// Prevents selecting the placeholder
func textViewDidChangeSelection(_ textView: UITextView) {
if self.view.window != nil {
if textView.textColor == UIColor.rgb(197, 197, 199) {
textView.selectedTextRange = textView.textRange(from: textView.beginningOfDocument, to: textView.beginningOfDocument)
}
}
}
}
Thank you!
The code below solves capitalization of the first 2 letters in text view.
func textViewDidChange(_ textView: UITextView) {
if textView.text.isEmpty {
textView.text = "Type something"
textView.textColor = self.placeholderTextColor
} else {
textView.textColor = .black
}
}
func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
if text.isEmpty {
let updatedText = (textView.text as NSString).replacingCharacters(in: range, with: text)
if updatedText.isEmpty {
textView.text = "Type something"
textView.textColor = self.placeholderTextColor
textView.selectedRange = NSRange(location: 0, length: 0)
}
} else {
if textView.text == "Type something" {
textView.text = ""
}
textView.textColor = .black
}
return true
}
The reason why the text view capitalized first 2 characters in text view is that you've returned false in below delegate method
func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool

Custom UITextfield with discontinuous bottom borders

I am now working on a UITextfield. I hope to know how to add four discontinuous bottom border to a UITextfield, and how to make the space between input digits larger to make them fit exactly on the four lines respectively. Moreover, if possible, how to make the line become black (while other lines remain grey) when users are inputing digit on that line? Thank you so much!
Use following subclass of UITextField and create textfield for each digit either in storyboard or programatically.
Note that each textfield has to set a tag, such as
1st Digit: textField1.tag=1
2nd Digit: textField1.tag=2
3rd Digit: textField1.tag=3
4th Digit: textField1.tag=4
class CustomTextField: UITextField {
private let normalStateColor = UIColor.lightGray.cgColor
private let focusStateColor = UIColor.black.cgColor
private let border = CALayer()
private let borderHeight: CGFloat = 4.0
// MARK:- Init
required init?(coder aDecoder: NSCoder) {
super.init(coder:aDecoder)
setup()
}
override init(frame:CGRect) {
super.init(frame:frame)
setup()
}
override func awakeFromNib() {
super.awakeFromNib()
setup()
}
// MARK:- Overrides
override func layoutSubviews() {
super.layoutSubviews()
let size = self.frame.size
self.border.frame = CGRect(x: 0, y: size.height - borderHeight, width: size.width, height: borderHeight)
}
override func willMove(toSuperview newSuperview: UIView!) {
guard newSuperview != nil else {
NotificationCenter.default.removeObserver(self)
return
}
NotificationCenter.default.addObserver(self, selector: #selector(beginEdit),
name: UITextField.textDidBeginEditingNotification, object: self)
NotificationCenter.default.addObserver(self, selector: #selector(endEdit),
name: UITextField.textDidEndEditingNotification, object: self)
}
#objc func beginEdit() {
border.backgroundColor = self.focusStateColor
}
#objc func endEdit() {
border.backgroundColor = self.normalStateColor
}
private func setup() {
border.backgroundColor = self.normalStateColor
textAlignment = .center
borderStyle = .none
layer.addSublayer(border)
delegate = self
}
}
extension CustomTextField: UITextFieldDelegate {
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
if textField.text!.count < 1 && string.count > 0 {
textField.text = string
textField.superview?.viewWithTag(textField.tag + 1)?.becomeFirstResponder()
return false
} else if textField.text!.count >= 1 && string.count == 0 {
textField.text = ""
textField.superview?.viewWithTag(textField.tag - 1)?.becomeFirstResponder()
return false
}
return true
}
}
That yields
check this..
ViewController.swift
import UIKit
class ViewController: UIViewController, UITextFieldDelegate {
#IBOutlet weak var txtOne: UITextField!
#IBOutlet weak var txtTwo: UITextField!
#IBOutlet weak var txtThree: UITextField!
#IBOutlet weak var txtFour: UITextField!
#IBOutlet weak var vwFour: UIView!
#IBOutlet weak var vwThree: UIView!
#IBOutlet weak var vwTwo: UIView!
#IBOutlet weak var vwOne: UIView!
func textFieldDidBeginEditing(_ textField: UITextField) {
if textField == txtOne {
vwOne.backgroundColor = .black
vwTwo.backgroundColor = .lightGray
vwThree.backgroundColor = .lightGray
vwFour.backgroundColor = .lightGray
} else if textField == txtTwo {
vwTwo.backgroundColor = .black
vwOne.backgroundColor = .lightGray
vwThree.backgroundColor = .lightGray
vwFour.backgroundColor = .lightGray
} else if textField == txtThree {
vwThree.backgroundColor = .black
vwTwo.backgroundColor = .lightGray
vwOne.backgroundColor = .lightGray
vwFour.backgroundColor = .lightGray
} else {
vwFour.backgroundColor = .black
vwTwo.backgroundColor = .lightGray
vwThree.backgroundColor = .lightGray
vwOne.backgroundColor = .lightGray
}
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
}

Swift - Automatically jump to next text field when not empty

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()
}
}
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()
}
}
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()
}
}
}
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()
}
}