赞
踩
近日有用户报 app 调用中文手写输入法时出现闪退。问题描述可以参照网上:
笔者的这个 App 中也是一样,使用了 +UITouch 分类重写了 ScrollView 的 touchesXXX 方法。
网上提到的解决办法都是千篇一律,即不要重写 ScrollView 的 touchesXXX 方法。但这个 app 中这是不可能的,因为笔者有一个自定义控件必须要让 scrollView 响应点击事件。
经过调试发现,这个崩溃是因为中文输入法中一个私有类 UIKBCandidateCollectionView(即显示候选词条的 bar)也是一个 UIScrollView ):
(lldb) cpo self
<UIKBCandidateCollectionView: 0x1040b5c00; frame = (0 0; 369 38); clipsToBounds = YES; opaque = NO; autoresize = LM+H; gestureRecognizers = <NSArray: 0x17424b4c0>; layer = <CALayer: 0x170225ac0>; contentOffset: {0, 0}; contentSize: {1003, 38}> collection view layout: <UICollectionViewFlowLayout: 0x103d4f090>
(lldb) cpo [self superclass]
UICollectionView
(lldb) cpo [[self superclass] superclass]
UIScrollView
而这个类的 nextResponder 是一个 UIKBHandwritingCandidateView:
(lldb) cpo [self nextResponder]
<UIKBHandwritingCandidateView: 0x10032ef90; frame = (0 0; 369 38); opaque = NO; layer = <CALayer: 0x170236dc0>>
而当 UIKBCandidateCollectionView 调用 nextResponder 的 touchesBegan:withEvent: 方法时就会 crash。
具体机制未细究,估计是 iOS 的 bug(有时间提给苹果吧)。于是解决办法就是修改 +UITouch 分类,在向 nextResponder 传递触摸事件之前做一个判断,发现是 UIKBCandidateCollectionView 类就不传递触摸事件:
#import "UIScrollView+UITouch.h"
#import <objc/runtime.h>
@implementation UIScrollView(UITouch)
-(NSString* )getClassName{
return NSStringFromClass([self class]);
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
if(![[self getClassName] hasPrefix:@"UIKB"]){
[[self nextResponder] touchesBegan:touches withEvent:event];
}
[super touchesBegan:touches withEvent:event];
}
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
if(![[self getClassName] hasPrefix:@"UIKB"]){
[[self nextResponder] touchesMoved:touches withEvent:event];
}
[super touchesMoved:touches withEvent:event];
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
if(![[self getClassName] hasPrefix:@"UIKB"]){
[[self nextResponder] touchesEnded:touches withEvent:event];
}
[super touchesEnded:touches withEvent:event];
}
@end
注意:Category 分类哪怕是不用导入头文件也会自动失效,这和普通类不一样(普通的类不导入头文件是不会加载的)。只不过如果你不导入分类的头文件的话,你无法在源代码中调用其扩展和覆盖的方法。但 +UITouch 这个分类不同,它的所有 touchesXXX 方法是触摸发生时自动调用的,不需要你在源代码中手动调用。
其实我们可以有另一种更好的选择,即继承 UIScrollView 子类,然后在子类中覆盖 touchesXXX 方法,从而避免在 Category 中使用私有 API,因为 UIKBCandidateCollectionView 类是苹果私有类,不能保证将来它还叫这个名字。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。