最近iOS App项目中使用Webview加载H5页面比较多,也有不少朋友经常问到这个问题,在这里我也学习学习如何通过iOS原生的方式来加载H5页面中的图片然后让webview显示图片。
相信有很多朋友也遇到过这样的问题,可是很多朋友都没有思路,不知如何入手。今天,刚好学习了一下,并写了一个简单的demo。下面让我们一起来学习一下吧!
注意:本文有Objective-C版和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来加载图片,加载完成后,将图片保存到本地:
// 加载图片 // 这里不处理重复加载的问题,实际开发中,应该要做一下处理。 // 也就是先判断是否已经加载过,且未清理掉该缓存的图片。如果 // 已经缓存过,否则才执行下面的语句。 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)
首先,我们要获取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="..."/>
来查找图片链接 这里使用了正则表达式来匹配查找 <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