How to achieve that placeholder text disappears character by character in UITextField
Asked Answered
D

6

20

Can you please help me.

In UITextField when we provide a placeholder text its placeholder string will be gone when we enter any character. How can I achieve that only entered character will be gone not a full string? Meaning that if I type in 3 characters, only the first 3 characters of placeholder will be gone.

enter image description here

#EDIT 1

Also, newly entered character text color will change and other remaining character text color remains same.

Thanks in advance.

Dasya answered 5/9, 2017 at 6:50 Comment(16)
no need of placeholder, create the overlay and set the same bounds of your textfiled, based on textfield char it automatically hidesMoreland
Ya ... That's a trick but I want to achieve using code by overriding default placeholder property. Thanks.Dasya
@Moreland Actually your solution doesn't work When I want to replace '_' underscore with specific char like 'A' or '10' then there is a mismatch in the string.Dasya
I guess you have to do this with overlay. @Moreland is true.Wheal
_ (underscore) you need to calculate based on input string user given the inputMoreland
Ok, I will do but there are no other workarounds to use this using default placeholder property?Dasya
ur placeholder text length is 8 , but user input is 4 so you need to calculate every text < 7 charsMoreland
by default default placeholder property not available related to ur concept,Moreland
but your question is good, i will add my answer, for e.g add contact number (xxx) - xxxxMoreland
Thanks for your concern.Dasya
@Moreland Actually, I have already implemented it. Setting text shown in the image and replacing individual character input entered by the user with the underscore.Dasya
underscore we need to append for every char input, is this possible to attch your project , it is easy to resolveMoreland
Ya I have already done it using textfield text property but my question is for placeholder text.Dasya
simple bro, can you attach your project what you have doneMoreland
Okay, I will put my own Answer for this question.Dasya
Let us continue this discussion in chat.Dasya
S
6

I don't believe the default behavior of the placeholder is editable, but what you are trying to accomplish can be done using NSAttributedString to simulate the placeholder value.

I'm sure this can be optimized, but here I have created a handler class that acts as the delegate for a given UITextField, manipulating the string the user inputs to achieve the desired effect. You init the handler with your desired placeholder string, so you can make any text field work this way.

Custom Placeholder Text Field

import UIKit

class CustomPlaceholderTextFieldHandler: NSObject {
    let placeholderText: String
    let placeholderAttributes = [NSForegroundColorAttributeName : UIColor.lightGray]
    let inputAttributes = [NSForegroundColorAttributeName : UIColor(red: 255/255, green: 153/255, blue: 0, alpha: 1.0)]
    var input = ""

    init(placeholder: String) {
        self.placeholderText = placeholder
        super.init()
    }

    func resetPlaceholder(for textField: UITextField) {
        input = ""
        setCombinedText(for: textField)
    }

    fileprivate func setCursorPosition(for textField: UITextField) {
        guard let cursorPosition = textField.position(from: textField.beginningOfDocument, offset: input.characters.count)
            else { return }

        textField.selectedTextRange = textField.textRange(from: cursorPosition, to: cursorPosition)
    }

    fileprivate func setCombinedText(for textField: UITextField) {
        let placeholderSubstring = placeholderText.substring(from: input.endIndex)
        let attributedString = NSMutableAttributedString(string: input + placeholderSubstring, attributes: placeholderAttributes)
        attributedString.addAttributes(inputAttributes, range: NSMakeRange(0, input.characters.count))
        textField.attributedText = attributedString
    }
}

extension CustomPlaceholderTextFieldHandler: UITextFieldDelegate {
    func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {

        if string == "" {
            if input.characters.count > 0 {
                input = input.substring(to: input.index(before: input.endIndex))
            }
        } else {
            input += string
        }

        if input.characters.count <= placeholderText.characters.count {
            setCombinedText(for: textField)
            setCursorPosition(for: textField)
            return false
        }
        return true
    }

    func textFieldDidBeginEditing(_ textField: UITextField) {
        setCursorPosition(for: textField)
    }
}

Here's a the way I initialized the gif above:

class ViewController: UIViewController {

