Swift- Text field formatting update while editing - uitableview

I have a function for formatting my text field as a phone number, but this function is only working after I save my managed object context. For example, I have a UITableView with static cells for text fields as a contact form. When I'm creating a new contact (before I've saved the contact) the text field for phone number doesn't get formatted by my function, but after I save that contact and reopen it, then go and enter a phone number it gets formatted properly. I'm trying to figure out why this is, and what I am do about it so that the number gets formatted in either case. Here's the function that I'm using to format the phone number.
func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
if textField == phoneTxt {
var newString = (textField.text as NSString).stringByReplacingCharactersInRange(range, withString: string)
var components = newString.componentsSeparatedByCharactersInSet(NSCharacterSet.decimalDigitCharacterSet().invertedSet)
var decimalString = "".join(components) as NSString
var length = decimalString.length
var hasLeadingOne = length > 0 && decimalString.characterAtIndex(0) == (1 as unichar)
if length == 0 || (length > 10 && !hasLeadingOne) || length > 11 {
var newLength = (textField.text as NSString).length + (string as NSString).length - range.length as Int
return (newLength > 10) ? false : true
}
var index = 0 as Int
var formattedString = NSMutableString()
if hasLeadingOne {
formattedString.appendString("1 ")
index += 1
}
if (length - index) > 3 {
var areaCode = decimalString.substringWithRange(NSMakeRange(index, 3))
formattedString.appendFormat("(%#) ", areaCode)
index += 3
}
if length - index > 3 {
var prefix = decimalString.substringWithRange(NSMakeRange(index, 3))
formattedString.appendFormat("%#-", prefix)
index += 3
}
var remainder = decimalString.substringFromIndex(index)
formattedString.appendString(remainder)
textField.text = formattedString
return false
} else {
return true
}
}
`

It seems when the form is used to create a new object, you somehow have not set the delegate of the text field. Check if the posted function is being called at all in this case.

Related

Cannot delete all characters from a UITextField

In my application, there is a possibility to add transactions. The transaction has an attribute called amount and this attribute is a Double. I have implemented the function to add a negative and a positive amount. I do this with a UISegmentedControll. If the user makes the amount negative, the amountTextField.textgets to `"-" + amountTextField.text. That the user can input just Doubles I added this function :
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
guard let oldText = textField.text, let r = Range(range, in: oldText) else{
return true
}
let newText = oldText.replacingCharacters(in: r, with: string)
let isNumeric = newText.isEmpty || (Double(newText) != nil)
let numberOfDots = newText.components(separatedBy: ".").count - 1
let numberOfDecimalDigits: Int
if let dotIndex = newText.index(of: "."){
numberOfDecimalDigits = newText.distance(from: dotIndex, to: newText.endIndex) - 1
} else {
numberOfDecimalDigits = 0
}
return isNumeric && numberOfDots <= 1 && numberOfDecimalDigits <= 2
}
When there is a minus in front of the positive Double, it is impossible to delete the first number of the string. For example if the amountTextField.text is -399.99, and the user presses the delete button as often as he wants, the textField will show -3. In my debugging work I found out that the function I added to the code is the reason for this.
How can I fix this issue?
Here is one approach:
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
guard let oldText = textField.text, let r = Range(range, in: oldText) else {
return true
}
let newText = oldText.replacingCharacters(in: r, with: string)
if newText == "-" {
// result will be "-" so just
return true
}
let isNumeric = newText.isEmpty || (Double(newText) != nil)
let numberOfDots = newText.components(separatedBy: ".").count - 1
let numberOfDecimalDigits: Int
if let dotIndex = newText.index(of: "."){
numberOfDecimalDigits = newText.distance(from: dotIndex, to: newText.endIndex) - 1
} else {
numberOfDecimalDigits = 0
}
if isNumeric && numberOfDots <= 1 && numberOfDecimalDigits <= 2 {
// value passes those tests, so make sure the leading "-" is still there
// if not, prepend it, set the text and return false
if newText.first != "-" {
textField.text = "-" + newText
return false
}
}
return isNumeric && numberOfDots <= 1 && numberOfDecimalDigits <= 2
}
We have a couple of additional if blocks to handle:
user moves the insertion point to delete the "-"
user does a "select all" and taps a new digit or delete, or pastes a value
I expect you already know you'll also want a bool check to handle this differently if the segmented control is not in the negative position.

Swift - unable custom cursor position from user in UITextField

