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

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 即可** 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值