    @IBOutlet weak var textField: UITextField!
    let placeholderHandler = CustomPlaceholderTextFieldHandler(placeholder: "_2_-__-__A")

    override func viewDidLoad() {
        super.viewDidLoad()
        textField.delegate = placeholderHandler
        placeholderHandler.resetPlaceholder(for: textField)
    }
}

This could be expanded to take color parameters, fonts, etc. at initialization, or you may find it cleaner to subclass UITextField and make it its own delegate. I also haven't really tested this for selecting/deleting/replacing multiple characters.

The input variable will return the text the user has input at any given point. Also, using a fixed-width font would remove the jitteriness as the user types and replaces the placeholder text.

Semeiology answered 14/9, 2017 at 2:41 Comment(0)
H
4

Instead of using placeholder text, use a UILabel below your textfield and give the same font style to both. the label text should be like "- - - - -"

And when user starts typing on textfield give a space after each character press.

func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {

    if textField.text?.characters.count == 0 && string.characters.count != 0 {
        textField.text = textField.text! + " "
    }
     else {
        return false
    }

    if textField.text?.characters.count == 1 && string.characters.count != 0 {
        textField.text = textField.text! + " "
    }
    else {
        return false
    }

    if textField.text?.characters.count == 2 && string.characters.count != 0 {
        textField.text = textField.text! + " "
    }
    else {
        return false
    }
    if textField.text?.characters.count == 3 && string.characters.count != 0 {
        textField.text = textField.text! + " "
    }
    else {
        return false
    }

    return true
}
Hysterical answered 8/9, 2017 at 6:33 Comment(1)
Okay. Nice solution but I am waiting for some more responses but If there is no other way to do this thing. I will accept your answerDasya
D
2

There is my solution using UITextField text property

+(BOOL)shouldChangeCharactersInRange:(NSRange)range
                   replacementString:(NSString *)string
                           textField:(UITextField *)textField
                                mask:(NSString *)mask withMaskTemplate:(NSString *)maskTemplate{

    NSString * alreadyExistString = @"";
    if (string.length == 0) {
        alreadyExistString = textField.text;
        for (int i = range.location; i >= 0; i--) {
            unichar  currentCharMask = [maskTemplate characterAtIndex:i];
            unichar  currentChar = [alreadyExistString characterAtIndex:i];
            if (currentCharMask == currentChar) {// fixed value and _
                continue;
            }else{
                alreadyExistString = [alreadyExistString stringByReplacingCharactersInRange:NSMakeRange(i, 1) withString:@"_"];
                break;
            }
        }
        textField.text = alreadyExistString;
        return NO;
    }else{
        alreadyExistString = [textField.text stringByReplacingCharactersInRange:NSMakeRange(range.location, 1) withString:string];
    }

    NSMutableString * validText = [[NSMutableString alloc] init];

    int last = 0;
    BOOL append = NO;
    for (int i = 0; i < alreadyExistString.length; i++) {
        unichar  currentCharMask = [mask characterAtIndex:i];
        unichar  currentChar = [alreadyExistString characterAtIndex:i];
        BOOL isLetter = [[NSCharacterSet alphanumericCharacterSet] characterIsMember: currentChar];
        BOOL isDigit  = [[NSCharacterSet decimalDigitCharacterSet] characterIsMember: currentChar];
        if ((isLetter && currentCharMask == '#') || (isDigit && currentCharMask == '9')) {
            [validText appendString:[NSString stringWithFormat:@"%c",currentChar]];
        }else{
            if (currentCharMask == '#' || currentCharMask == '9') {
                break;
            }
            if ((isLetter && currentCharMask!= currentChar)|| (isDigit && currentCharMask!= currentChar)) {
                append = YES;
            }
            [validText appendString:[NSString stringWithFormat:@"%c",currentCharMask]];
        }
        last = i;
    }

    for (int i = last+1; i < mask.length; i++) {
        unichar currentCharMask = [mask characterAtIndex:i];
        if (currentCharMask != '#' && currentCharMask != '9') {
            [validText appendString:[NSString stringWithFormat:@"%c",currentCharMask]];
        }
        if (currentCharMask == '#' || currentCharMask == '9') {
            break;
        }
    }
    if (append) {
        [validText appendString:string];
    }

    NSString *placeHolderMask = textField.text;
    NSString *sub = [validText substringWithRange:NSMakeRange(range.location, 1)];
    placeHolderMask = [placeHolderMask stringByReplacingCharactersInRange:NSMakeRange(range.location, 1) withString:sub];
    textField.text = placeHolderMask;
    return NO;
}

