iOS --- 使用runtime解决3D Touch导致UIImagePicker崩溃的问题
来源:互联网 发布:彭真 知乎 编辑:程序博客网 时间:2024/06/07 22:05
UIImagePickerController是iOS中自带的系统相册选择器, 使用起来非常简便.
UIImagePickerController
UIImagePickerController *imagePickerController = [[UIImagePickerController alloc] init];// 设置sourceType为系统相册, 如果使用Camera请对应修改该属性.imagePickerController.sourceType = UIImagePickerControllerSourceTypePhotoLibrary;imagePickerController.allowsEditing = YES;imagePickerController.delegate = self;[self presentViewController:imagePickerController animated:YES completion:nil];
UIImagePickerController本身继承自UINavigationController, 因此不能将其简单视作一个UIViewController来看待.
了UINavigationControllerDelegate和UIImagePickerControllerDelegate协议, 包含了由三个ViewController组成的导航视图, 我们通过打印其viewControllers即可看出来:
(lldb) po [picker viewControllers]<__NSArrayI 0x146b31140>( <PUUIAlbumListViewController: 0x14609a200>, <PUUIPhotosAlbumViewController: 0x1458e5a00>, <PUUIImageViewController: 0x1468f1ad0>)
UIImagePickerControllerDelegate
UIImagePickerControllerDelegate协议有如下三个代理方法.
__TVOS_PROHIBITED @protocol UIImagePickerControllerDelegate<NSObject>@optional// The picker does not dismiss itself; the client dismisses it in these callbacks.// The delegate will receive one or the other, but not both, depending whether the user// confirms or cancels.- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingImage:(UIImage *)image editingInfo:(nullable NSDictionary<NSString *,id> *)editingInfo NS_DEPRECATED_IOS(2_0, 3_0);- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary<NSString *,id> *)info;- (void)imagePickerControllerDidCancel:(UIImagePickerController *)picker;
其中info包含了所选取照片的基本信息, 如下:
// info dictionary keysUIKIT_EXTERN NSString *const UIImagePickerControllerMediaType __TVOS_PROHIBITED; // an NSString (UTI, i.e. kUTTypeImage)UIKIT_EXTERN NSString *const UIImagePickerControllerOriginalImage __TVOS_PROHIBITED; // a UIImageUIKIT_EXTERN NSString *const UIImagePickerControllerEditedImage __TVOS_PROHIBITED; // a UIImageUIKIT_EXTERN NSString *const UIImagePickerControllerCropRect __TVOS_PROHIBITED; // an NSValue (CGRect)UIKIT_EXTERN NSString *const UIImagePickerControllerMediaURL __TVOS_PROHIBITED; // an NSURLUIKIT_EXTERN NSString *const UIImagePickerControllerReferenceURL NS_AVAILABLE_IOS(4_1) __TVOS_PROHIBITED; // an NSURL that references an asset in the AssetsLibrary frameworkUIKIT_EXTERN NSString *const UIImagePickerControllerMediaMetadata NS_AVAILABLE_IOS(4_1) __TVOS_PROHIBITED; // an NSDictionary containing metadata from a captured photoUIKIT_EXTERN NSString *const UIImagePickerControllerLivePhoto NS_AVAILABLE_IOS(9_1) __TVOS_PROHIBITED; // a PHLivePhoto}
当在系统相册中选取了照片之后, 会调用imagePickerController:didFinishPickingMediaWithInfo:方法, 在其中可以添加对该照片的一些操作, 如写入相册等.
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary<NSString *,id> *)info { UIImage *originalImage = info[UIImagePickerControllerOriginalImage]; UIImage *editedImage = info[UIImagePickerControllerEditedImage]; UIImage *savedImage = editedImage ?: originalImage; if (picker.sourceType == UIImagePickerControllerSourceTypeCamera) { UIImageWriteToSavedPhotosAlbum(savedImage, nil, nil, nil); } __weak ViewController *weakSelf = self; [picker dismissViewControllerAnimated:YES completion:^{ weakSelf.imageView.image = savedImage; }];}
并且UIImagePickerController自身并不会dismiss, 需要我们自己调用, 并且通过回调的方式将照片进行相应的处理.
上边的代码, 即在UIImagePickerController的dismiss操作之后, 将照片放置到UIImageView中.
自定义UIImagePickerController
创建一个UIViewController, 继承自UIImagePickerController, 并实现相关协议方法, 即可以自定义一个相册选择器.
不过实际上依然只是对UIImagePickerController进行了一次包装而已.
请参考demo:
https://github.com/icetime17/Playground/tree/master/DemoUIImagePicker
另外, 可以使用PhotoKit框架, 结合UICollectionView来实现真正意义上的自定义相册.
参考博客:
iOS — 使用PhotoKit代替ALAssetsLibrary来管理相册资源
问题
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; }#pragma clang diagnostic push#pragma clang diagnostic ignored "-Wundeclared-selector" 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; } }];#pragma clang diagnostic pop}
这样, 即可成功解决3D Touch导致系统的UIImagePickerController崩溃的问题.
- iOS --- 使用runtime解决3D Touch导致UIImagePicker崩溃的问题
- iOS --- 使用runtime解决3D Touch导致UIImagePicker崩溃的问题
- IOS 3D touch的使用
- ios 3D Touch 的使用
- 如何解决ios SIGPIPE 导致的崩溃
- 如何解决ios SIGPIPE 导致的崩溃
- 如何解决ios SIGPIPE 导致的崩溃
- 如何解决ios SIGPIPE 导致的崩溃
- iOS中解决后台返回的null导致的崩溃问题
- iOS开发 3D Touch的简单使用
- iOS 3D Touch开发tableview页面内的使用
- iOS开发--3D Touch的基本使用
- iOS利用runtime,解决多次点击相同button,导致重复跳转的问题
- iOS- 利用runtime,解决多次点击相同button,导致重复跳转的问题
- iOS开发--利用 runtime,解决多次点击相同 button,导致重复跳转的问题
- 3D touch的使用
- iOS开发 3D-touch使用
- iOS 3D Touch简单使用
- Keras - 一个基于 Theano 的深度学习 Python 库
- 《C++0x漫谈》系列之:Concept, Concept!
- oracle11g dataguard 线上维护问题记录
- 基于Theano的深度学习(Deep Learning)框架Keras
- Spring整合Hibernate中自动建表
- iOS --- 使用runtime解决3D Touch导致UIImagePicker崩溃的问题
- 最小m段和问题
- getview重复调用问题
- Linux并发(子进程退出状态的处理)
- 第四周项目 猜数
- PADS覆铜地线为什么不能连在一起呢?
- 学术搜索及谷歌学术镜像
- sizeof运算符
- [LeetCode 335] Self Crossing