UIWebView保存网页中的图片(转载)

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

在系统自带的Safari浏览器已经实现了该功能,可是iOS开发中咱们若是调用UIWebView加载图片,会发现没法使用Safari保存图片的功能的。这就须要咱们本身去实现。浏览器

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

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

若是图片是经过js动态生成的,就没法使用下面的方法获取。this

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

实现原理:lua

该方法主要是获取手指点击的位置,而后获取该位置的标签的src属性,若是是img标签,那么就能够获取到url。若是是其余标签,就没法获取到url属性。url

实现代码:spa

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

分析:3d

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

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

能够看到获取到了正确的图片地址

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

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

一、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

二、向webview注入js

 

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

QQ截图20160615162341.png

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

为"jscallbackoc://saveImage_*\"+this.src;形式,咱们先不着急为何这样作,先记住这里就行了,下面会给你们解释为何这么作。

三、切割跳转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就能够被执行了。

四、拦截跳转url

咱们在webview的代理方法中,能够捕获到点击图片后的跳转url,而后作处理

QQ截图20160615161333.png

在如上方法中咱们作了判断,若是跳转过来的url包含了前缀"jscallbackoc://,就说明使咱们自定义的方法,咱们须要作拦截处理,转换为oc方法,可是其余跳转url就不作任务处理。

五、验证结果

咱们分别点击网页上的不一样图片,输出结果以下:

1465978377543803.png

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

六、完整代码

QQ截图20160615161000.png

QQ截图20160615161027.png

QQ截图20160615161047.png

总结:

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

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

相关文章
相关标签/搜索