I have a textfield and I do not want users to be able to set the cursor position by touching and holding at the textfield. Is there a way to always have the cursor to be at the very end of the textfield?
First of all, its not good to change this type of default behavior of textField.
As per your comment if you want to add credit card and you are facing some input complexity then I found code from my old project and might be it will work for you.
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
let replacementStringIsLegal = string.rangeOfCharacter(from: NSCharacterSet(charactersIn: "0123456789").inverted) == nil
if !replacementStringIsLegal {
return false
}
let newString = (textField.text! as NSString).replacingCharacters(in: range, with: string)
let components = newString.components(separatedBy: NSCharacterSet(charactersIn: "0123456789").inverted)
let decimalString = components.joined(separator: "") as NSString
let length = decimalString.length
let hasLeadingOne = length > 0 && decimalString.character(at: 0) == (1 as unichar)
if length == 0 || (length > 16 && !hasLeadingOne) || length > 19
{
let newLength = (textField.text! as NSString).length + (string as NSString).length - range.length as Int
return (newLength > 16) ? false : true
}
var index = 0 as Int
let formattedString = NSMutableString()
if hasLeadingOne
{
formattedString.append("1 ")
index += 1
}
if length - index > 4
{
let prefix = decimalString.substring(with: NSMakeRange(index, 4))
formattedString.appendFormat("%# ", prefix)
index += 4
}
if length - index > 4
{
let prefix = decimalString.substring(with: NSMakeRange(index, 4))
formattedString.appendFormat("%# ", prefix)
index += 4
}
if length - index > 4
{
let prefix = decimalString.substring(with: NSMakeRange(index, 4))
formattedString.appendFormat("%# ", prefix)
index += 4
}
let remainder = decimalString.substring(from: index)
formattedString.append(remainder)
textField.text = formattedString as String
return false
}

Formating phone number does not work fine

I am trying to format the User input phone number. I have implemented this:
import UIKit
class ViewController: UIViewController {
var phoneNumberTextField : UITextField?
var docController: UIDocumentInteractionController?
override func viewDidLoad() {
super.viewDidLoad()
phoneNumberTextField = UITextField(frame: CGRectMake(10, 150, 100, 50))
phoneNumberTextField?.backgroundColor = UIColor.brownColor()
self.view.addSubview(phoneNumberTextField!)
}
func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool
{
if (textField == phoneNumberTextField)
{
let newString = (textField.text! as NSString).stringByReplacingCharactersInRange(range, withString: string)
let components = newString.componentsSeparatedByCharactersInSet(NSCharacterSet.decimalDigitCharacterSet().invertedSet)
let decimalString = components.joinWithSeparator("") as NSString
let length = decimalString.length
let hasLeadingOne = length > 0 && decimalString.characterAtIndex(0) == (1 as unichar)
if length == 0 || (length > 10 && !hasLeadingOne) || length > 11
{
let newLength = (textField.text! as NSString).length + (string as NSString).length - range.length as Int
return (newLength > 10) ? false : true
}
var index = 0 as Int
let formattedString = NSMutableString()
if hasLeadingOne
{
formattedString.appendString("1 ")
index += 1
}
if (length - index) > 3
{
let areaCode = decimalString.substringWithRange(NSMakeRange(index, 3))
formattedString.appendFormat("(%#)", areaCode)
index += 3
}
if length - index > 3
{
let prefix = decimalString.substringWithRange(NSMakeRange(index, 3))
formattedString.appendFormat("%#-", prefix)
index += 3
}
let remainder = decimalString.substringFromIndex(index)
formattedString.appendString(remainder)
textField.text = formattedString as String
return false
}
else
{
return true
}
}
}
From Here. When I run the app and I start adding numbers nothing happens. Brackets and hypens do not appear. I want to convert (197)-444-4444 to 1974444444.
What is wrong here?
You dont seems to have set delegate for your textfield:
phoneNumberTextField.delegate = self
And your viewcontroller doesnt seems to conform the UITextFieldDelegate like:
class ViewController: UIViewController, UITextFieldDelegate {
Also, it would never be nil so just change it to var phoneNumberTextField : UITextField!

Swift UITextField Decimal Field Type - No Pasting, 1 Decimal Point Only and Length Limit

I am trying to do some error checking for an text field which will only accept a decimal value. This means that the text field cannot be pasted into, the user can only enter 1 decimal point and my personal preference I want a length limit of 2 characters after the decimal place.
I have got the current code which simply restricts the paste:
//Does not allow pasting into text field
//https://stackoverflow.com/questions/26919854/how-can-i-declare-that-a-text-field-can-only-contain-an-integer
func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
let invalidCharacters = NSCharacterSet(charactersInString: "0123456789.").invertedSet
return string.rangeOfCharacterFromSet(invalidCharacters, options: [], range: string.startIndex ..< string.endIndex) == nil
}
It works, but I want other features as well. How can I implement these?
To check whether It has a decimal point and limit 2 decimal place you can use like this :-
let str = "456.23" // Your Label Value
let nsStr = NSString(string: str) // Conver label value to NSString
let strSplit = str.characters.split(".") // split label before decimal point and after decimal point
if str.containsString(".") {
//Contains Decimal value
if String(strSplit.last!).characters.count <= 2 {
// Contains 2 or 1 decimal values
} else {
// More than 2 decimal values
}
} else {
//Do not contains Decimal value
}
you can do something like,
func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
var dotLocation = Int()
let nonNumberSet = NSMutableCharacterSet() //create an empty mutable set
nonNumberSet.formUnionWithCharacterSet(NSCharacterSet.URLQueryAllowedCharacterSet())
nonNumberSet.addCharactersInString("0123456789.")
//allow backspace
if range.length == 0 && string.characters.count == 0
{
return true
}
if string == "."
{
if range.location == 0{
return false
}
if dotLocation == 0 {
dotLocation == range.location
return true
}
else{
return false
}
}
if range.location == dotLocation && string.characters.count == 0{
dotLocation = 0
}
var newStr = (textField.text! as NSString).stringByReplacingCharactersInRange(range, withString: string)
newStr = (newStr.componentsSeparatedByCharactersInSet(nonNumberSet) as NSArray).componentsJoinedByString("")
textField.text = newStr
return false
}
Hope this will help :)

