iOS Native加載H5中的圖片
來自: http://www.henishuo.com/ios-native-h5-img/
前言
最近iOS App項目中使用Webview加載H5頁面比較多,也有不少朋友經常問到這個問題,在這里我也學習學習如何通過iOS原生的方式來加載H5頁面中的圖片然后讓webview顯示圖片。
相信有很多朋友也遇到過這樣的問題,可是很多朋友都沒有思路,不知如何入手。今天,剛好學習了一下,并寫了一個簡單的demo。下面讓我們一起來學習一下吧!
本篇文章適合哪些人群閱讀?
- 項目中有類似需求的,而又沒有思路的
- 曾經做過類似需求的,可以參考兩者的思想有何異同,比較哪種方式更好
- 沒有做過類似需求,且也沒有思路入手的,可以參考學習
注意:本文有Objective-C版和Swift,根據個人情況看相關章節
思路講解
這里有兩種方式可以實現:
- 直接使用NSData同步加載圖片的方式( Swift版用同步實現 )
- 通過其他第三方庫異步加載圖片的方式( ObjC版用異步實現 )
Swift版代碼講解
下面看看我們的核心代碼吧:
letpath = NSBundle.mainBundle().pathForResource("test",ofType: "html") leturl = NSURL(fileURLWithPath: path!) do { lethtml = tryString(contentsOfURL: url,encoding: NSUTF8StringEncoding) // print(html) // 獲取所有img src中的src鏈接,并將src更改名稱 // 這里直接采用同步獲取數據,異步也是一樣的道理,為了方便寫demo,僅以同步加載圖片為例。 // 另外,這不考慮清除緩存的問題。 do { letregex = tryNSRegularExpression(pattern: "<img\\ssrc[^>]*/>",options: .AllowCommentsAndWhitespace) letresult = regex.matchesInString(html,options: .ReportCompletion,range: NSMakeRange(0, html.characters.count)) varcontent = htmlasNSString varsourceSrcs: [String: String] = ["": ""] for itemin result { letrange = item.rangeAtIndex(0) letimgHtml = content.substringWithRange(range) asNSString vararray = [""] if imgHtml.rangeOfString("src=\"").location != NSNotFound { array = imgHtml.componentsSeparatedByString("src=\"") } else if imgHtml.rangeOfString("src=").location != NSNotFound { array = imgHtml.componentsSeparatedByString("src=") } if array.count >= 2 { varsrc = array[1]as NSString if src.rangeOfString("\"").location != NSNotFound { src = src.substringToIndex(src.rangeOfString("\"").location) // 圖片鏈接正確解析出來 print(src) // 加載圖片 // 這里不處理重復加載的問題,實際開發中,應該要做一下處理。 // 也就是先判斷是否已經加載過,且未清理掉該緩存的圖片。如果 // 已經緩存過,否則才執行下面的語句。 letdata = NSData(contentsOfURL: NSURL(string: src asString)!) letlocalUrl = self.saveImageData(data!,name: (srcasString).md5) // 記錄下原URL和本地URL // 如果用異步加載圖片的方式,先可以提交將每個URL起好名字,由于這里使用的是原URL的md5作為名稱, // 因此每個URL的名字是固定的。 sourceSrcs[srcasString] = localUrl } } } for (src, localUrl) in sourceSrcs { if !localUrl.isEmpty { content = content.stringByReplacingOccurrencesOfString(srcasString,withString: localUrl,options: NSStringCompareOptions.LiteralSearch,range: NSMakeRange(0, content.length)) } } print(contentasString) webView.loadHTMLString(contentasString,baseURL: url) } catch { print("match error") } } catch { print("load html error") }
NSData同步加載圖片
可以直接通過NSData來加載圖片,加載完成后,將圖片保存到本地:
// 加載圖片 // 這里不處理重復加載的問題,實際開發中,應該要做一下處理。 // 也就是先判斷是否已經加載過,且未清理掉該緩存的圖片。如果 // 已經緩存過,否則才執行下面的語句。 letdata = NSData(contentsOfURL: NSURL(string: src asString)!) letlocalUrl = self.saveImageData(data!, name: (srcasString).md5)
異步加載圖片
這里的Demo中沒有使用異步加載圖片,其實與同步沒有多大的差別,在這里只是說一下思路,具體開發時,也是很容易套用的。這里很巧妙地使用HTML中的原始圖片URL作為Key,而原始圖片URL的md5值作為圖片的名稱,但是只要我們將圖片都統一存儲到同一個沙盒中的同一個目錄下,那么每個圖片存儲到本地后的名稱就是固定的了。因此,我們就可以在匹配到所有的 <img src="..."/> 之后,取得src的值,然后將這個url進行md5加密,再獲取document目錄的路徑,就可以得到這個url在異步加載好圖片后存儲下來的名稱,那么我們就可以在異步加載圖片之前,先修改這個url為本地存儲的路徑。剩下的就不用多說了吧。
在處理加也需要存儲本地圖片路徑與網頁中的圖片鏈接,并通過鍵值對的方式來關聯起來。
// 記錄下原URL和本地URL // 如果用異步加載圖片的方式,先可以提交將每個URL起好名字, // 由于這里使用的是原URL的md5作為名稱, // 因此每個URL的名字是固定的。 sourceSrcs[srcasString] = localUrl
在獲取HTML中原始URL與本地圖片的存儲路徑關聯值后,就可以將HTML中的所有原始url替換成我們ios沙盒中存儲的圖片路徑,如下:
for (src, localUrl) in sourceSrcs { if !localUrl.isEmpty { content = content.stringByReplacingOccurrencesOfString(srcasString, withString: localUrl, options: NSStringCompareOptions.LiteralSearch, range: NSMakeRange(0, content.length)) } }
當我們已經全部將HTML中原始的圖片路徑都替換成ios沙盒中存儲的圖片路徑后,就可以通過Webview加載了:
webView.loadHTMLString(contentasString, baseURL: url)
ObjC版代碼講解
首先,我們要獲取HTML內容,并通過正則表達式來匹配出所有的 <img src="..."/> 的標簽:
NSURL *url = [[NSBundle mainBundle]URLForResource:@"test"withExtension:@"html"]; NSString *html = [[NSString alloc]initWithContentsOfURL:urlencoding:NSUTF8StringEncodingerror:nil]; NSRegularExpression *regex = [NSRegularExpressionregularExpressionWithPattern:@"<img\\ssrc[^>]*/>"options:NSRegularExpressionAllowCommentsAndWhitespaceerror:nil]; NSArray *result = [regexmatchesInString:htmloptions:NSMatchingReportCompletionrange:NSMakeRange(0, html.length)];
接下來,我們需要一個字典來存儲HTML原始的URL和與之關聯的本地URL。由于使用原始URL的md5值作為文件名字,因此本地路徑也就唯一確定了。這里就將圖片都放到Document下。
NSMutableDictionary *urlDicts = [[NSMutableDictionary alloc]init]; NSString *docPath = [NSHomeDirectory()stringByAppendingPathComponent:@"Documents"];
然后,我們需要遍歷所有匹配到的 <img src="..."/> 標簽,并提取出Src屬性值,也就是我們要的原始URL。將并該URL存儲下來,以便下一步替換。
for (NSTextCheckingResult *item in result) { NSString *imgHtml = [htmlsubstringWithRange:[itemrangeAtIndex:0]]; NSArray *tmpArray = nil; if ([imgHtmlrangeOfString:@"src=\""].location != NSNotFound) { tmpArray = [imgHtmlcomponentsSeparatedByString:@"src=\""]; } else if ([imgHtmlrangeOfString:@"src="].location != NSNotFound) { tmpArray = [imgHtmlcomponentsSeparatedByString:@"src="]; } if (tmpArray.count >= 2) { NSString *src = tmpArray[1]; NSUInteger loc = [srcrangeOfString:@"\""].location; if (loc != NSNotFound) { src = [srcsubstringToIndex:loc]; NSLog(@"正確解析出來的SRC為:%@", src); if (src.length > 0) { NSString *localPath = [docPathstringByAppendingPathComponent:[selfmd5:src]]; // 先將鏈接取個本地名字,且獲取完整路徑 [urlDictssetObject:localPathforKey:src]; } } } }
下一步,我們需要將HTML中所有的原始src的url值替換成我們app的沙盒中的圖片路徑,如果該路徑中未存在,則需要去下載圖片,否則不需要重復下載。如下:
// 遍歷所有的URL,替換成本地的URL,并異步獲取圖片 for (NSString *src in urlDicts.allKeys) { NSString *localPath = [urlDictsobjectForKey:src]; html = [htmlstringByReplacingOccurrencesOfString:srcwithString:localPath]; // 如果已經緩存過,就不需要重復加載了。 if (![[NSFileManager defaultManager]fileExistsAtPath:localPath]) { [selfdownloadImageWithUrl:src]; } }
下載圖片后,還需要將圖片存儲到該原始url對應的本地路徑,也就是Document下,其文件名為原始url的md5值,其他也就可以得出去唯一路徑。這里只貼出存儲代碼,關于如何下載圖片,查看demo。
NSData *data = UIImagePNGRepresentation(image); NSString *docPath = [NSHomeDirectory()stringByAppendingPathComponent:@"Documents"]; NSString *localPath = [docPathstringByAppendingPathComponent:[selfmd5:src]]; if (![datawriteToFile:localPathatomically:NO]) { NSLog(@"寫入本地失敗:%@", src); }
難點
這里有幾處難點:
- 如何匹配 <img src="..."/> 來查找圖片鏈接
- 在匹配到以后,如何獲取src的值
- 在得到src的值以后,如何在iOS原生獲取圖片后讓webview加載
這里使用了正則表達式來匹配查找 <img src="..."/> ,匹配結果可能有多個,遍歷數組就可以處理所有的圖片鏈接:
NSRegularExpression(pattern: "<img\\ssrc[^>]*/>", options: .AllowCommentsAndWhitespace
存儲圖片到沙盒
通過獲取到HTML中圖片的鏈接后,我們需要通過ios原生的方式來發起請求,加載圖片,在加載完成后,我們需要將圖片存儲到沙盒中document下:
funcsaveImageData(data: NSData,name: String) ->String { letdocPath = NSSearchPathForDirectoriesInDomains(NSSearchPathDirectory.DocumentDirectory, NSSearchPathDomainMask.UserDomainMask, true)[0]as NSString letpath = docPath.stringByAppendingPathComponent(name) // 若已經緩存過,就不需要重復操作了 if NSFileManager.defaultManager().fileExistsAtPath(path) { return path } do { trydata.writeToFile(path,options: NSDataWritingOptions.DataWritingAtomic) } catch { print("save image data with name: \(name) error") } return path }
驗證是否成功
首先我們可以看到test.html中只有兩個img標簽:
<imgsrc="http://www.jhjcqc.com/ueditor/php/upload/image/20151014/1444783819412910.jpg" /> <imgsrc="http://www.jhjcqc.com/ueditor/php/upload/image/20151014/1444783847836404.jpg" />
在我們替換路徑完成后,我們加載webview,然后打印出webview所加載的HTML內容中這兩個 <img> 標簽的src是否變化,結果如下:
<imgsrc="/Users/huangyibiao/Library/Developer/CoreSimulator/Devices/09692E07-2E79-4070-9537-CFD9F3141B0D/data/Containers/Data/Application/73F6D429-E0FD-4BD4-B0A5-85C1BD179840/Documents/5353c07f4c2ea0471b9f3ee36dedcaac" /> <imgsrc="/Users/huangyibiao/Library/Developer/CoreSimulator/Devices/09692E07-2E79-4070-9537-CFD9F3141B0D/data/Containers/Data/Application/73F6D429-E0FD-4BD4-B0A5-85C1BD179840/Documents/54edea1f2edd444aaba9d0321d786962" />
根據效果圖,我們可以看到圖片是顯示出來了,這就說明替換成功后仍然可以加載出來圖片,實驗成功。
源代碼
想要下載源代碼,請移步github下載,內有Swift版的工程和ObjC版的工程:
https://github.com/CoderJackyHuang/iOSLoadWebViewImage