您的位置:首页 > 产品设计 > UI/UE

iOS --- 使用runtime解决3D Touch导致UIImagePicker崩溃的问题

2016-04-15 02:55 423 查看
3D Touch

3D Touch是iPhone 6s/6splus设备才有的特点, 在系统相册中长按一个照片, 可触发3D Touch相关的操作.

而在没有3D Touch的设备中, 在系统相册中长按一个照片, 会导致crash. 这看起来像是iOS系统的一个bug.

原因在于:

触发3D Touch操作后, PUPhotosGridViewController的previewingContext:viewControllerForLocation:未实现, 所以导致crash.

解决方法:

使用runtime来实现method swizzling, 即在runtime中将该方法替换.

使用method_exchangeImplementations(originalMethod, replacementMethod);方法即可实现.

首先, 封装一个方法用于实现method swizzling

- (void)replaceSelectorForClass:(Class)cls
SelectorOriginal:(SEL)original
SelectorReplace:(SEL)replacement
withBlock:(id)block {

IMP implementation = imp_implementationWithBlock(block);
Method originalMethod = class_getInstanceMethod(cls, original);
class_addMethod(cls, replacement, implementation, method_getTypeEncoding(originalMethod));
Method replacementMethod = class_getInstanceMethod(cls, replacement);
if (class_addMethod(cls, original, method_getImplementation(replacementMethod), method_getTypeEncoding(replacementMethod))) {
class_replaceMethod(cls, replacement, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod));
} else {
method_exchangeImplementations(originalMethod, replacementMethod);
}

}


在AppDelegate的application:didFididFinishLaunchingWithOptions:方法中, 进行method swizzling:

- (void)preventImagePickerCrashOn3DTouch {
// Load PhotosUI and bail if 3D Touch is unavailable.
// (UIViewControllerPreviewing may be redundant,
// as PUPhotosGridViewController only seems to exist on iOS 9,
// but I'm being cautious.)
NSString *photosUIPath = @"/System/Library/Frameworks/PhotosUI.framework";
NSBundle *photosUI = [NSBundle bundleWithPath:photosUIPath];
Class photosClass = [photosUI classNamed:@"PUPhotosGridViewController"];
if (!(photosClass && objc_getProtocol("UIViewControllerPreviewing"))) {
return;
}

SEL selector = @selector(ab_previewingContext:viewControllerForLocation:);
[self replaceSelectorForClass:photosClass
SelectorOriginal:@selector(previewingContext:viewControllerForLocation:)
SelectorReplace:selector
withBlock:^UIViewController *(id self, id<UIViewControllerPreviewing> previewingContext, CGPoint location) {

// Default implementation throws on iOS 9.0 and 9.1.
@try {
//                                MTLog(@"Replace method at runtime to prevent UIImagePicker crash on 3D Touch.");

return ((UIViewController *(*)(id, SEL, id, CGPoint))objc_msgSend)(self, selector, previewingContext, location);
} @catch (NSException *e) {
return nil;
}
}];

}


**#import <objc/runtime.h>
如果编译报错 user of undeclared identifier objc_msgSend
选中项目 Build Settings 将ENABLE_STRICT_OBJC_MSGSEND设置为 NO 即可**
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: