如今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地址,若是不是就返回空值。
效果展現:
打開的網頁以下所示:
長按不一樣圖片輸出結果以下:
能夠看到獲取到了正確的圖片地址
方法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代碼
上面的方法是遍歷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方法,先看代碼
須要注意的一點就是跳轉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,而後作處理
在如上方法中咱們作了判斷,若是跳轉過來的url包含了前綴"jscallbackoc://,就說明使咱們自定義的方法,咱們須要作攔截處理,轉換爲oc方法,可是其餘跳轉url就不作任務處理。
五、驗證結果
咱們分別點擊網頁上的不一樣圖片,輸出結果以下:
能夠看到oc的方法-(void)saveImage:(NSString *)imageURL被執行了。
六、完整代碼
總結:
方法1比較簡潔,可是僅僅只能獲取圖片。
方法二比較繁瑣,可是使用範圍更廣,不光能夠保存圖片,還能夠執行任意的oc代碼。