一、场景
H5经常会需要选择手机照片来完善自身内容;比如上传身份证照片、资产照片等等等。有时候这些照片经常需要app处理完后才上传给H5,那么这时候作为app的你就必须先处理好H5选择的手机照片,再上传给他。所以本文接下来将讲解如何拦截处理H5选择的手机照片;
二、基础知识准备
普及基础知识:
H5选择手机照片的方式主要有两种:
- 1、通过与app的JS交互,选择手机照片;
- 2、通过H5自身的<input>标签,选择手机照片;
第一种JS交互的,图片数据的获取太容易处理了,就不再这里讲了。我们这边主要讲H5通过H5自身的<input>标签,选择手机照片。
三、拦截的方法
拦截的思路:1、拦截、替换系统方法;2、在新方法中按产品需求处理图片;
详细步骤如下。
1、拦截、替换系统方法
即:将在WKFileUploadPanel
中实现的UIImagePickerController
的代理方法imagePickerController:didFinishPickingMediaWithInfo:
替换为你在其他类(因为系统没提供WKFileUploadPanel
这个类给你,供你在该类中直接写交换的方法)中写的其他方法。
如:将其替换为你在CJHookFileUploadPanel类中实现的- (void)swizzled_imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary<NSString *,id> *)info
方法。
通过此步,你就能够在新写的方法- (void)swizzled_imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary<NSString *,id> *)info
里面获取到图片选择器选取到的图片数据了。
@implementation CJHookFileUploadPanel
+ (void)hookFileUploadPanel:(BOOL)hook {
SEL originalSelector = @selector(imagePickerController:didFinishPickingMediaWithInfo:);
SEL swizzledSelector = @selector(swizzled_imagePickerController:didFinishPickingMediaWithInfo:);
Class originalClass = NSClassFromString(@"WKFileUploadPanel");
Class otherClass = [CJHookFileUploadPanel class];
if (hook) {
bool success = HookCJHelper_exchangeOriMethodToNewMethodWhichAddFromDiffClass(originalClass, originalSelector, otherClass, swizzledSelector);
NSLog(@"exchangeOriMethodToNewMethod:%@", success ? @"success": @"failure");
} else {
bool success = HookCJHelper_recoverOriMethodToNewMethodWhichAddFromDiffClass(originalClass, originalSelector, otherClass, swizzledSelector);
NSLog(@"recoverOriMethodToNewMethod:%@", success ? @"success": @"failure");
}
}
- (void)swizzled_imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary<NSString *,id> *)info {
NSMutableDictionary *new_info = [[NSMutableDictionary alloc] initWithDictionary:info];
// 处理新new_info......
[self swizzled_imagePickerController:picker didFinishPickingMediaWithInfo:new_info];
}
@end
上述为某类添加另一个类中的方法的实现代码如下:
#pragma mark - Deal New Method Which From Diff Class
/**
* exchange class1's method to class2's method (can recover originalMethod)
* @brief add swizzledSelOwnerClass's swizzledSelector method to originalSelOwnerClass, and make originalSelector method's imp is swizzledSelector's imp
* @attention swizzledSelOwnerClass and originalSelOwnerClass shouldn't same;
*
* @param originalSelOwnerClass the class which will add swizzledMethod, and exchangeImp between originalMethod and swizzledMethod
* @param originalSelector the method which will be exchange
* @param swizzledSelOwnerClass the new method owner class
* @param swizzledSelector the method which will be add for class1, which is in class2
*
* @return is add and exchange Success
*/
bool HookCJHelper_exchangeOriMethodToNewMethodWhichAddFromDiffClass(Class originalSelOwnerClass, SEL originalSelector, Class swizzledSelOwnerClass, SEL swizzledSelector) {
return HookCJHelper_dealNewMethodWhichFromDiffClass(YES, originalSelOwnerClass, originalSelector, swizzledSelOwnerClass, swizzledSelector);
}
///recover class1's method to class2's method (can recover originalMethod)
bool HookCJHelper_recoverOriMethodToNewMethodWhichAddFromDiffClass(Class originalSelOwnerClass, SEL originalSelector, Class swizzledSelOwnerClass, SEL swizzledSelector) {
return HookCJHelper_dealNewMethodWhichFromDiffClass(NO, originalSelOwnerClass, originalSelector, swizzledSelOwnerClass, swizzledSelector);
}
/**
* accoding to the `didAddAndExchange` boolValue, do the corresponding deal. for example, if YES:add the new method which from diff class to origin class; if NO:did recover origin method if has exchange origin method ever before
* @brief if YES:add swizzledSelOwnerClass's swizzledSelector method to originalSelOwnerClass, and make originalSelector method's imp is swizzledSelector's imp; if NO:did recover origin method if has exchange origin method ever before
* @attention swizzledSelOwnerClass and originalSelOwnerClass shouldn't same;
*
* @param didAddAndExchange if YES:add the new method which from diff class to origin class; if NO:did recover origin method if has exchange origin method ever before
* @param originalSelOwnerClass the class which will add swizzledMethod, and exchangeImp between originalMethod and swizzledMethod
* @param originalSelector the method which will be exchange
* @param swizzledSelOwnerClass the new method owner class
* @param swizzledSelector the method which will be add for class1, which is in class2
*
* @return is deal the new method Success
*/
bool HookCJHelper_dealNewMethodWhichFromDiffClass(bool didAddAndExchange, Class originalSelOwnerClass, SEL originalSelector, Class swizzledSelOwnerClass, SEL swizzledSelector) {
NSString *class1String = NSStringFromClass(originalSelOwnerClass);
NSString *class2String = NSStringFromClass(swizzledSelOwnerClass);
if ([class1String isEqualToString:class2String]) {
NSLog(@"Error:the two class should be difference");
return false;
}
Method oriClassSwizzledMethod = class_getInstanceMethod(originalSelOwnerClass, swizzledSelector);
if (oriClassSwizzledMethod) {
NSLog(@"Error:the original class has exist the new method");
return false;
}
Method originalMethod = class_getInstanceMethod(originalSelOwnerClass, originalSelector);
Method swizzledMethod = class_getInstanceMethod(swizzledSelOwnerClass, swizzledSelector);
// demand:exchange the class1's originalMethod to class1's newMethod, but the newMethod can't implemente in class1
// problem analysis: because of apple system doesn't give WKFileUploadPanel class, so we can't implemente and add the new method for it, directly. So
// step1: we implemente the new method in other class at first, for example UIImagePickerController class. Then we add the class2's new method for the class1.
// step2: exchange the class1's originalMethod to class1's newMethod
// eg1:
// decription: add `diff_change_printLog` method for `TestChangeModel1` class
// originalSelOwnerClass:TestChangeModel1 just exist printLog and common_change_printLog method
// swizzledMethod:diff_change_printLog
// eg2:
// decription: add `swizzled_imagePickerController:didFinishPickingMediaWithInfo:` method for `WKFileUploadPanel` class
// originalSelOwnerClass:NSClassFromString(@"WKFileUploadPanel") just exist `imagePickerController:didFinishPickingMediaWithInfo:`
// swizzledMethod:swizzled_imagePickerController:didFinishPickingMediaWithInfo:
NSString *newMethodName = NSStringFromSelector(swizzledSelector);
NSString *oriMethodBeenChangedToNewMethodKey = [NSString stringWithFormat:@"cj_addMethod_%@_%@", class2String, newMethodName]; // a key of flag mark the origin class's origin method's IMP will last be change to which class's which method
BOOL oriMethodBeenChangedToNewMethod = [objc_getAssociatedObject(originalSelOwnerClass, &oriMethodBeenChangedToNewMethodKey) boolValue]; // mark if the origin class's origin method's IMP has been change to other class's method's IMP
if (oriMethodBeenChangedToNewMethod) {
BOOL shouldRecoverIfHasExchangeOriginMethod = !didAddAndExchange;
if (shouldRecoverIfHasExchangeOriginMethod) {
Method oriClassNewMethod = class_getInstanceMethod(originalSelOwnerClass, swizzledSelector);
method_exchangeImplementations(originalMethod, oriClassNewMethod);
objc_setAssociatedObject(originalSelOwnerClass, &oriMethodBeenChangedToNewMethodKey, @(NO), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
return false;
}
// if originalClass doesn't exist swizzledSelector, add it
BOOL swizzMethodIsNewMethodForOriClass =
class_addMethod(originalSelOwnerClass,
swizzledSelector,
method_getImplementation(swizzledMethod),
method_getTypeEncoding(swizzledMethod));
// if swizzMethod is new method for OriClass before, then the above method will add swizzMethod for OriClass and return YES, else it will return NO;
// eg: UIImagePickerController class doesn't exist method2, then add it to UIImagePickerController class;
// eg: now UIImagePickerController exist method1 and method2
// swizzledSelOwnerClass:NSClassFromString(@"WKFileUploadPanel")
// originalMethod:imagePickerController:didFinishPickingMediaWithInfo:
// then: change UIImagePickerController method2 to method1
if (swizzMethodIsNewMethodForOriClass) {
Method oriClassNewMethod = class_getInstanceMethod(originalSelOwnerClass, swizzledSelector);
method_exchangeImplementations(originalMethod, oriClassNewMethod);
objc_setAssociatedObject(originalSelOwnerClass, &oriMethodBeenChangedToNewMethodKey, @(YES), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
return true;
}
NSLog(@"Error:the class1 has exist implemete or added the method, please use new method name for your method which you want to add to class1 from class2");
return false;
}
2、在新方法中按产品需求处理图片
即在你的以上新方法中处理即可。
- (void)swizzled_imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary<NSString *,id> *)info {
NSMutableDictionary *new_info = [[NSMutableDictionary alloc] initWithDictionary:info];
// 处理新new_info......
// 略
[self swizzled_imagePickerController:picker didFinishPickingMediaWithInfo:new_info];
}
结束语
感谢阅读!
网友评论