UIWebView保存图片
来源:互联网 发布:mac 无法开机无法充电 编辑:程序博客网 时间:2024/05/29 17:32
现在H5混合原生开发的方式越来越流行,也就要用到UIWenview控件。在开发过程中,我们可能会遇到一个需求,要求我们保存网页上的图片,当用户点击图片的时候,就可以让用户选择是否下载图片。
在系统自带的Safari浏览器已经实现了该功能,但是iOS开发中我们如果调用UIWebView加载图片,会发现无法使用Safari保存图片的功能的。这就需要我们自己去实现。
要保存网页中的图片,关键是要获取手指点击位置的图片的url地址,这就需要从js调用oc的方法。下面介绍两种方法来实现图片保存功能,但是这两种方法都只适用于图片地址用如下形式表示才可以获取:
![](http://www.img0.bdstatic.com/img/image/meinvtoutu1242.png)
如果图片是通过js动态生成的,就无法使用下面的方法获取。
方法1、获取点击位置的图片的src属性
实现原理:
该方法主要是获取手指点击的位置,然后获取该位置的标签的src属性,如果是img标签,那么就可以获取到url。如果是其他标签,就无法获取到url属性。
实现代码:
@interface ViewController ()<UIGestureRecognizerDelegate,UIWebViewDelegate>@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;}
分析:
上述代码的核心功能实现就是如下两行代码:
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地址,如果不是就返回空值。
效果展示:
打开的网页如下所示:
长按不同图片输出结果如下:
可以看到获取到了正确的图片地址
方法二、遍历Dom树获取src属性
该方法的实现比较复杂,但是灵活性更高,不仅仅适用于保存图片。而是适用于任何从js调用oc方法的场景。
1、NSObject类扩展
首先我们来给nsobject加一个类扩展,是为了可以给方法传递任意个参数,因为默认系统的performSelector方法最多只能传递两个参数
#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:<#(SEL)#> withObject:<#(id)#> withObject:<#(id)#>]顶多只能传递两个参数给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代码
-(void)injectJS:(UIWebView *)webView{ //js方法遍历图片添加点击事件 返回图片个数 static NSString * const jsGetImages = @"function getImages(){\ var objs = document.getElementsByTagName(\"img\");\ for(var i=0;i<objs.length;i++){\ objs[i].onclick=function(){\ document.location.href=\"jscallbackoc://saveImage_*\"+this.src;\ };\ };\ return objs.length;\ };"; [webView stringByEvaluatingJavaScriptFromString:jsGetImages];//注入js方法 //注入自定义的js方法后别忘了调用 否则不会生效 [webView stringByEvaluatingJavaScriptFromString:@"getImages()"];//调用js方法 // 禁用用户选择 [webView stringByEvaluatingJavaScriptFromString:@"document.documentElement.style.webkitUserSelect='none';"]; // 禁用长按弹出框 [webView stringByEvaluatingJavaScriptFromString:@"document.documentElement.style.webkitTouchCallout='none';"];}
上面的方法是遍历Dom树,从中找到img标签,然后给每个img标签加上点击事件,让图片的url变为"jscallbackoc://saveImage_*\"+this.src;
形式,我们先不着急为什么这样做,先记住这里就好了,下面会给大家解释为什么这么做。
3、切割跳转url
在点击图片的时候,上面的注入的js代码就会起作用了,那么点击图片,就会触发绑定在该图片的时间,开始跳转,然后执行如下js代码
document.location.href=\"jscallbackoc://saveImage_*\"+this.src;\
此时就会生成新的图片跳转url为:jscallbackoc://saveImage_*www.baidu.com
我们在webview的如下代理方法中,可以捕获到该url,然后切割处理
(BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
我们捕获到跳转url后,就开始做切割处理,把他转换成oc方法,先看代码
- (void)performJSMethodWithURL:(NSString *)url protocolName:(NSString *)name performViewController:(UIViewController *)viewController { /* 跳转url : jscallbackoc://sendMessage_number2_number3_*100$100$99 protocolName: jscallbackoc:// 方法名: sendMessage:number2:number3 参数: 100,100,99 方法名和参数组合为oc的方法为:- (void)sendMessage:(NSString *)number number2:(NSString *)number2 number3:(NSString *)number3 */ // 获得协议后面的路径为: sendMessage_number2_*200$300 NSString *path = [url substringFromIndex:name.length]; // 利用“*”切割路径,“*”前面是方法名,后面是参数 NSArray *subpaths = [path componentsSeparatedByString:@"*"]; // 方法名 methodName == sendMessage:number2: // 下面的方法是把"_"替换为"?', js返回的url里面把“:”直接省略了,只能在js里面使用“_”来表示,然后在oc里面再替换回“:” NSString *methodName =[[subpaths firstObject ] stringByReplacingOccurrencesOfString:@"_" withString:@":"]; // 参数 200$300,每个参数之间使用符号$链接(避免和url里面的其他字符混淆,因为一般url里面不会出现这个字符) NSArray *params = nil; if (subpaths.count == 2) { params = [[subpaths lastObject] componentsSeparatedByString:@"$"]; } NSLog(@"方法名:%@-----参数:%@", methodName,params); // 调用NSObject类扩展方法,传递多个参数 [viewController performSelector:NSSelectorFromString(methodName) withObjects:params];}
需要注意的一点就是跳转url的格式必须如下所示:
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代码了
[viewController performSelector:NSSelectorFromString(methodName) withObjects:params];
此时只要在OC代码里面实现方法-(void)saveImage:(NSString *)imageURL
就可以被执行了。
4、拦截跳转url
我们在webview的代理方法中,可以捕获到点击图片后的跳转url,然后做处理
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType{ //点击web页面的图片调用如下代码 NSString *url = [[request URL] absoluteString]; NSString *protocolName = @"jscallbackoc://"; if ( [url hasPrefix:protocolName]) { [self performJSMethodWithURL:url protocolName:protocolName performViewController:self ]; return NO; } return YES;}
在如上方法中我们做了判断,如果跳转过来的url包含了前缀"jscallbackoc://
,就说明使我们自定义的方法,我们需要做拦截处理,转换为oc方法,但是其他跳转url就不做任务处理。
5、验证结果
我们分别点击网页上的不同图片,输出结果如下:
可以看到oc的方法-(void)saveImage:(NSString *)imageURL
被执行了。
6、完整代码
#import "ViewController.h"#import "NSObject+Extension.h"@interface ViewController ()<UIGestureRecognizerDelegate,UIWebViewDelegate>@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"]]];}-(void)webViewDidFinishLoad:(UIWebView *)webView{ [self injectJS:webView];}//每次网页在需要跳转之前,就会执行该方法。返回yes,表示可以跳转。返回no,表示不跳转。我们可以在这个方法里面拦截到跳转的url,然后做处理- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType{ //点击web页面的图片调用如下代码 NSString *url = [[request URL] absoluteString]; NSString *protocolName = @"jscallbackoc://"; if ( [url hasPrefix:protocolName]) { [self performJSMethodWithURL:url protocolName:protocolName performViewController:self ]; return NO; } return YES;}-(void)injectJS:(UIWebView *)webView{ //js方法遍历图片添加点击事件 返回图片个数 static NSString * const jsGetImages = @"function getImages(){\ var objs = document.getElementsByTagName(\"img\");\ for(var i=0;i<objs.length;i++){\ objs[i].onclick=function(){\ document.location.href=\"jscallbackoc://saveImage_*\"+this.src;\ };\ };\ return objs.length;\ };"; [webView stringByEvaluatingJavaScriptFromString:jsGetImages];//注入js方法 //注入自定义的js方法后别忘了调用 否则不会生效 [webView stringByEvaluatingJavaScriptFromString:@"getImages()"];//调用js方法 // 禁用用户选择 [webView stringByEvaluatingJavaScriptFromString:@"document.documentElement.style.webkitUserSelect='none';"]; // 禁用长按弹出框 [webView stringByEvaluatingJavaScriptFromString:@"document.documentElement.style.webkitTouchCallout='none';"];}- (void)performJSMethodWithURL:(NSString *)url protocolName:(NSString *)name performViewController:(UIViewController *)viewController { /* 跳转url : jscallbackoc://sendMessage_number2_number3_*100$100$99 protocolName: jscallbackoc:// 方法名: sendMessage:number2:number3 参数: 100,100,99 方法名和参数组合为oc的方法为:- (void)sendMessage:(NSString *)number number2:(NSString *)number2 number3:(NSString *)number3 */ // 获得协议后面的路径为: sendMessage_number2_*200$300 NSString *path = [url substringFromIndex:name.length]; // 利用“*”切割路径,“*”前面是方法名,后面是参数 NSArray *subpaths = [path componentsSeparatedByString:@"*"]; // 方法名 methodName == sendMessage:number2: // 下面的方法是把"_"替换为"?', js返回的url里面把“:”直接省略了,只能在js里面使用“_”来表示,然后在oc里面再替换回“:” NSString *methodName =[[subpaths firstObject ] stringByReplacingOccurrencesOfString:@"_" withString:@":"]; // 参数 200$300,每个参数之间使用符号$链接(避免和url里面的其他字符混淆,因为一般url里面不会出现这个字符) NSArray *params = nil; if (subpaths.count == 2) { params = [[subpaths lastObject] componentsSeparatedByString:@"$"]; } NSLog(@"方法名:%@-----参数:%@", methodName,params); // 调用NSObject类扩展方法,传递多个参数 [viewController performSelector:NSSelectorFromString(methodName) withObjects:params];}-(void)saveImage:(NSString *)imageURL{ NSLog(@"获取到图片地址:%@",imageURL);}@end
总结:
方法1比较简洁,但是仅仅只能获取图片。
方法二比较繁琐,但是使用范围更广,不光可以保存图片,还可以执行任意的oc代码。
大家酌情选择。
- UIWebView保存图片
- UIWebView保存图片
- uiwebview保存图片
- UIWebView保存图片
- UIWebView保存图片
- 把UIWebView内容保存图片
- UIWebView长按保存图片
- 将UIWebView内容保存为png图片
- UIWebView长按图片生成保存菜单
- uiwebview 保存图片到系统相册
- ios uiwebview 长按图片,保存到本地相册
- ios uiwebview 长按图片,保存到本地相册
- ios uiwebview 长按图片,保存到本地相册
- 自定义 UIWebview 添加长按保存图片等操作
- UIWebView使用 保存图片的本地,以及设置
- 自定义 UIWebview 添加长按保存图片等操作
- 长按UIWebView上的图片保存到相册
- 长按UIWebView上的图片保存到相册
- poj Children of the Candy Corn(BFS+DFS)
- apache 配置 httpd-vhost.conf 。VirtualHost 配置更改域名,端口 。 无法访问 localhost
- MyBatis动态传入表名,字段名参数的解决办法
- JAVA 枚举类型 根据代码得到中文
- PAT乙级1027
- UIWebView保存图片
- Spring面试,IoC和AOP的理解
- 2016年第七届蓝桥杯C/C++程序设计_编程题9题_交换瓶子
- 十五个常用Linux命令行总汇
- Binder连接池
- 《JAVA与模式》之观察者模式
- Linux 之 图形界面与命令行界面间的转换
- Postgresql 当中有四种方式获取当前时间
- 当代移动通信发展四个阶段