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 即可**