Bronze Challenge (my Solution, Ch.4)

#1

I am finally reading through my book and wasn’t real happy with what I saw for Bronze Challenge solutions after several failed attempts it got me thinking that the solution should be more simple than I was making it.

This lead me to reconsider the “.” solutions and just to say, if replacement text has a letter, refuse it.
That got me to NSCharacterSet.letterCharacterSet() instead of how I was originally attempting to invert NSCharacterSet.decimalCharacterSet. Anyway, here’s my go.

func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange,        replacementString string: String) -> Bool {
    
    let existingTextHasDecimalSeparator = textField.text?.rangeOfString(".")
    let replacementTextHasDecimalSeparator = string.rangeOfString(".")
    

    // Bronze Challenge Modifications
    let replacementStringHasLetters = string.rangeOfCharacterFromSet(NSCharacterSet.letterCharacterSet())
    
    
    if ((existingTextHasDecimalSeparator != nil && replacementTextHasDecimalSeparator != nil) || replacementStringHasLetters != nil) {
        return false
    } else {
        return true
    }
#2

With your solution:

  1. I can enter characters such as !#?.

  2. Using the hardware keyboard, I can paste in a number with two decimal points (the book’s solution has the same problem).

Whether you choose to address issues such as those is up to you. The more complicated answers attempted to go further than what the exercise asked for–and only allow numbers and a maximum of one decimal point to be entered in the TextField.

#3

Thanks,

I’ll probably delve in a bit on both those problems as it helps me learn.

Just tried with hardware keyboard and the mutli decimals couldn’t be reproduced.
Ahh I see pasting a dual decimal, nevermind didn’t try that one. (.oO I bet there is an event for this)

However I do see the special chars are allowable. .oO ( must ponder, probably have to approach with decimalCharacterSet again)

#4

Okay, had a chance to reconsider this evening. I came up with the following, which I think solves the problems 7stud7stud indicated:

func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
    
    // Bronze Challenge Modifications and BEYOND!
    
    let existingTextHasDecimalSeparator = textField.text?.rangeOfString(".")
    let replacementTextHasDecimalSeparator = string.rangeOfString(".")
    
    let existingTextHasNegativeDelimiter = textField.text?.rangeOfString("-")
    let replacementTextHasNegativeDelimiter = string.rangeOfString("-")
    
    // 7stud indicated that the special characters are still allowed when hardware keyboard is active
    // also allows for pasting of string with two periods (odd case)
    // code no longer allows pasting of multi . (e.g. 2.2.2) in hardware keyboard
    
    // Need to figure out a way to remove "." from the inverted decimalCharacterSet i just made so
    // . are allowed in input both for onscreen digital keybaord and hardware keyboard
    
    // Had to add in - as well since CharacterSet I created included that.  This allowed for
    // duplicates of - (e.g. -3.3-4), so added another check for it in existing/replacement text
    
    let workingBadSet = NSMutableCharacterSet()
    workingBadSet.formUnionWithCharacterSet(NSCharacterSet.decimalDigitCharacterSet().invertedSet)
    workingBadSet.removeCharactersInString(".-") // aha, woohoo
    
    let replacementStringHasNonDecimals = string.rangeOfCharacterFromSet(workingBadSet)
    
    if (existingTextHasDecimalSeparator != nil && replacementTextHasDecimalSeparator != nil) {
        return false
        } else if (existingTextHasNegativeDelimiter != nil && replacementTextHasNegativeDelimiter != nil) {
            return false
        } else if replacementStringHasNonDecimals != nil {
            return false
        } else {
        return true
    }
    
}
#5

I can still paste in numbers with two decimal places:

  1. In TextEdit type in 2.3.4.
  2. Edit>Copy
  3. With the Simulator selected, Edit>Paste
  4. Click on the TextField, then click on the Paste pop up.
#6

Well, got an issue with “-” values now, where “-” can go in other places but I’m not going to worry about this challenge beyond today. I think I fixed the multiple “.” issue in a pasted string, just to see if I could figure it out. Here’s my last and final entry for this one.

func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
    
    // Bronze Challenge Modifications and BEYOND!
    
    let existingTextHasDecimalSeparator = textField.text?.rangeOfString(".")
    let replacementTextHasDecimalSeparator = string.rangeOfString(".")
    
    let existingTextHasNegativeDelimiter = textField.text?.rangeOfString("-")
    let replacementTextHasNegativeDelimiter = string.rangeOfString("-")
    
    // 7stud indicated that the special characters are still allowed when hardware keyboard is active
    // also allows for pasting of string with two periods (odd case)
    // code no longer allows pasting of multi . (e.g. 2.2.2) in hardware keyboard
    
    // Need to figure out a way to remove "." from the inverted decimalCharacterSet i just made so
    // . are allowed in input both for onscreen digital keybaord and hardware keyboard
    
    // Had to add in - as well since CharacterSet I created included that.  This allowed for
    // duplicates of - (e.g. -3.3-4), so added another check for it in existing/replacement text
    
    let workingBadSet = NSMutableCharacterSet()
    workingBadSet.formUnionWithCharacterSet(NSCharacterSet.decimalDigitCharacterSet().invertedSet)
    workingBadSet.removeCharactersInString(".-")
    
    let replacementStringHasNonDecimals = string.rangeOfCharacterFromSet(workingBadSet)
    
    var hasReplacementStringWithMultipleDots: Bool = false
    
    if (string.componentsSeparatedByString(".").count > 2){
        //debug
        print("string.componentsSeparatedByString returns \(string.componentsSeparatedByString(".").count)")
        hasReplacementStringWithMultipleDots = true
    }
    
    if (existingTextHasDecimalSeparator != nil && replacementTextHasDecimalSeparator != nil) {
        return false
    } else if (existingTextHasNegativeDelimiter != nil && replacementTextHasNegativeDelimiter != nil) {
            return false
    } else if replacementStringHasNonDecimals != nil {
            return false
    } else if hasReplacementStringWithMultipleDots {
            return false
    } else {
        return true
    }
}
#7

Here’s what I ended up doing after piecing together from other examples I’ve found online. Seems to work well.

func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
let existingTextHasDecimalSeparator = textField.text?.rangeOfString(".")
let replacementTextHasDecimalSeparator = string.rangeOfString(".")
let InValidChars = NSCharacterSet(charactersInString: “0123456789.-”).invertedSet
let compByCharsInSet = string.componentsSeparatedByCharactersInSet(inValidChars)
let validated = compByCharsInSet.joinWithSeparator("")

    if existingTextHasDecimalSeparator != nil && replacementTextHasDecimalSeparator != nil {
        return false
    } else {
        return string == validated
    }
}