zoukankan      html  css  js  c++  java
  • UIWebView保存网页中的图片(转载)

    现在H5混合原生开发的方式越来越流行,也就要用到UIWebView控件。在开发过程中,我们可能会遇到一个需求,要求我们保存网页上的图片,当用户点击图片的时候,就可以让用户选择是否下载图片。

    在系统自带的Safari浏览器已经实现了该功能,但是iOS开发中我们如果调用UIWebView加载图片,会发现无法使用Safari保存图片的功能的。这就需要我们自己去实现。

    要保存网页中的图片,关键是要获取手指点击位置的图片的url地址,这就需要从js调用oc的方法。下面介绍两种方法来实现图片保存功能,但是这两种方法都只适用于图片地址用如下形式表示才可以获取:

    1
    ![](http://www.img0.bdstatic.com/img/image/meinvtoutu1242.png)

    如果图片是通过js动态生成的,就无法使用下面的方法获取。

    方法1、获取点击位置的图片的src属性

    实现原理:

    该方法主要是获取手指点击的位置,然后获取该位置的标签的src属性,如果是img标签,那么就可以获取到url。如果是其他标签,就无法获取到url属性。

    实现代码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    @interface ViewController ()@property (weak, nonatomic) IBOutlet UIWebView *webView;
    @end
    @implementation ViewController
    - (void)viewDidLoad
    {
    self.webView.delegate = self;
    [self.webView  loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"http://image.baidu.com/wisebrowse/index?tag1=%E7%BE%8E%E5%A5%B3&tag2=%E5%85%A8%E9%83%A8&tag3=&pn=0&rn=10&from=index&fmpage=index&pos=magic#/home"]]];
    UILongPressGestureRecognizer * longPressed = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(longPressed:)];
    longPressed.delegate = self;
    [self.webView addGestureRecognizer:longPressed];
    }
    - (void)longPressed:(UITapGestureRecognizer*)recognizer{
    //只在长按手势开始的时候才去获取图片的url
    if (recognizer.state != UIGestureRecognizerStateBegan) {
    return;
    }
    CGPoint touchPoint = [recognizer locationInView:self.webView];
    NSString *js = [NSString stringWithFormat:@"document.elementFromPoint(%f, %f).src", touchPoint.x, touchPoint.y];
    NSString *urlToSave = [self.webView stringByEvaluatingJavaScriptFromString:js];
    if (urlToSave.length == 0) {
    return;
    }
    NSLog(@"获取到图片地址:%@",urlToSave);
    }
    //可以识别多个手势
    -(BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
    {
    return YES;
    }

    分析:

    上述代码的核心功能实现就是如下两行代码:

    1
    2
    NSString *js = [NSString stringWithFormat:@"document.elementFromPoint(%f, %f).src", touchPoint.x, touchPoint.y];
    NSString *urlToSave = [self.webView stringByEvaluatingJavaScriptFromString:js];

    第一行代码是通过js获取点击位置的标签的src属性,第二行代码是接受向webview注入第一行的js代码后返回的src属性。

    如果点击位置是图片,那么久可以通过img.src拿到图片的url地址,如果不是就返回空值。

    效果展示:

    打开的网页如下所示:

    277755-724e52f24aac9d85.png

    长按不同图片输出结果如下:

    277755-93e8c1eeed6bd9e4.png

    可以看到获取到了正确的图片地址

    方法二、遍历Dom树获取src属性

    该方法的实现比较复杂,但是灵活性更高,不仅仅适用于保存图片。而是适用于任何从js调用oc方法的场景。

    1、NSObject类扩展

    首先我们来给nsobject加一个类扩展,是为了可以给方法传递任意个参数,因为默认系统的performSelector方法最多只能传递两个参数

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    #import "NSObject+Extension.h"
    @implementation NSObject (Extension)
    - (id)performSelector:(SEL)selector withObjects:(NSArray *)objects
    {
    // 方法签名(对方法的描述)
    NSMethodSignature *signature = [[self class] instanceMethodSignatureForSelector:selector];
    if (signature == nil) {
    [NSException raise:@"严重错误" format:@"(%@)方法找不到", NSStringFromSelector(selector)];
    }
    /*NSInvocation : 利用一个NSInvocation对象通过调用方法签名来实现对方法的调用(方法调用者、方法名、方法参数、方法返回值)
    如果仅仅完成这步,和普通的函数调用没有区别,但是关键在于NSInvocation可以对传递进来的selector进行包装,实现可以传递无限多个参数
    普通的方法调用比如:[self performSelector: withObject: withObject:]顶多只能传递两个参数给selector*/
    NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
    invocation.target = self; //调用者是自己
    invocation.selector = selector; //调用的方法是selector
    // 设置参数,可以传递无限个参数
    NSInteger paramsCount = signature.numberOfArguments - 2; // 除self、_cmd以外的参数个数
    paramsCount = MIN(paramsCount, objects.count); //防止函数有参数但是不传递参数时,导致崩溃
    for (NSInteger i = 0; i < paramsCount; i++) {
    id object = objects[i];
    if ([object isKindOfClass:[NSNull class]]) continue//如果传递的参数为null,就不处理了
    [invocation setArgument:&object atIndex:i + 2]; // +2是因为第一个和第二个参数默认是self和_cmd
    }
    // 调用方法
    [invocation invoke];
    // 获取返回值
    id returnValue = nil;
    if (signature.methodReturnLength) { // 有返回值类型,才去获得返回值
    [invocation getReturnValue:&returnValue];
    }
    return returnValue;
    }
    @end

    2、向webview注入js

    在webview的代理方法webViewDidFinishLoad注入如下js代码

    QQ截图20160615162341.png

    上面的方法是遍历Dom树,从中找到img标签,然后给每个img标签加上点击事件,让图片的url变

    为"jscallbackoc://saveImage_*"+this.src;形式,我们先不着急为什么这样做,先记住这里就好了,下面会给大家解释为什么这么做。

    3、切割跳转url

    在点击图片的时候,上面的注入的js代码就会起作用了,那么点击图片,就会触发绑定在该图片的时间,开始跳转,然后执行如下js代码

    1
    document.location.href="jscallbackoc://saveImage_*"+this.src;

    此时就会生成新的图片跳转url为:jscallbackoc://saveImage_*www.baidu.com

    我们在webview的如下代理方法中,可以捕获到该url,然后切割处理

    1
    (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType

    我们捕获到跳转url后,就开始做切割处理,把他转换成oc方法,先看代码

    QQ截图20160615161450.png

    需要注意的一点就是跳转url的格式必须如下所示:

    1
    jscallbackoc://sendMessage_number2_number3_*100$100$99

    其中jscallbackoc使我们自定义的协议名字,你可以改成任意的。但是其他的部分格式必须按照上面所示

    • 协议名格式: 协议名://

    • 方法名: 方法签名1:方法签名2:方法签名3(方法签名用冒号连接)

    • 参数: 参数1$参数2$参数3(多个参数用$连接)

    协议名和方法名直接连接,方法名和参数用*连接

    不建议把上面的冒号和$改成其他符号,比如你改成问号(?),如果原来的src的url里面包括?,那么就会出现错误。

    假设被点击的图片的src地址是:www.baidu.com。那么js代码`` document.location.href="jscallbackoc://saveImage_"+this.src;``生成的图片跳转url就是jscallbackoc://saveImage_www.baidu.com

    经过上面的方法切割之后,得到了方法名:saveImage和参数:www.baidu.com。

    然后把方法名和参数传入最后一行的该方法,就可以执行oc代码了:

    1
    [viewController performSelector:NSSelectorFromString(methodName) withObjects:params];

    此时只要在OC代码里面实现方法-(void)saveImage:(NSString *)imageURL就可以被执行了。

    4、拦截跳转url

    我们在webview的代理方法中,可以捕获到点击图片后的跳转url,然后做处理

    QQ截图20160615161333.png

    在如上方法中我们做了判断,如果跳转过来的url包含了前缀"jscallbackoc://,就说明使我们自定义的方法,我们需要做拦截处理,转换为oc方法,但是其他跳转url就不做任务处理。

    5、验证结果

    我们分别点击网页上的不同图片,输出结果如下:

    1465978377543803.png

    可以看到oc的方法-(void)saveImage:(NSString *)imageURL被执行了。

    6、完整代码

    QQ截图20160615161000.png

    QQ截图20160615161027.png

    QQ截图20160615161047.png

    总结:

    方法1比较简洁,但是仅仅只能获取图片。

    方法二比较繁琐,但是使用范围更广,不光可以保存图片,还可以执行任意的oc代码。

  • 相关阅读:
    mysql触发器:插入数据前更新创建时间为服务器的时间
    import Vue form 'vue’的意思
    【LOJ#10172】涂抹果酱
    【LOJ#10171】牧场的安排
    【LOJ#10170】国王
    【POJ2411】Mondriaan's Dream
    【POJ2228】Naptime
    【CTSC1997】选课
    【CH5302】金字塔
    【洛谷P1168】中位数
  • 原文地址:https://www.cnblogs.com/MasterPeng/p/5711888.html
Copyright © 2011-2022 走看看