Uneditable prefix inside a UITextField using Swift
Asked Answered
F

3

8

I'm having a problem regarding the creation of a prefix inside a UITextField using the new Swift language. Currently I have created the UITextField using the Interface Builder and I have assigned an IBOutlet to it, named usernameField, then using the textFieldDidBeginEditing function I write a NSMutableAttributedString inside it, named usernamePrefix, containing only the word "C-TAD-" and finally I limited the UITextField max characters number to 13, like so:

class ViewController: UIViewController, UITextFieldDelegate {

    @IBOutlet var usernameField : UITextField!

    private var usernamePrefix = NSMutableAttributedString(string: "C-TAD-")

    func textFieldDidBeginEditing(textField: UITextField) {
        if textField == usernameField {
            if usernameField.text == "" {
                usernameField.attributedText = usernamePrefix
            }
        }

        usernameField.addTarget(self, action: "textFieldDidChangeText:", forControlEvents:UIControlEvents.EditingChanged)
    }

    func textField(textField: UITextField!, shouldChangeCharactersInRange range: NSRange, replacementString string: String!) -> Bool {
        let maxUsernameLength = countElements(usernameField.text!) + countElements(string!) - range.length

        return maxUsernameLength <= 13
    }

    override func viewDidLoad() {
        super.viewDidLoad()

        usernameField.delegate = self
        passwordField.delegate = self
    }
}

Now, how can I assign new parameters to the usernamePrefix in order to have to give 2 different colors to the text written in the UITextField? I would like to have the prefix in .lightGreyColor() and the rest in .blackColor(). Also how can I make the usernamePrefix un-editable and un-deletable by the user?

Thanks for the help

Followup answered 10/2, 2015 at 15:5 Comment(0)
P
22

For the first part, you can refactor your delegate method as follow.

func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
    //This makes the new text black.
    textField.typingAttributes = [NSForegroundColorAttributeName:UIColor.blackColor()]
    let protectedRange = NSMakeRange(0, 6)
    let intersection = NSIntersectionRange(protectedRange, range)
    if intersection.length > 0 {

        return false
    }
    if range.location == 12 {
        return true
    }
    if range.location + range.length > 12 {
        return false
    }
    return true
}

This will lock down both the length at 13 and the prefix can not be deleted. Everything typed will be UIColor.blackColor()

Then you can a method like the following in your viewDidLoad, to set the prefix.

func makePrefix() {
    let attributedString = NSMutableAttributedString(string: "C-TAD-")
    attributedString.addAttribute(NSForegroundColorAttributeName, value: UIColor.lightGrayColor(), range: NSMakeRange(0,6))
    textField.attributedText = attributedString
}
Petronille answered 10/2, 2015 at 16:30 Comment(0)
V
25

Simpler option would be to set leftView of the UITextField and customise it how you like it:

let prefix = UILabel()
prefix.text = "C-TAD-"
// set font, color etc.
prefix.sizeToFit()

usernameField.leftView = prefix
usernameField.leftViewMode = .whileEditing // or .always

It is un-editable and un-deletable and you don't need to do any calculations to check the length of the input.

Vasily answered 11/12, 2018 at 13:13 Comment(2)
A notable difference between this and the accepted answer is this: with the accepted answer, the prefix is still part of the text field's text. The distinction may matter if you have a text field with, say, a line underneath the editable text. With the accepted answer, such a line will show underneath the prefix, while with this answer, it will not; the line will start where the prefix ends. This might be a pro or a con, depending on your needs.Graphemics
I was about to use this answer..but @PhlippieBosman's comment saved my time. ThanksFranza
P
22

For the first part, you can refactor your delegate method as follow.

func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
    //This makes the new text black.
    textField.typingAttributes = [NSForegroundColorAttributeName:UIColor.blackColor()]
    let protectedRange = NSMakeRange(0, 6)
    let intersection = NSIntersectionRange(protectedRange, range)
    if intersection.length > 0 {

        return false
    }
    if range.location == 12 {
        return true
    }
    if range.location + range.length > 12 {
        return false
    }
    return true
}

This will lock down both the length at 13 and the prefix can not be deleted. Everything typed will be UIColor.blackColor()

Then you can a method like the following in your viewDidLoad, to set the prefix.

func makePrefix() {
    let attributedString = NSMutableAttributedString(string: "C-TAD-")
    attributedString.addAttribute(NSForegroundColorAttributeName, value: UIColor.lightGrayColor(), range: NSMakeRange(0,6))
    textField.attributedText = attributedString
}
Petronille answered 10/2, 2015 at 16:30 Comment(0)
O
1

I've adopted the solution from Jeremy and make a little bit improvement to make it a bit more swifty, and also handle the case when user pastes multiple characters into the text field.

func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
    let protectedRange = NSRange(location: 0, length: usernamePrefix.length)
    let intersection = protectedRange.intersection(range)
    
    // prevent deleting prefix
    if intersection != nil {
        return false
    }
    
    // limit max character count
    if (textField.text ?? "").count + string.count > 13 {
        return false
    }
    
    return true
}
Ofay answered 11/12, 2020 at 16:30 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.