iOS 拦截篇(一):拦截H5通过标签选取的图片

一、场景

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];
}

结束语

感谢阅读!

猜你喜欢

转载自blog.csdn.net/weixin_34175509/article/details/87451742