How to Format Textfiled.text in Turkish Phone Format?

I've a textfield which only takes phone number and I'm trying to format it into Turkish format number which looks like this (555) 555 5555. How can I make that in Swift?
You can do something like this on EiditingChange method, this will change first three entries to this () format while user inputs text, you can follow same approach to remove format if user is deleting entries
#IBAction func onEditingChanged(sender: UITextField!)
{
var string = sender.text as NSString
if string.length > 3
{
let range = string.rangeOfString("(")
if range.location == NSNotFound
{
var firstPart = string.substringToIndex(3)
var secondPart = string.substringFromIndex(3)
var string = "(\(firstPart))\(secondPart)"
sender.text = string
}
}
}
Well if we take the assumption that all Turkish numbers are 10 digits long, we can do it as follows.
First lets define a helper function to get the substrings:
func sub(str: String, start: Int, end: Int) -> String {
return str.substringWithRange(Range<String.Index>(start: advance(str.startIndex, start), end: advance(str.startIndex, end)))
}
Now we just apply the function to get the sections of the number:
// Lets say this is the number we get from the textfield
let number = "1234567890"
let start = sub(number, 0, 3) // "123"
let mid = sub(number, 3, 6) // "456"
let end = sub(number, 6, 10) // "7890"
And then we format this into a single string as desired.
let formatNumber = "(\(start)) \(mid) \(end)" // "(123) 456 7890"
Note that this would only work for numbers that have 10 digits (I doubt that all Turkish numbers are). You would need to modify this to format for numbers of different lengths, by specifying different substrings of the start mid and end above.
If you wanted to limit the user to only using 10 digit numbers, you should perform validation on textfield.
var shouldAttemptFormat: Bool = true
func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
if textField == self.phoneNumberTextField{
let resultString: String = (textField.text as NSString).stringByReplacingCharactersInRange(range, withString:string)
let oldString: String = self.phoneNumberTextField.text
let oldCount = count(oldString)
let newCount = count(resultString)
shouldAttemptFormat = newCount > oldCount
return true//newCount < 15
}else{
return true
}
// otherwise we should just let them continue
}
// MARK: - phone number formatting
func formatPhoneNumber() {
// this value is determined when textField shouldChangeCharactersInRange is called on a phone
// number cell - if a user is deleting characters we don't want to try to format it, otherwise
// using the current logic below certain deletions will have no effect
if !shouldAttemptFormat {
return
}
// here we are leveraging some of the objective-c NSString functions to help parse and modify
// the phone number... first we strip anything that's not a number from the textfield, and then
// depending on the current value we append formatting characters to make it pretty
let currentValue: NSString = self.phoneNumberTextField.text
let strippedValue: NSString = currentValue.stringByReplacingOccurrencesOfString("[^0-9]", withString: "", options: .RegularExpressionSearch, range: NSMakeRange(0, currentValue.length))
var formattedString: NSString = ""
if strippedValue.length == 0 {
formattedString = "";
}
else if strippedValue.length < 3 {
formattedString = "(" + (strippedValue as String)
}
else if strippedValue.length == 3 {
formattedString = "(" + (strippedValue as String) + ") "
}
else if strippedValue.length < 6 {
formattedString = "(" + strippedValue.substringToIndex(3) + ") " + strippedValue.substringFromIndex(3)
}
else if strippedValue.length == 6 {
formattedString = "(" + strippedValue.substringToIndex(3) + ") " + strippedValue.substringFromIndex(3) + "-"
}
else if strippedValue.length <= 10 {
formattedString = "(" + strippedValue.substringToIndex(3) + ") " + strippedValue.substringWithRange(NSMakeRange(3, 3)) + "-" + strippedValue.substringFromIndex(6)
}
else if strippedValue.length >= 11 {
formattedString = "(" + strippedValue.substringToIndex(3) + ") " + strippedValue.substringWithRange(NSMakeRange(3, 3)) + "-" + strippedValue.substringWithRange(NSMakeRange(6, 4))
}
self.phoneNumberTextField.text = formattedString as String
}
I use this code as it looks above, It works. When user type any character, formatPhoneNumber function works, for each char.
self.phoneNumberTextField.addTarget(self, action: "formatPhoneNumber", forControlEvents: .EditingChanged)
You must to add this line in viewDidLoad.
Hopefully, it will work for you

Resources