你好,歡迎來到IOS教程網

 Ios教程網 >> IOS編程開發 >> IOS開發綜合 >> UIWebView保存圖片

UIWebView保存圖片

編輯:IOS開發綜合

現在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 ()
@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

上面的方法是遍歷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 ()
@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&#43;&#43;){\ objs[i].onclick="function(){\" document.location.href="\&quot;jscallbackoc://saveImage_*\&quot;+this.src;\" };\="" return="" objs.length;\="" };";="" [webview="" stringbyevaluatingjavascriptfromstring:jsgetimages];="" 注入js方法="" 注入自定義的js方法後別忘了調用="" 否則不會生效="" stringbyevaluatingjavascriptfromstring:@"getimages()"];="" 調用js方法="" 禁用用戶選擇="" stringbyevaluatingjavascriptfromstring:@"document.documentelement.style.webkituserselect="none" ;"];="" 禁用長按彈出框="" 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:="" 方法名:="" 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裡面再替換回“:”="" *methodname="[[subpaths" firstobject="" ]="" stringbyreplacingoccurrencesofstring:@"_"="" withstring:@":"];="" 參數="" 200$300,每個參數之間使用符號$鏈接(避免和url裡面的其他字符混淆,因為一般url裡面不會出現這個字符)="" *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代碼。

大家酌情選擇。

  1. 上一頁:
  2. 下一頁:
蘋果刷機越獄教程| IOS教程問題解答| IOS技巧綜合| IOS7技巧| IOS8教程
Copyright © Ios教程網 All Rights Reserved