廣告
Swift iOS開發小書 ,幫你快速上手開發 www.ituring.com.cn/book/2413
正文
每次載入WebView內容,如果圖片可以快取的話,速度就會非常快。預設情況下,WebView自己來載入圖片,快取的策略也是自己定的。如想要自己快取的話,那麼可以使用URLSessionDataDelegate來截獲HTTP訪問,如果訪問的是圖片的話,就自己去發起請求,快取,如果快取了的話,就提取快取,自己構建一個HTTP響應物件返回。
如下案例,使用了Kingfisher庫做實際的圖片快取,從而隔離開和檔案操作相關的細節。程式碼來自: github.com/Finb/V2ex-S… 。可以使用,但是程式碼有些破碎,晚點有時間才調整。
import UIKit
import Kingfisher
class Page: UIViewController,UIGestureRecognizerDelegate{
var c : UIWebView!
var tapGesture : UITapGestureRecognizer!
override func viewDidLoad() {
super.viewDidLoad()
c = UIWebView()
c.frame = super.view.frame
view.addSubview(c)
c.frame.origin.y += 100
c.frame.size.height = 100
c.frame.size.width = 100
let button = UIButton()
button.setTitle("reload", for: .normal)
button.addTarget(self, action: #selector(tap), for: .touchDown)
button.frame = CGRect(x: 0, y: 70, width: 100, height: 20)
view.addSubview(button)
}
func tap(){
loadurl()
}
func loadurl(){
let url = URL(string:"https://httpbin.org/image/png")//must be a https url ,otherwise iOS will fobidden it
let ro = URLRequest(url:url!)
c.loadRequest(ro)
}
}
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
URLProtocol.registerClass(WebViewImageProtocol.self)
self.window = UIWindow(frame: UIScreen.main.bounds)
self.window!.rootViewController = Page()
self.window?.makeKeyAndVisible()
return true
}
}
fileprivate let WebviewImageProtocolHandledKey = "WebviewImageProtocolHandledKey"
class WebViewImageProtocol: URLProtocol ,URLSessionDataDelegate {
var session: URLSession?
var dataTask: URLSessionDataTask?
var imageData: Data?
override class func canInit(with request: URLRequest) -> Bool{
let b = request.url?.absoluteURL.absoluteString.contains("png")
if b! {
if let tag = self.property(forKey: WebviewImageProtocolHandledKey, in: request) as? Bool , tag == true {
return false
}
return true
}
return false
}
override class func canonicalRequest(for request: URLRequest) -> URLRequest{
return request
}
override class func requestIsCacheEquivalent(_ a: URLRequest, to b: URLRequest) -> Bool {
return super.requestIsCacheEquivalent(a, to: b)
}
override func startLoading() {
let resource = ImageResource(downloadURL: self.request.url!)
let data = try? Data(contentsOf:URL(fileURLWithPath: KingfisherManager.shared.cache.cachePath(forKey: resource.cacheKey)))
if let data = data {
//在磁碟上找到Kingfisher的快取,則直接使用快取
print("hitted")
var mimeType = data.contentTypeForImageData()
mimeType.append(";charset=UTF-8")
let header = ["Content-Type": mimeType
,"Content-Length": String(data.count)]
let response = HTTPURLResponse(url: self.request.url!, statusCode: 200, httpVersion: "1.1", headerFields: header)!
self.client?.urlProtocol(self, didReceive: response, cacheStoragePolicy: .allowed)
self.client?.urlProtocol(self, didLoad: data)
self.client?.urlProtocolDidFinishLoading(self)
}
else{
//沒找到圖片則下載
print("caching")
guard let newRequest = (self.request as NSURLRequest).mutableCopy() as? NSMutableURLRequest else {return}
WebViewImageProtocol.setProperty(true, forKey: WebviewImageProtocolHandledKey, in: newRequest)
self.session = URLSession(configuration: URLSessionConfiguration.default, delegate: self, delegateQueue: nil)
self.dataTask = self.session?.dataTask(with:newRequest as URLRequest)
self.dataTask?.resume()
}
}
override func stopLoading() {
self.dataTask?.cancel()
self.dataTask = nil
self.imageData = nil
}
func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive response: URLResponse, completionHandler: @escaping (URLSession.ResponseDisposition) -> Void) {
self.client?.urlProtocol(self, didReceive: response, cacheStoragePolicy: .allowed)
completionHandler(.allow)
}
func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) {
self.client?.urlProtocol(self, didLoad: data)
if self.imageData == nil {
self.imageData = data
}
else{
self.imageData!.append(data)
}
}
func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
if let error = error {
self.client?.urlProtocol(self, didFailWithError: error)
}
else{
self.client?.urlProtocolDidFinishLoading(self)
let resource = ImageResource(downloadURL: self.request.url!)
guard let imageData = self.imageData else { return }
//儲存圖片到Kingfisher
guard let image = DefaultCacheSerializer.default.image(with: imageData, options: nil) else { return }
KingfisherManager.shared.cache.store(image, original: imageData, forKey: resource.cacheKey, toDisk: true){
print("cached")
}
}
}
}
fileprivate extension Data {
func contentTypeForImageData() -> String {
var c:UInt8 = 0
self.copyBytes(to: &c, count: MemoryLayout<UInt8>.size * 1)
switch c {
case 0xFF:
return "image/jpeg";
case 0x89:
return "image/png";
case 0x47:
return "image/gif";
default:
return ""
}
}
}複製程式碼
執行後點選按鈕reload兩次輸出為:
caching
cached
hitted