已实现
- 网络请求获取图片
- 内存缓存图片
- 磁盘缓存图片
- 获取磁盘缓存数据大小
- 删除指定图片的缓存
- 删除全部图片缓存
- UIImageView对应分类(支持占位图以及占位图显示模式更改) 其他类要实现设置网络图片请参考UIImageView分类
extension <#类名#>: RBWebImageProtocol {
func <#方法名#>(url: URL, <#其他参数#>) {
// 1.从缓存中获取图片加载
if let cacheImage = RBWebImageCach.getCacheImage(urlStr: url.absoluteString) {
// 缓存中找到图片,更新图片
<#code#>
return
}
// 2.缓存中没有时通过网络请求图片数据
getData(url: url!) {[weak self] (data) in
if data == nil {
// 网络请求到的数据为空
<#code#>
}else {
if let ima = UIImage(data: data!) {
// 网络请求到图片
<#code#>
// 缓存图片data
RBWebImageCach.saveImageData(data: data!, urlStr: (url!.absoluteString))
}else {
// 网络请求到的数据转图片失败
<#code#>
}
}
}
}
}
流程
- 通过url设置图片
- 通过url到缓存中获取图片(现在内存中去找,内存中没有找到再到磁盘中查找)
- 缓存找到图片后就直接设置
- 缓存中没有找到就通过网络请求图片
- 请求到数据后到主线程中去设置图片
主要文件
RBWebImageProtocol
协议,通过对协议的扩展实现了网络获取图片数据的功能
import Foundation
import UIKit
protocol RBWebImageProtocol {
/// 通过url获取data
/// - Parameters:
/// - url: url
/// - completion: 完成后的回调
func getData(url: URL, completion: ((Data?) -> Void)?)
}
extension RBWebImageProtocol {
/// 通过url获取data
/// - Parameters:
/// - url: url
/// - completion: 完成后的回调
func getData(url: URL, completion: ((Data?) -> Void)? = nil) {
// 创建并行队列异步执行
DispatchQueue(label: "com.RBWebImage.concurrentQueue",attributes:.concurrent).async {
do {
let data: Data = try Data(contentsOf: url)
if data.count > 0 {
completion?(data)
}else {
completion?(nil)
}
} catch _ {
completion?(nil)
}
}
}
}
RBWebImageCach
缓存管理类,缓存相关的所有功能
- RBWebImageMemoryManager 内存缓存管理类
- RBWebImageDiskManager 磁盘缓存管理类
import Foundation
import UIKit
fileprivate func showLog(_ msg: String) {
if isShowRBWebImageLog {
print(msg)
}
}
class RBWebImageCach {
/// 从缓存中获取图片
/// - Parameter urlStr: 图片url
/// - Returns: 缓存中的图片
class func getCacheImage(urlStr: String) -> UIImage? {
let key = getKeyString(urlStr: urlStr)
// 从内存中获取图片
showLog("将去内存中获取缓存:\(urlStr)")
if let cacheImage = RBWebImageMemoryManager.shared.getImage(key: key) {
showLog("内存中找到图片:\(urlStr)")
return cacheImage
}
showLog("内存中没有找到,将去磁盘中获取缓存:\(urlStr)")
// 内存中没有时,从磁盘中获取图片数据
if let data = RBWebImageDiskManager.shared.getData(key: key) {
if let cacheImage = UIImage(data: data) {
showLog("磁盘中找到图片:\(urlStr)")
return cacheImage
}
}
return nil
}
/// 保存图片数据
/// - Parameters:
/// - data: 图片数据
/// - urlStr: 图片url
class func saveImageData(data: Data, urlStr: String) {
let key = getKeyString(urlStr: urlStr)
if let image = UIImage(data: data) {
// 缓存到内存中
showLog("存储到内存中:\(urlStr)")
RBWebImageMemoryManager.shared.saveImage(image: image, key: key)
// 缓存到磁盘中
showLog("存储到磁盘中:\(urlStr)")
RBWebImageDiskManager.shared.saveImage(data: data, key: key)
}else {
showLog("无效数据:\(urlStr)")
}
}
/// 删除所有缓存数据
class func deleteAllCacheData() {
// 删除内存中的所有缓存数据
RBWebImageMemoryManager.shared.deleteAllImage()
// 删除磁盘中所有缓存数据
RBWebImageDiskManager.shared.deleteAllCacheData()
}
class func deleteCacheData(urlStr: String) {
let key = getKeyString(urlStr: urlStr)
// 删除内存中的缓存数据
RBWebImageMemoryManager.shared.deleteImage(key: key)
// 删除磁盘中的缓存数据
RBWebImageDiskManager.shared.deleteCacheData(key: key)
}
/// 获取缓存图片数据的大小
/// - Returns: 图片数据的大小
class func getCacheSize() -> Double {
return RBWebImageDiskManager.shared.getCacheSize()
}
/// 根据url字符串获取缓存用的key
/// - Parameter urlStr: url字符串
/// - Returns: key
private class func getKeyString(urlStr: String) -> String {
var key = urlStr
for s in ":/\\." {
key = key.replacingOccurrences(of: String(s), with: "_")
}
return key
}
}
/// 内存缓存管理类
class RBWebImageMemoryManager {
static let shared = RBWebImageMemoryManager()
private init() {}
private let cache = NSCache<NSString, UIImage>()
private var keys = Array<NSString>()
/// 保存图片到内存中
/// - Parameters:
/// - image: 图片
/// - key: key
func saveImage(image: UIImage, key: String) {
let nKey = key as NSString
if !keys.contains(nKey) {
keys.append(nKey)
}
cache.setObject(image, forKey: nKey)
}
/// 通过key获取内存中的图片
/// - Parameter key: key
/// - Returns: 内存中的图片
func getImage(key: String) -> UIImage? {
return cache.object(forKey: key as NSString)
}
/// 删除内存中的所有图片
func deleteAllImage() {
cache.removeAllObjects()
keys.removeAll()
}
/// 删除key对应的图片
/// - Parameter key: key
func deleteImage(key: String) {
let nKey = key as NSString
cache.removeObject(forKey: nKey)
}
}
/// 磁盘缓存管理类
class RBWebImageDiskManager {
static let shared = RBWebImageDiskManager()
private init() {}
private let cachePath = "\(NSHomeDirectory())/Library/Caches/BGWebImageCache"// 缓存路径
/// 保存数据到磁盘中
/// - Parameters:
/// - data: 数据
/// - key: key
func saveImage(data: Data, key: String) {
let path: String = getFullCachePath(key: key)
if FileManager.default.createFile(atPath: path, contents: data, attributes: nil) {
showLog("保存成功")
}else {
showLog("保存失败")
}
}
/// 通过key获取磁盘中的图片数据
/// - Parameter key: key
/// - Returns: 磁盘中的图片数据
func getData(key: String) -> Data? {
var data:Data?
let path: String = getFullCachePath(key: key)
if FileManager.default.fileExists(atPath: path) {
data = FileManager.default.contents(atPath: path)
}
return data
}
/// 删除磁盘中的所有图片数据
func deleteAllCacheData() {
let fileManager: FileManager = FileManager.default
if fileManager.fileExists(atPath: cachePath) {
do {
try fileManager.removeItem(atPath: cachePath)
} catch {
showLog("删除磁盘缓存异常")
}
}
}
/// 删除key对应的图片数据
/// - Parameter key: key
func deleteCacheData(key: String) {
let path = getFullCachePath(key: key)
let fileManager: FileManager = FileManager.default
if fileManager.fileExists(atPath: path) {
do {
try fileManager.removeItem(atPath: path)
} catch {
showLog("删除磁盘缓存异常")
}
}
}
/// 获取磁盘中图片的总大小
/// - Returns: size
func getCacheSize() -> Double {
let manage = FileManager.default
if !manage.fileExists(atPath: cachePath) {
return 0
}
let childFilePath = manage.subpaths(atPath: cachePath)
var size:Double = 0
for path in childFilePath! {
let fileAbsoluePath = cachePath+"/"+path
size += getFileSize(filePath: fileAbsoluePath)
}
return size
}
/// 通过key获取完整的缓存路径
/// - Parameter key: key
/// - Returns: 完整缓存路径
private func getFullCachePath(key: String) -> String {
let path = "\(cachePath)/\(key)"
let fileManager: FileManager = FileManager.default
if !(fileManager.fileExists(atPath: cachePath)) {
do {
try fileManager.createDirectory(atPath: cachePath, withIntermediateDirectories: true, attributes: nil)
return path
}catch {
showLog("缓存完整路径设置失败")
return ""
}
}
return path
}
/// 获取文件大小
/// - Parameter filePath: 文件路径
/// - Returns: 文件大小
private func getFileSize(filePath: String) -> Double {
let manager = FileManager.default
var fileSize:Double = 0
do {
let attr = try manager.attributesOfItem(atPath: filePath)
// fileSize = Double(attr[FileAttributeKey.size] as! UInt64)
let dict = attr as NSDictionary
fileSize = Double(dict.fileSize())
} catch {
showLog("\(error)")
}
return fileSize
}
}
UIImageView+RBWebImage
UIImageView的扩展,实现UIimageView设置网络图片的功能
import UIKit
let isShowRBWebImageLog = true
extension UIImageView: RBWebImageProtocol {
/// 设置网络图片
/// - Parameters:
/// - url: url
/// - defaultImage: 占位图
/// - isKeepOriginal: 在原来有图片时,是否保持原有的图片(true:不替换占位图,false:每次都替换占位图)
/// - completion: 完成后回调
func rbSetImage(url: URL?, defaultImage: UIImage?, isKeepOriginal: Bool = false, completion: ((UIImage?) -> Void)? = nil) {
showLog("开始设置网络图片:\(url?.absoluteString ?? "")")
if !isKeepOriginal {
self.image = nil
}
let originImage = self.image
if originImage == nil && defaultImage != nil {
self.image = defaultImage!
}
if url == nil {
completion?(nil)
showLog("设置失败:url空")
return
}
// 从缓存中获取图片加载
if let cacheImage = RBWebImageCach.getCacheImage(urlStr: url!.absoluteString) {
self.image = cacheImage
self.showLog("设置成功:\(url!.absoluteString)")
return
}
showLog("缓存中获取图片失败:\(url!.absoluteString)")
// 缓存中没有时通过网络请求图片数据
showLog("开始通过网络请求图片数据:\(url!.absoluteString)")
getData(url: url!) {[weak self] (data) in
if data == nil {
completion?(nil)
self?.showLog("设置失败:网络请求到的数据为空-\(url!.absoluteString)")
}else {
if let ima = UIImage(data: data!) {
// 在主线程更新图片
DispatchQueue.main.async {[weak self] in
self?.image = ima
}
// 缓存图片data
RBWebImageCach.saveImageData(data: data!, urlStr: (url!.absoluteString))
self?.showLog("设置成功:\(url!.absoluteString)")
completion?(ima)
}else {
self?.showLog("设置失败:网络请求到的数据转图片失败-\(url!.absoluteString)")
completion?(nil)
}
}
}
}
/// 设置网络图片
/// - Parameters:
/// - urlStr: url字符串
/// - defaultImageName: 占位图名称
func rbSetImage(urlStr: String, defaultImageName: String?) {
var defauleImage: UIImage? = nil
if defaultImageName != nil {
defauleImage = UIImage(named: defaultImageName!)
}
var url: URL? = nil
if let us = urlStr.urlString() {
url = URL(string: us)
}
rbSetImage(url: url, defaultImage: defauleImage, isKeepOriginal: false, completion: nil)
}
/// 打印日志
/// - Parameter msg: msg 日志信息
private func showLog(_ msg: String) {
if isShowRBWebImageLog {
print(msg)
}
}
}
extension String {
/// 格式化url
/// - Returns: 格式化url字符串
func urlString() -> String? {
if self.count == 0 {
return nil
}
let urlStr = CFURLCreateStringByReplacingPercentEscapes(kCFAllocatorDefault, self as CFString, "!$&'()*+,-./:;=?@_~%#[]" as CFString) as String?
if urlStr == nil || urlStr?.count == 0 {
return nil
}
return urlStr
}
}
参考代码:github