runtime全局过滤表情emoji

2018-06-06  本文已影响0人  昵称nickName

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。

上一篇 下一篇

猜你喜欢

热点阅读