@property (nonatomic,strong) NSString * maskTemplate;// like: _2_-__-__A
@property (nonatomic,strong) NSString * mask;// like: #2#-99-##A
  • Initially set text field text to mask template
  • Then when user enters input shouldChangeCharactersInRange invoked and it does a great job

#Edit 1 There is also some more code I have implemented which move the cursor to the underscore location. If someone need help. Please comment I will help you.

#Edit 2

Problems I have facing using this approached

  • Not able to change individual text color. The color will be same for both the string like @"_" underscore and input characters has the same color.
  • If user not providing any input then I have to provide a check to send blank as a string.
  • Tracking for individual inputs.

Thanks, Still waiting for if there is any other workaround using Placeholder String.

Dasya answered 7/9, 2017 at 10:11 Comment(1)
Please add complete procedure. (Create a class with name and implement it in the code).Dyanne
E
1

I guess this is just what you are looking for. Create your UITextField object with the text _2_-__-__A (not placeholder text). Then, use its view controller as delegate, and add that to the view controller:

-(BOOL)textField:(UITextField*)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString*)string{
    if (range.length>1) return NO; // Avoids removing multiple characters at once
    if (range.location==1) range.location++;  // '2' index
    if (range.location==3) range.location++;  // '-' index
    if (range.location==6) range.location++;  // '-' index
    if (range.location==9)  return NO; // 'A' index
    if (range.location==10) return NO; // String end
    if ([string isEqualToString:@""]) return NO; //Avoids removing characters

    if (range.length==0) {
        range.length++;
        UITextPosition *beginning = textField.beginningOfDocument;
        UITextPosition *start = [textField positionFromPosition:beginning offset:range.location];
        UITextPosition *end = [textField positionFromPosition:start offset:range.length];
        UITextRange *textRange = [textField textRangeFromPosition:start toPosition:end];
        [textField setSelectedTextRange:textRange];
    }

    return YES;
}
-(void)textFieldDidBeginEditing:(UITextField*)textField{
    UITextPosition *beginning = textField.beginningOfDocument;
    UITextPosition *start = [textField positionFromPosition:beginning offset:0];
    UITextPosition *end = [textField positionFromPosition:start offset:0];
    UITextRange *textRange = [textField textRangeFromPosition:start toPosition:end];
    [textField setSelectedTextRange:textRange];
}
-(BOOL)textFieldShouldReturn:(UITextField*)textField{
    [passwordInput resignFirstResponder];
    return YES;
}

It should work as intended.

Elihu answered 10/9, 2017 at 23:59 Comment(0)
F
-1

For that situation use attributed string in swift like bellow,

let attributeFontSaySomething : [String : Any] = [NSFontAttributeName : UIFont.systemFont(ofSize: 12.0)]
let attributeColorSaySomething : [String : Any] = [NSForegroundColorAttributeName : UIColor.blue]

var attributes = attributeFontSaySomething
for (key, value) in attributeColorSaySomething {
    attributes(value, forKey: key)
}

let attStringSaySomething = NSAttributedString(string: "Say something", attributes: attributes)
Fremantle answered 8/9, 2017 at 7:49 Comment(1)
Not a solution attributedPlacholderString will gone when we enter any input.Dasya
J
-3

No You can not do this without make a custom layer on UITextfield after making it you need to check char entered does matched in placehoder string ,then only that charecter ll be replaced. see also Replacing character after each type in UITextField?

Jolson answered 5/9, 2017 at 7:30 Comment(2)
Okay. I will try but I want to replace placeholder individual text. You can see when we enter any input character full placeholder string was gone. I want to override its default behavior.Dasya
okay i am tring to make a demo pls wait,i revert u if can make it.Jolson

© 2022 - 2024 — McMap. All rights reserved.