美文网首页
UITextField输入限制

UITextField输入限制

作者: jayhe | 来源:发表于2019-07-08 15:55 被阅读0次

格式化手机号 | 格式化卡号 | 设置光标位置

在开发的过程中,经常会有产品要求输入框的输入字符限制类型,手机号或者卡号格式化(几位一空格)、金额的输入、输入字符位数的限制等等各式各样的需求

金额输入

要求如下:

  • 首位输入.直接显示0.
  • 小数只能保持2位,超过不可输入
  • 支持限制输入的金额大小

实现效果如下

2019-07-08 16.09.02.gif

代码实现

// 使用的地方,设置输入类型`HCTextFieldInputTypePrice`,设置可输入的金额上限
- (void)testTextFieldUsage {
    self.aTextFiled.delegate = self;
    self.aTextFiled.hcui_inputType = HCTextFieldInputTypePrice;
    self.aTextFiled.hcui_maxValue = 50000;
}

// 在代理方法中,返回类别的判断实现方法
#pragma mark - UITextFieldDelegate

- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
    return [textField hcui_shouldChangeCharactersInRange:range replacementString:string];
}

// 类别中的判断方法,根据设置的类型,调用不同的方法,这里就调用价格输入控制的方法
- (BOOL)hcui_priceCheckWithString:(NSString *)string range:(NSRange)range {
    if ([string isEqualToString:@""]) {
        return YES;
    }
    
    if ([string isEqualToString:@"."]) { // 输入小数点
        NSRange dotRange = [self.text rangeOfString:@"."];
        if (self.text.length < 1) { // 第一位输入小数点,则转化为0.
            self.text = @"0.";
            return NO;
        } else if (self.text.length >= 1 && dotRange.location == NSNotFound) { // 没有输入过小数点
            if (self.text.length - range.location - range.length <= 2) {// 防止输入了一串数字,然后移动光标到某个位置插入小数点,这里小数点允许插入到最多两位小数
                return YES;
            } else {
                return NO;
            }
        } else {
            return NO;
        }
    } else {
        // 如果是小数,小数点后最多两位小数,前面位数限制
        NSRange dotRange = [self.text rangeOfString:@"."];
        if (dotRange.location != NSNotFound) {
            if (range.location > dotRange.location) { // 在小数后面插入
                if (self.text.length + string.length - dotRange.location - dotRange.length <= 2) {
                    return YES;
                } else {
                    return NO;
                }
            }
        }
        // 如果第一位输入的是0
        if (self.text.length == 0 && [string isEqualToString:@"0"]) {
            self.text = @"0.";
            return NO;
        }
        // 判断是否超过最大输入金额限制
        NSMutableString *tmpValue = self.text.mutableCopy;
        [tmpValue insertString:string atIndex:range.location];
        if ([tmpValue doubleValue] <= self.hcui_maxValue) {
            return YES;
        } else {
            return NO;
        }
        return YES;
    }
}

由于需要限制小数点后精度位2位,所以我们需要防止在输入了一串数字之后,移动光标在某个不合适的位置插入小数点

手机号输入

要求如下:

  • 限制输入11位
  • 格式化输入类似于手机通讯录中的电话展示格式 eg:150 1234 1234
  • 删除字符如果前面是空格支持连空格一起删除
  • 复制粘贴的情况也能正常

实际效果如下

2019-07-08 16.06.05.gif

代码实现

- (void)testTextFieldUsage {
    self.aTextFiled.delegate = self;
    self.aTextFiled.hcui_inputType = HCTextFieldInputTypeFormatedPhoneNumber;
    self.aTextFiled.hcui_limitLegnth = 11;
}

#pragma mark - UITextFieldDelegate

- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
    return [textField hcui_shouldChangeCharactersInRange:range replacementString:string];
}

