问题
How can I limit a UITextField to one decimal point with Swift? The text field is for entering a price, so I cannot allow more than one decimal point. If I was using Objective-C, I would have used this code:
-(BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
NSString *newString = [textField.text stringByReplacingCharactersInRange:range withString:string];
NSArray *sep = [newString componentsSeparatedByString:@"."];
if([sep count] >= 2)
{
NSString *sepStr=[NSString stringWithFormat:@"%@",[sep objectAtIndex:1]];
return !([sepStr length]>1);
}
return YES;
}
But due a difference with how Swift uses ranges, I cannot convert this code to Swift. The first line gives me an error saying NSRange is not convertible to Range<String.Index>
EDIT: I ended up doing it like this before I saw the answer:
func textField(textField: UITextField!, shouldChangeCharactersInRange range: NSRange, replacementString string: String!) -> Bool {
let tempRange = textField.text.rangeOfString(".", options: NSStringCompareOptions.LiteralSearch, range: nil, locale: nil)
if tempRange?.isEmpty == false && string == "." {
return false
}
return true
}
I found this on a different post. This solution works fine but I'm not sure if it is the best way to do it but it is a short and clean way.
回答1:
Alexey got there before me, but seeing as I'd implemented a Swift version to check things out, here it is:
func textField(textField: UITextField!, shouldChangeCharactersInRange range: NSRange, replacementString string: String!) -> Bool {
// I'm explicitly unwrapping newString here, as I want to use reverse() on it, and that
// apparently doesn't work with implicitly unwrapped Strings.
if let newString = (textField.text as NSString).stringByReplacingCharactersInRange(range, withString: string) {
var decimalFound = false
var charactersAfterDecimal = 0
for ch in reverse(newString) {
if ch == "." {
decimalFound = true
break
}
charactersAfterDecimal++
}
if decimalFound && charactersAfterDecimal > 1 {
return false
}
}
return true;
}
Note that the first line now explicitly casts the textField.text to an NSString. This is because the String version of stringByReplacingCharactersInRange takes a Swift range, not an NSRange like the one that's passed in.
I've also made the later code a lot more Swiftian, removing NSString operations. (I'm not convinced that this is wonderfully efficient, as reverse(String) might actually reverse the whole String rather than providing a simple backward iterator, but it looks like the most Swifty way of doing it.)
回答2:
Your first line in Swift will look like:
var newString = NSString(string: textField.text).stringByReplacingCharactersInRange(range, withString: string)
回答3:
Swift 4 & Xcode 9.2
This solution allows only one decimal point to be input, therefore allowing a valid double.
// Only allows one decimal point
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
let newString = (textField.text! as NSString).replacingCharacters(in: range, with: string)
let arrayOfString = newString.components(separatedBy: ".")
if arrayOfString.count > 2 {
return false
}
return true
}
回答4:
I suggest using NSNumberFormatter's decimalSeparator property to split your String. Here's how to limit your input to one decimal place only using the NSNumberFormatter:
func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
if string.isEmpty { // back key
return true
}
if let input = textField.text {
let numberFormatter = NSNumberFormatter()
let range = input.rangeOfString(numberFormatter.decimalSeparator)
if let r = range {
let endIndex = input.startIndex.advancedBy(input.startIndex.distanceTo(r.endIndex))
let decimals = input.substringFromIndex(endIndex)
return decimals.characters.count < 1
}
}
return true
}
回答5:
Try this for one decimal entry:-
func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
if(string == "." ){
let countdots = textField.text!.componentsSeparatedByString(".").count - 1
if countdots > 0 && string == "."
{
return false
}
}
return true
}
回答6:
I hope it might help you:
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
let newString = (textField.text! as NSString).replacingCharacters(in: range, with: string)
let stringArr: [String] = newString.components(separatedBy: ".")
if stringArr.count < 2 || (stringArr.count == 2 && stringArr[1].count == 1){
return true
}
return false
}
回答7:
Swift 4, Xcode 9.2
This will remove the last entered decimal point:
let numberOfDecimalPoints = textField.text.components(separatedBy: ".").count - 1
if numberOfDecimalPoints > 1 {
textField.deleteBackward()
}
回答8:
func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
if (textField.text?.componentsSeparatedByString(".").count > 1 && string == ".")
{
return false
}
return string == "" || (string == "." || Float(string) != nil)
}
来源:https://stackoverflow.com/questions/25225904/limit-uitextfield-to-one-decimal-point-swift