https://my.oschina.net/5zx1Vm/blog/700546
具体实现,导入全局的Category,这里叫UITextField+Emoji,覆盖+load函数(该函数在当前类读入内存时会收到消息),在该函数中通过Runtime替换初始化和Delegate相关函数,在初始化函数中,我们代理业务代码设置的Delegate,当用户有输入操作时,会触发我们代理的Delegate,在处理完Emoji校验之后,再路由给业务代码的Delegate。
我们项目采用.pch做全局h导入,在这里我们导入UITextField+Emoji的Gategory
/// 过滤Emoji字符
import "UITextField+EmojiText.h"
在.m中,我们在+load函数内替换相关函数指针,让原有函数指向我们实现的IMP
void exchangeMethod(Class class, SEL oSEL, SEL nSEL)
{
Method oMethod = class_getInstanceMethod(class, oSEL);
Method nMethod = class_getInstanceMethod(class, nSEL);// 验证当前实例是否实现originalSEL,避免返回父类SEL
BOOL ok = class_addMethod(class, oSEL, method_getImplementation(nMethod), method_getTypeEncoding(nMethod));
if (ok) {
class_replaceMethod(class, nSEL, method_getImplementation(oMethod), method_getTypeEncoding(oMethod));
} else {
method_exchangeImplementations(oMethod, nMethod);
}
}
+ (void) load
{
// setDelegate,拦截Delegate设置,默认走Emoji过滤
exchangeMethod([self class], @selector(setDelegate:), @selector(emoji_setDelegate:));
// getDelegate,返回业务代码设置的Delegate,确保set和get统一
exchangeMethod([self class], @selector(delegate), @selector(emoji_delegate));
// 几种初始化情况
exchangeMethod([self class], @selector(init), @selector(emoji_init));
exchangeMethod([self class], @selector(initWithFrame:), @selector(emoji_initWithFrame:));
exchangeMethod([self class], @selector(initWithCoder:), @selector(emoji_initWithCoder:));
// 释放内部持有资源
// exchangeMethod([self class], @selector(dealloc), @selector(emoji_dealloc));
}
- (id) emoji_init
{
id ret = [self emoji_init];// 因为执行了函数指针替换,setDelegate会走emoji_setDelegate,这里调用setDelegate是为了确保没有设置delegate的业务代码同样过滤Emoji
self.delegate = nil;return ret;
}
- (id) emoji_initWithFrame:(CGRect)frame
{
id ret = [self emoji_initWithFrame:frame];self.delegate = nil;return ret;
}
- (id) emoji_initWithCoder:(NSCoder *)aDecoder
{
id ret = [self emoji_initWithCoder:aDecoder];self.delegate = nil;return ret;
}
- (void) emoji_setDelegate:(id )delegate
{
// 如果没有设置过delegate,需要设置内部代理的Delegate,否则让替换内部originalDelegate
id del = [self emoji_delegate];
if (!del) {
EmojiDelegate *emojiDelegate = [[EmojiDelegate alloc] initWithTextField:self];
emojiDelegate.originalDelegate = delegate;
[self emoji_setDelegate:emojiDelegate];
self.strongDel = emojiDelegate;//给一个强引用,防止释放
} else {
EmojiDelegate *emojiDelegate = (EmojiDelegate *) del;
emojiDelegate.originalDelegate = delegate;
}
}
- (id )emoji_delegate
{
return ((EmojiDelegate *)[self emoji_delegate]).originalDelegate;
}
- (void)setStrongDel:(id)strongDel {
objc_setAssociatedObject(self, @"strongDel", strongDel, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
下面是表情过滤方法
@interface EmojiDelegate : NSObject
@property(nonatomic, weak) UITextField *textField;
@property(nonatomic, weak) id originalDelegate;
@property(nonatomic, strong) NSString *prevText;// 上次的输入结果
- (id) initWithTextField:(UITextField *)textField;
@end
@implementation EmojiDelegate
- (id) initWithTextField:(UITextField *)textField
{
self = [super init];self.textField = textField;
[textField addTarget:self action:@selector(textFieldDidChange:) forControlEvents:UIControlEventEditingChanged];return self;
}
- (void) dealloc
{
[_textField removeTarget:self action:@selector(textFieldDidChange:) forControlEvents:UIControlEventEditingChanged];
self.originalDelegate = nil;
self.prevText = nil;
// [super dealloc];
}
- (BOOL)textFieldShouldBeginEditing:(UITextField *)textField
{
if ([self.originalDelegate respondsToSelector:@selector(textFieldShouldBeginEditing:)]) {
return [self.originalDelegate textFieldShouldBeginEditing:textField];
}
return YES;
}
- (void)textFieldDidBeginEditing:(UITextField *)textField
{
if ([self.originalDelegate respondsToSelector:@selector(textFieldDidBeginEditing:)]) {
return [self.originalDelegate textFieldDidBeginEditing:textField];
}
}
- (BOOL)textFieldShouldEndEditing:(UITextField *)textField
{
if ([self.originalDelegate respondsToSelector:@selector(textFieldShouldEndEditing:)]) {
return [self.originalDelegate textFieldShouldEndEditing:textField];
}
return YES;
}
- (void)textFieldDidEndEditing:(UITextField *)textField
{
if ([self.originalDelegate respondsToSelector:@selector(textFieldDidEndEditing:)]) {
return [self.originalDelegate textFieldDidEndEditing:textField];
}
}
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
if (string.length == 0) {
return YES;
}/// 过滤emoji// 忽略系统默认的emoji键盘
if ([[[textField textInputMode] primaryLanguage] isEqualToString:@"emoji"]) {
return NO;
}// 验证string的emoji字符
if ([string containEmoji]) {
return NO;
}if ([self.originalDelegate respondsToSelector:@selector(textField:shouldChangeCharactersInRange:replacementString:)]) {
return [self.originalDelegate textField:textField shouldChangeCharactersInRange:range replacementString:string];
}
return YES;
}
- (BOOL)textFieldShouldClear:(UITextField *)textField
{
if ([self.originalDelegate respondsToSelector:@selector(textFieldShouldClear:)]) {
return [self.originalDelegate textFieldShouldClear:textField];
}
return NO;
}
- (BOOL)textFieldShouldReturn:(UITextField *)textField
{
if ([self.originalDelegate respondsToSelector:@selector(textFieldShouldReturn:)]) {
return [self.originalDelegate textFieldShouldReturn:textField];
}
return NO;
}
过程中新的代理一直设置不上,EmojiDelegate初始化后就释放,后来想到UITextField的delegate是weak属性!!!!解决方法,又给分类加了一个strong属性的StrongDel,用它指向新初始化的EmojiDelegate。
网友评论