// 在代理中将文字格式化,涉及到需要修改光标的位置,计算得到光标的位置,然后设置
- (BOOL)hcui_formatedPhoneNumberCheckWithString:(NSString *)string range:(NSRange)range {
    NSMutableString *phoneNumbers = [NSMutableString stringWithString:self.text];
    // 删除或剪切
    if (!string.length) {
        if (phoneNumbers.length > 1) {
            NSInteger offset;
            NSString *deleteChar = [phoneNumbers substringWithRange:range];
            if ([deleteChar isEqualToString:@" "]) {
                [phoneNumbers replaceCharactersInRange:NSMakeRange(range.location - 1, 2) withString:@""];
                offset = range.location - 1;
            } else {
                [phoneNumbers replaceCharactersInRange:range withString:@""];
                offset = range.location;
            }
            self.text = [self hcui_formatedPhoneNo:phoneNumbers];
            // 设置光标的位置
            UITextPosition *position = [self positionFromPosition:self.beginningOfDocument inDirection:UITextLayoutDirectionRight offset:offset];
            [self setSelectedTextRange:[self textRangeFromPosition:position toPosition:position]];
            [self sendActionsForControlEvents:UIControlEventEditingChanged];
            return NO;
        }
        return YES;
    } else {
        // 粘贴或者输入
        NSString *tempCardString = [phoneNumbers stringByReplacingOccurrencesOfString:@" " withString:@""];
        NSString *tempString = [string stringByReplacingOccurrencesOfString:@" " withString:@""];
        if (tempCardString.length + tempString.length > self.hcui_limitLegnth) {
            return NO;
        }
        NSInteger beforeBlankCount = [self hcui_blankCountAfterCursorWithRange:range];
        UITextPosition *prePosition = self.selectedTextRange.end; // 记录上一次光标的位置
        NSInteger preLength = phoneNumbers.length;
        [phoneNumbers insertString:string atIndex:range.location];
        self.text = [self hcui_formatedPhoneNo:phoneNumbers];
        NSInteger afterLength = self.text.length;
        NSInteger addedCount = afterLength - preLength; // 字符插入前后,文字的长度变化
        NSInteger afterBlankCount = [self hcui_blankCountAfterCursorWithRange:NSMakeRange(range.location + addedCount, 0)];
        NSInteger blankCountAddedAfterCursor = afterBlankCount - beforeBlankCount; // 计算在插入前后,预设置的光标的后面的空格增加数
        NSInteger offset = addedCount - blankCountAddedAfterCursor; // 文字长度变化 - 在光标之后生成的空格 = 光标实际的偏移量
         // 设置光标的位置
        UITextPosition *position = [self positionFromPosition:prePosition inDirection:UITextLayoutDirectionRight offset:offset];
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.001 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            [self setSelectedTextRange:[self textRangeFromPosition:position toPosition:position]];// 延时是为了修改直接复制粘贴文本的时候,设置光标位置不生效的问题 https://blog.csdn.net/feinifi/article/details/84941280
        });
        [self sendActionsForControlEvents:UIControlEventEditingChanged]; // 代理返回NO之后,通知和event会失效,手动触发事件
        return NO;// 手动设置了text,所以返回NO
    }
}

// 格式化手机号,通过空格来拆分,344的样式
- (NSString *)hcui_formatedPhoneNo:(NSString *)originalPhoneNo {
    NSString *noSpacePhoneNo = [originalPhoneNo stringByReplacingOccurrencesOfString:@" " withString:@""];
    if (noSpacePhoneNo.length <= 3) {
        return noSpacePhoneNo;
    }
    
    NSMutableArray *numbers = [NSMutableArray array];
    NSString *threeNumber = [noSpacePhoneNo substringWithRange:NSMakeRange(0, 3)];
    [numbers addObject:threeNumber];
    NSString *fourNumbersString = [noSpacePhoneNo substringFromIndex:3];
    NSInteger fourNumbersCount = ceil((fourNumbersString.length) / 4.0);
    for (NSInteger i = 0; i < fourNumbersCount; i ++) {
        NSRange range = NSMakeRange(i * 4, MIN(fourNumbersString.length - i * 4, 4));
        NSString *fourNumber = [fourNumbersString substringWithRange:range];
        [numbers addObject:fourNumber];
    }
    
    return [numbers componentsJoinedByString:@" "];
}

由于需要格式化在合适的位置插入空格,所以需要在原本字符输入的时候,插入和删除必要的空格以及移动光标插入字符的时候涉及到空格的移动等,这里通过一个函数将字符转成格式化的,然后在计算光标的位置,设置光标到合适的位置

