How to achieve that placeholder text disappears character by character in UITextField

后端 未结 6 1791
挽巷
挽巷 2021-02-18 20:28

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

相关标签:
6条回答
  • 2021-02-18 21:03

    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
    }
    
    0 讨论(0)
  • 2021-02-18 21:14

    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)
    
    0 讨论(0)
  • 2021-02-18 21:20

    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.

    0 讨论(0)
  • 2021-02-18 21:23

    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.

    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.

    0 讨论(0)
  • 2021-02-18 21:28

    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.

    0 讨论(0)
  • 2021-02-18 21:29

    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?

    0 讨论(0)
提交回复
热议问题