最近公司有个需求,要在所有的输入框内限制emoji表情和特殊字符的输入,因为公司的项目已经开发了有半年了,所有输入的地方数不胜数,于是我只能通过Method Swizzling来实现这个需求。
其实在项目中大量使用Method Swizzling的代码会导致后期项目难以维护,所以一般我在使用Method Swizzling的时候会考虑是否有其他的解决方案。
其实Method Swizzling的使用真的很简单,举个简单的例子,就是直接让字体跟屏幕宽度变化。替换系统的设置字体的方法。
+ (void)load{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
SEL originSel = @selector(systemFontOfSize:);
Method originMethod = class_getClassMethod(object_getClass(self), originSel);
SEL customSel = @selector(customFontOfSize:);
Method customMethod = class_getClassMethod(object_getClass(self), customSel);
BOOL success = class_addMethod(object_getClass(self), originSel, method_getImplementation(customMethod), method_getTypeEncoding(customMethod));
if (success) {
class_replaceMethod(object_getClass(self), customSel, method_getImplementation(originMethod), method_getTypeEncoding(originMethod));
}else{
method_exchangeImplementations(originMethod, customMethod);
}
});
}
+ (UIFont *)customFontOfSize:(CGFloat )size{
CGFloat scale = [UIScreen mainScreen].bounds.size.width / 375.0;
CGFloat tepSize = size * scale;
return [self customFontOfSize:tepSize];
}
接下来,我们就可以考虑怎么限制textfield或者textview的输入了。
新建一个category
image.png
引入头文件:
image.png
第一步获取获取交换其代理方法:
+ (void)load{
Class selfClass = [self class];
//注意这里是实例方法
SEL originSel = @selector(setDelegate:);
Method originDelegate = class_getInstanceMethod(selfClass, originSel);
SEL customSel = @selector(setCustomDelegate:);
Method customDelegate = class_getInstanceMethod(selfClass, customSel);
method_exchangeImplementations(originDelegate, customDelegate);
}
- (void)setCustomDelegate:(id<UITextFieldDelegate>)delegate{
//注意:这里不会引起死循环,方法已经交换过来了
[self setCustomDelegate:delegate];
[self exchangeDelegate:delegate];
}
第二步:交换代理对象的方法和被替换的方法
//hook 代理对象的方法
- (void)exchangeDelegate:(id<UITextFieldDelegate>)delegate{
SEL originSel = @selector(textField:shouldChangeCharactersInRange:replacementString:);
Method originMethod = class_getInstanceMethod([delegate class], originSel);
SEL swizzleSel = @selector(swizzleTextField:shouldChangeCharactersInRange:replacementString:);
Method swizzleMethod = class_getInstanceMethod([self class], swizzleSel);
BOOL success = class_addMethod([delegate class], originSel, method_getImplementation(swizzleMethod), method_getTypeEncoding(swizzleMethod));
if (success) {
class_replaceMethod([self class], swizzleSel, method_getImplementation(originMethod), method_getTypeEncoding(originMethod));
}else{
method_exchangeImplementations(originMethod, swizzleMethod);
}
}
- (BOOL)swizzleTextField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string{
NSLog(@"swizzleTextField input == %@", string);
if ([string isEqualToString:@"#"] || [string isEqualToString:@"%"]) {
NSLog(@"不能输入特殊字符");
return NO;
}
return YES;
}
但有一点就是textview 和textfield的代理必须要本类实现,不然无法获取到代理对象,也就无法交换其方法。
self.view.backgroundColor = [UIColor whiteColor];
UITextField * field = [[UITextField alloc]initWithFrame:CGRectMake(20, 200, 300, 30)];
field.borderStyle = UITextBorderStyleRoundedRect;
//这里代理必须实现,因为需要hook需要获取代理对象
field.delegate = self;
[self.view addSubview:field];
你可以自己扩展自己想要限制的字符。
demo 地址