卡号的输入

要求如下:

  • 卡号格式化输入;4位一空格
  • 删除字符如果前面是空格支持连空格一起删除
  • 复制粘贴的情况也能正常

实际效果如下

2019-07-08 16.01.38.gif

代码如下

- (void)testTextFieldUsage {
    self.aTextFiled.delegate = self;
    self.aTextFiled.hcui_inputType = HCTextFieldInputTypeFormatedCardNumber;
    self.aTextFiled.hcui_limitLegnth = 19;
}

#pragma mark - UITextFieldDelegate

- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
    return [textField hcui_shouldChangeCharactersInRange:range replacementString:string];
}

- (BOOL)hcui_formatedCardCheckWithString:(NSString *)string range:(NSRange)range {
    NSMutableString *cardNumbers = [NSMutableString stringWithString:self.text];
    // 删除或剪切
    if (!string.length) {
        if (cardNumbers.length > 1) {
            NSInteger offset;
            NSString *deleteChar = [cardNumbers substringWithRange:range];
            if ([deleteChar isEqualToString:@" "]) {
                [cardNumbers replaceCharactersInRange:NSMakeRange(range.location - 1, 2) withString:@""];
                offset = range.location - 1;
            } else {
                [cardNumbers replaceCharactersInRange:range withString:@""];
                offset = range.location;
            }
            self.text = [self hcui_formatedCardNo:cardNumbers];
            // 设置光标的位置
            UITextPosition *position = [self positionFromPosition:self.beginningOfDocument inDirection:UITextLayoutDirectionRight offset:offset];
            [self setSelectedTextRange:[self textRangeFromPosition:position toPosition:position]];
            [self sendActionsForControlEvents:UIControlEventEditingChanged];
            return NO;
        }
        return YES;
    } else {
        // 粘贴或者输入
        NSString *tempCardString = [cardNumbers stringByReplacingOccurrencesOfString:@" " withString:@""];
        NSString *tempString = [string stringByReplacingOccurrencesOfString:@" " withString:@""];
        if (tempCardString.length + tempString.length > self.hcui_limitLegnth) {
            return NO;
        }
        NSInteger beforeBlankCount = [self hcui_blankCountAfterCursorWithRange:range];
        UITextPosition *prePosition = self.selectedTextRange.end; // 记录上一次光标的位置
        NSInteger preLength = cardNumbers.length;
        [cardNumbers insertString:string atIndex:range.location];
        self.text = [self hcui_formatedCardNo:cardNumbers];
        NSInteger afterLength = self.text.length;
        NSInteger addedCount = afterLength - preLength; // 字符插入前后,文字的长度变化
        NSInteger afterBlankCount = [self hcui_blankCountAfterCursorWithRange:NSMakeRange(range.location + addedCount, 0)];
        NSInteger blankCountAddedAfterCursor = afterBlankCount - beforeBlankCount; // 计算在插入前后,预设置的光标的后面的空格增加数
        NSInteger offset = addedCount - blankCountAddedAfterCursor; // 文字长度变化 - 在光标之后生成的空格 = 光标实际的偏移量
        // 设置光标的位置
        UITextPosition *position = [self positionFromPosition:prePosition inDirection:UITextLayoutDirectionRight offset:offset];
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.001 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            [self setSelectedTextRange:[self textRangeFromPosition:position toPosition:position]];// 延时是为了修改直接复制粘贴文本的时候,设置光标位置不生效的问题 https://blog.csdn.net/feinifi/article/details/84941280
        });
        [self sendActionsForControlEvents:UIControlEventEditingChanged]; // 代理返回NO之后,通知和event会失效,手动触发事件
        return NO;// 手动设置了text,所以返回NO
    }
}

实现方式跟格式化手机号类似

在粘贴文本的时候,光标的位置设置,虽然计算的是对的,但是设置还是不生效,这个时候采用一个延时设置的方案来解决

示例中代码的完整代码地址:UITextField+HCInputType

相关文章

网友评论

      本文标题:UITextField输入限制

      本文链接:https://www.haomeiwen.com/subject/spfahctx.html