前言
相信大家在ios移動應(yīng)用開發(fā)中报嵌,經(jīng)常會遇到這樣的需求:給輸入框限定輸入格式玻熙,比如最多只能輸入11個電話數(shù)字叉庐、只能輸入小于指定值的價格格式(同時最多兩位小數(shù))等等裹匙。我看了大家基本上是實(shí)現(xiàn)UITextFieldDelegate協(xié)議的textField:shouldChangeCharactersInRange:replacementString:方法瑞凑。但我發(fā)現(xiàn)很少對這種常見的需求實(shí)現(xiàn)進(jìn)行封裝。試想如果在開發(fā)中概页,一個view中有很多個UITextfield,而且還是需要不同的輸入格式要求籽御,那么我想在代理方法textField:shouldChangeCharactersInRange:replacementString:中的實(shí)現(xiàn)還是比較繁瑣的,需要各種判斷惰匙。
實(shí)現(xiàn)思路
為了解決上述問題技掏,我想封裝一個可以定制任何格式的UITextfileld 子類。
我準(zhǔn)備也在代理方法textField:shouldChangeCharactersInRange:replacementString:里面進(jìn)行字符判斷项鬼,來控制輸入框能否編輯哑梳。但為了更好的定制化,需要運(yùn)用不同的正則表達(dá)式來匹配格式绘盟。同時這些判斷邏輯我會寫在的UITextfileld子類 --- BSLimitFormTextFild里面鸠真。具體步驟如下:
- 創(chuàng)建UITextfileld子類BSLimitFormTextFild,添加正則表達(dá)式文本的屬性regex,用于控制各式各樣的輸入要求
#import <UIKit/UIKit.h>
static NSString *limitRegexPrice = @"^(\\d{0,5})(\\.[0-9]{0,2})?$"; // 最大值為 99999.99的價格正則表達(dá)式
@interface BSLimitFormTextFild : UITextField
@property (nonatomic, copy) NSString *regex; /**< 輸入框內(nèi)容的正則表達(dá)式 */
@end
- 在BSLimitFormTextFild里面實(shí)現(xiàn)UITextfileld的代理方法textField:shouldChangeCharactersInRange:replacementString:龄毡,通過返回YES or NO來控制是否能夠輸入吠卷。
#import "BSLimitFormTextFild.h"
#import <objc/runtime.h>
@interface BSLimitFormTextFild() <UITextFieldDelegate>
@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) {
//本來是符合規(guī)則的
return [pred evaluateWithObject:text];
}else{
//本來不符合規(guī)則的話就清空(比如外部對該textFild set 一個不合規(guī)的text,當(dāng)點(diǎn)擊開始編輯時就要清空)
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;
}
}
- 最后,可能在使用BSLimitFormTextFild的時候沦零,外部設(shè)置了delegate祭隔,比如用來監(jiān)聽UITextField的開始編輯、結(jié)束編輯的操作的話蠢终,這時會失效以上那些功能序攘;所以我重寫了- (void)setDelegate:(id<UITextFieldDelegate>)delegate 方法茴她,如果外部設(shè)置了新delegate,又沒有使用到textField:shouldChangeCharactersInRange:replacementString:方法的話,就自動給新代理動態(tài)添加該方法程奠,如果用到了就不做任何操作丈牢。具體代碼如下:
- (void)setDelegate:(id<UITextFieldDelegate>)delegate {
[super setDelegate:delegate];
if (delegate != self) {
if (![delegate respondsToSelector:@selector(textField:shouldChangeCharactersInRange:replacementString:)]) {
// 代理沒有實(shí)現(xiàn)該代理方法,就動態(tài)幫他實(shí)現(xiàn)
// b表示反悔bool值; b@:表示沒有參數(shù); b@:@表示有一個參數(shù)(文檔: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 {
// 外部實(shí)現(xiàn)了該代理方法瞄沙,就由外部決定是否可以編輯
}
}
}
demo演示
#import "ViewController.h"
#import "BSLimitFormTextFild.h"
@interface ViewController () <UITextFieldDelegate>
@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;
// 只能輸入整數(shù)
self.integerTF.regex = limitRegexInteger;
// 最多只能輸入11位整數(shù)
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;
}
}
結(jié)語
demo源碼我放在github上了https://github.com/LvBisheng/BSLimitFormTextFild和cocopods了(pod 'BSLimitFormTextFild', '~>1.0.0')己沛,歡迎大家指導(dǎo)交流...