iOS 14
开始,输入时使用系统的emoji
键盘时,使用键盘自带的搜索框TUIEmojiSearchTextField
,会导致崩溃。
堆栈中注意到来自TUIEmojiSearchTextField
的调用,点击emoji,就会崩溃。
从堆栈看是无限循环调用keyboardInputChangedSelection:
最终导致崩溃,崩溃位置显示在A2DynamicDelegate.m
中。
方法一
在NSObject+A2BlockDelegate.m
中bk_registerDynamicDelegateNamed:
方法中过滤掉TUIEmojiSearchTextField
。
//添加如下代码
if (@available(iOS 13.0, *)) {
if ([delegate isKindOfClass:NSClassFromString(@"TUIEmojiSearchTextField")]) {
delegate = nil;
}
}
+ (void)bk_registerDynamicDelegateNamed:(NSString *)delegateName forProtocol:(Protocol *)protocol
{
NSMapTable *propertyMap = [self bk_delegateInfoByProtocol:YES];
A2BlockDelegateInfo *infoAsPtr = (__bridge void *)[propertyMap objectForKey:protocol];
if (infoAsPtr != NULL) { return; }
const char *name = delegateName.UTF8String;
objc_property_t property = class_getProperty(self, name);
SEL setter = setterForProperty(property, name);
SEL a2_setter = prefixedSelector(setter);
SEL getter = getterForProperty(property, name);
A2BlockDelegateInfo info = {
setter, a2_setter, getter
};
[propertyMap setObject:(__bridge id)&info forKey:protocol];
infoAsPtr = (__bridge void *)[propertyMap objectForKey:protocol];
IMP setterImplementation = imp_implementationWithBlock(^(NSObject *delegatingObject, id delegate) {
A2DynamicDelegate *dynamicDelegate = getDynamicDelegate(delegatingObject, protocol, infoAsPtr, YES);
if ([delegate isEqual:dynamicDelegate]) {
delegate = nil;
}
if (@available(iOS 13.0, *)) {
if ([delegate isKindOfClass:NSClassFromString(@"TUIEmojiSearchTextField")]) {
delegate = nil;
}
}
dynamicDelegate.realDelegate = delegate;
});
if (!swizzleWithIMP(self, setter, a2_setter, setterImplementation, "v@:@", YES)) {
bzero(infoAsPtr, sizeof(A2BlockDelegateInfo));
return;
}
if (![self instancesRespondToSelector:getter]) {
IMP getterImplementation = imp_implementationWithBlock(^(NSObject *delegatingObject) {
return [delegatingObject bk_dynamicDelegateForProtocol:a2_protocolForDelegatingObject(delegatingObject, protocol)];
});
addMethodWithIMP(self, getter, NULL, getterImplementation, "@@:", NO);
}
}
方法二
修改UITextField+BlocksKit.m
处理TUIEmojiSearchTextField
//
// UITextField+BlocksKit.m
// BlocksKit
//
#import "UITextField+BlocksKit.h"
#import "A2DynamicDelegate.h"
#import "NSObject+A2BlockDelegate.h"
#pragma mark Delegate
@interface A2DynamicUITextFieldDelegate : A2DynamicDelegate
@end
@implementation A2DynamicUITextFieldDelegate
- (BOOL)textFieldShouldBeginEditing:(UITextField *)textField {
BOOL ret = YES;
id realDelegate = self.realDelegate;
if (realDelegate && [realDelegate respondsToSelector:@selector(textFieldShouldBeginEditing:)])
ret = [realDelegate textFieldShouldBeginEditing:textField];
BOOL (^block)(UITextField *) = [self blockImplementationForMethod:_cmd];
if (block)
ret &= block(textField);
return ret;
}
- (void)textFieldDidBeginEditing:(UITextField *)textField {
id realDelegate = self.realDelegate;
if (realDelegate && [realDelegate respondsToSelector:@selector(textFieldDidBeginEditing:)])
[realDelegate textFieldDidBeginEditing:textField];
void (^block)(UITextField *) = [self blockImplementationForMethod:_cmd];
if (block)
block(textField);
}
- (BOOL)textFieldShouldEndEditing:(UITextField *)textField {
BOOL ret = YES;
id realDelegate = self.realDelegate;
if (realDelegate && [realDelegate respondsToSelector:@selector(textFieldShouldEndEditing:)])
ret = [realDelegate textFieldShouldEndEditing:textField];
BOOL (^block)(UITextField *) = [self blockImplementationForMethod:_cmd];
if (block)
ret &= block(textField);
return ret;
}
- (void)textFieldDidEndEditing:(UITextField *)textField {
id realDelegate = self.realDelegate;
if (realDelegate && [realDelegate respondsToSelector:@selector(textFieldDidEndEditing:)])
[realDelegate textFieldDidEndEditing:textField];
void (^block)(UITextField *) = [self blockImplementationForMethod:_cmd];
if (block)
block(textField);
}
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
BOOL ret = YES;
id realDelegate = self.realDelegate;
if (realDelegate && [realDelegate respondsToSelector:@selector(textField:shouldChangeCharactersInRange:replacementString:)])
ret = [realDelegate textField:textField shouldChangeCharactersInRange:range replacementString:string];
BOOL (^block)(UITextField *, NSRange, NSString *) = [self blockImplementationForMethod:_cmd];
if (block)
ret &= block(textField, range, string);
return ret;
}
- (BOOL)textFieldShouldClear:(UITextField *)textField {
BOOL ret = YES;
id realDelegate = self.realDelegate;
if (realDelegate && [realDelegate respondsToSelector:@selector(textFieldShouldClear:)])
ret = [realDelegate textFieldShouldClear:textField];
BOOL (^block)(UITextField *) = [self blockImplementationForMethod:_cmd];
if (block)
ret &= block(textField);
return ret;
}
- (BOOL)textFieldShouldReturn:(UITextField *)textField {
BOOL ret = YES;
id realDelegate = self.realDelegate;
if (realDelegate && [realDelegate respondsToSelector:@selector(textFieldShouldReturn:)])
ret = [realDelegate textFieldShouldReturn:textField];
BOOL (^block)(UITextField *) = [self blockImplementationForMethod:_cmd];
if (block)
ret &= block(textField);
return ret;
}
- (BOOL)keyboardInputChangedSelection:(UITextField *)textField {
BOOL ret = YES;
id realDelegate = self.realDelegate;
if ([textField isKindOfClass:NSClassFromString(@"TUIEmojiSearchTextField")]) {
return ret;
}
if (realDelegate && [realDelegate respondsToSelector:@selector(keyboardInputChangedSelection:)])
ret = [realDelegate keyboardInputChangedSelection:textField];
BOOL (^block)(UITextField *) = [self blockImplementationForMethod:_cmd];
if (block)
ret &= block(textField);
return ret;
}
- (BOOL)keyboardInputChanged:(UITextField *)textField {
BOOL ret = YES;
id realDelegate = self.realDelegate;
if ([textField isKindOfClass:NSClassFromString(@"TUIEmojiSearchTextField")]) {
return ret;
}
if (realDelegate && [realDelegate respondsToSelector:@selector(keyboardInputChanged:)])
ret = [realDelegate keyboardInputChanged:textField];
BOOL (^block)(UITextField *) = [self blockImplementationForMethod:_cmd];
if (block)
ret &= block(textField);
return ret;
}
- (BOOL)keyboardInputShouldDelete:(UITextField *)textField {
BOOL ret = YES;
id realDelegate = self.realDelegate;
if ([textField isKindOfClass:NSClassFromString(@"TUIEmojiSearchTextField")]) {
return ret;
}
if (realDelegate && [realDelegate respondsToSelector:@selector(keyboardInputShouldDelete:)])
ret = [realDelegate keyboardInputShouldDelete:textField];
BOOL (^block)(UITextField *) = [self blockImplementationForMethod:_cmd];
if (block)
ret &= block(textField);
return ret;
}
@end
#pragma mark - Category
@implementation UITextField (BlocksKit)
@dynamic bk_shouldBeginEditingBlock, bk_didBeginEditingBlock, bk_shouldEndEditingBlock, bk_didEndEditingBlock, bk_shouldChangeCharactersInRangeWithReplacementStringBlock, bk_shouldClearBlock, bk_shouldReturnBlock;
+ (void)load {
[self bk_registerDynamicDelegate];
[self bk_linkDelegateMethods:@{
@"bk_shouldBeginEditingBlock": @"textFieldShouldBeginEditing:",
@"bk_didBeginEditingBlock": @"textFieldDidBeginEditing:",
@"bk_shouldEndEditingBlock": @"textFieldShouldEndEditing:",
@"bk_didEndEditingBlock": @"textFieldDidEndEditing:",
@"bk_shouldChangeCharactersInRangeWithReplacementStringBlock": @"textField:shouldChangeCharactersInRange:replacementString:",
@"bk_shouldClearBlock": @"textFieldShouldClear:",
@"bk_shouldReturnBlock": @"textFieldShouldReturn:",
}];
}
- (id)customOverlayContainer {
return self;
}
@end