ios UITextfield输入框限制:只能输入两位小数的价格格式或其他定制格式

前言

相信大家在ios移动应用开发中,经常会遇到这样的需求:给输入框限定输入格式,比如最多只能输入11个电话数字、只能输入小于指定值的价格格式(同时最多两位小数)等等。我看了大家基本上是实现UITextFieldDelegate协议的textField:shouldChangeCharactersInRange:replacementString:方法。但我发现很少对这种常见的需求实现进行封装。试想如果在开发中,一个view中有很多个UITextfield,而且还是需要不同的输入格式要求,那么我想在代理方法textField:shouldChangeCharactersInRange:replacementString:中的实现还是比较繁琐的,需要各种判断。

实现思路

为了解决上述问题,我想封装一个可以定制任何格式的UITextfileld 子类。
我准备也在代理方法textField:shouldChangeCharactersInRange:replacementString:里面进行字符判断,来控制输入框能否编辑。但为了更好的定制化,需要运用不同的正则表达式来匹配格式。同时这些判断逻辑我会写在的UITextfileld子类 --- BSLimitFormTextFild里面。具体步骤如下:

  1. 创建UITextfileld子类BSLimitFormTextFild,添加正则表达式文本的属性regex,用于控制各式各样的输入要求
    #import 

    static NSString *limitRegexPrice = @"^(\\d{0,5})(\\.[0-9]{0,2})?$"; // 最大值为         99999.99的价格正则表达式

    @interface BSLimitFormTextFild : UITextField

    @property (nonatomic, copy) NSString *regex; /**< 输入框内容的正则表达式 */


    @end

  1. 在BSLimitFormTextFild里面实现UITextfileld的代理方法textField:shouldChangeCharactersInRange:replacementString:,通过返回YES or NO来控制是否能够输入。
#import "BSLimitFormTextFild.h"
#import 

@interface BSLimitFormTextFild() 

@end
@implementation BSLimitFormTextFild

#pragma mark - Life Cycle && Initialize

- (instancetype)initWithFrame:(CGRect)frame {
    if (self = [super initWithFrame:frame]) {
        self.delegate = self;
    }
    return self;
}

- (void)awakeFromNib {
    [super awakeFromNib];
    self.delegate = self;
}

#pragma mark - private action

- (BOOL)predicateWithRegex:(NSString *)regex
                      text:(NSString *)text
                 textField:(UITextField *)textField {
    
    NSPredicate *pred = [NSPredicate predicateWithFormat:@"SELF MATCHES %@",regex];
    BOOL originMatch = YES;
    if (textField.text.length) {
        originMatch = [pred evaluateWithObject:textField.text];
    }
    if (originMatch) {
        //本来是符合规则的
        return [pred evaluateWithObject:text];
    }else{
        //本来不符合规则的话就清空(比如外部对该textFild set 一个不合规的text,当点击开始编辑时就要清空)
        textField.text = nil;
        return YES;
    }
}

#pragma mark - UITextFieldDelegate
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
    NSString *toBeString = [textField.text stringByReplacingCharactersInRange:range withString:string];
    if (!toBeString.length) {
        return YES;
    }
    if (self.regex) {
        return [self predicateWithRegex:self.regex text:toBeString textField:textField];
    } else {
        return YES;
    }
}

  1. 最后,可能在使用BSLimitFormTextFild的时候,外部设置了delegate,比如用来监听UITextField的开始编辑、结束编辑的操作的话,这时会失效以上那些功能;所以我重写了- (void)setDelegate:(id)delegate 方法,如果外部设置了新delegate,又没有使用到textField:shouldChangeCharactersInRange:replacementString:方法的话,就自动给新代理动态添加该方法,如果用到了就不做任何操作。具体代码如下:

- (void)setDelegate:(id)delegate {
    [super setDelegate:delegate];
    if (delegate != self) {
        if (![delegate respondsToSelector:@selector(textField:shouldChangeCharactersInRange:replacementString:)]) {
            // 代理没有实现该代理方法,就动态帮他实现
            // b表示反悔bool值; b@:表示没有参数;  b@:@表示有一个参数(文档:https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtTypeEncodings.html)
            class_addMethod(delegate.class, @selector(textField:shouldChangeCharactersInRange:replacementString:), class_getMethodImplementation(self.class, @selector(textField:shouldChangeCharactersInRange:replacementString:)), "b@:@:@:@");
        } else {
            // 外部实现了该代理方法,就由外部决定是否可以编辑
        }
    }
}

demo演示


#import "ViewController.h"
#import "BSLimitFormTextFild.h"

@interface ViewController () 

@property (weak, nonatomic) IBOutlet BSLimitFormTextFild *nomalTF;
@property (weak, nonatomic) IBOutlet BSLimitFormTextFild *priceTF;
@property (weak, nonatomic) IBOutlet BSLimitFormTextFild *integerTF;
@property (weak, nonatomic) IBOutlet BSLimitFormTextFild *limitCountTF;
@property (weak, nonatomic) IBOutlet BSLimitFormTextFild *customTF;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    // 只能输入价格
    self.priceTF.regex = limitRegexPrice;
    // 只能输入整数
    self.integerTF.regex = limitRegexInteger;
    // 最多只能输入11位整数
    self.limitCountTF.regex = limitRegexIntegerCount;
    
    self.customTF.delegate = self;
}

- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
    NSString *toBeString = [textField.text stringByReplacingCharactersInRange:range withString:string];
    if (toBeString.length > 2) {
        NSLog(@"最多只能输入两个字");
        return NO;
    } else {
        return YES;
    }
}

demo效果

结语

demo源码我放在github上了https://github.com/LvBisheng/BSLimitFormTextFild和cocopods了(pod 'BSLimitFormTextFild', '~>1.0.0'),欢迎大家指导交流...

你可能感兴趣的:(ios UITextfield输入框限制:只能输入两位小数的价格格式或其他定制格式)