Kingfisher的基本使用(一)

序言

Kingfisher 是一个下载、缓存网络图片的轻量级纯swift库, 作者@王巍自称是受著名三方库SDWebImage激励所写,一年多以来,该库深受广大iOS之swift开发者所喜爱,目前被很多iOS开发者应用在app中。在swift中它真的是一个SDWebImage的升级版,作为swift开发者来说,为了摒弃Objective-C的风格,甚至“断绝”与Objective-C的关系,使工程更swift化,我们更希望更喜欢使用纯净的swift来开发自己的app。在此,也非常感谢@喵神给众多开发者提供了很大的便利,
为业界开发者所做的无私贡献。

github: [Kingfisher] (https://github.com/onevcat/Kingfisher)

特征

  • 异步下载和缓存图片
  • 基于networkingURLSession, 提供基础的图片处理器和过滤器
  • 内存和磁盘的多层缓存
  • 可撤销组件,可根据需要分开地使用下载器和缓存系统
  • 必要时可从缓存中读取并展示图片
  • 扩展UIImageViewNSImageUIButton来直接设置一个URL图片
  • 设置图片时,内置过渡动画
  • 支持扩展图片处理和图片格式

先看一个Kingfisher的基本用法:从网络上设置一张基本的图片

let url = URL(string: "http://mvimg2.meitudata.com/55fe3d94efbc12843.jpg")
imageView.kf.setImage(with: url)

美女跃然屏上(模拟器截图)

Kingfisher从url下载图片,将图片存储在内存缓存和磁盘缓存,然后将它展示在imageView上。当使用同样的代码后(url不变),就会直接从内存中获取之前缓存的图片并立即显示出来。

要求

  • iOS 8.0+ / macOS 10.10+
  • Swift 3(Kingfisher 3.x), Swift 2.3(Kingfisher 2.x)

Kingfisher 3.0迁移指导 : 从早期版本升级到Kingfisher 3.0以上

集成

注:这里只介绍CocoaPods的集成方式

source 'https://gitgub.com/CocoaPods/Specs.git'
platform :ios, '8.0'

target '你的工程名' do
use_frameworks!
    pod 'Kingfisher', '~> 3.0'
end

then

pod install

基本使用

直接设置一张url图片

if let url = URL(string: "http://mvimg2.meitudata.com/55fe3d94efbc12843.jpg") {
    imgView.kf.setImage(with: url)
}

诠释:使用直接设置的方法,Kingfisher首先会尝试从缓存中去取,如果没有,则直接下载图片并且缓存下来以备后用。此外,Kingfisher默认使用absoluteString of url(即绝对url)作为cacheKey以方便再次加载该图片的时候去缓存中根据cacheKey(也就是图片url)查找,通俗来说就是把图片的整个链接作为cacheKey来缓存在本地。

指定一个cacheKey缓存图片

let imageResource = ImageResource(downloadURL: url, cacheKey: "Custom_cache_key")
imageView.kf.setImage(with: imageResource)

设置占位符图片

Kingfisher中,为了防止加载网络图片失败,提供了一个设置占位符图片功能,也就是说,当网络加载过程中或者图片加载失败时,就使用自定义的默认图片来代替显示。

if let url = URL(string: "http://mvimg2.meitudata.com/55fe3d94efbc12843.jpg") {
    imageView.kf.setImage(with: url, placeholder: placeholder_image, options: nil, progressBlock: nil, completionHandler: nil)
}

下载完成回调

if let url = URL(string: "http://mvimg2.meitudata.com/55fe3d94efbc12843.jpg") {
    imageView.kf.setImage(with: url, placeholder: nil, options: nil, progressBlock: nil, completionHandler: { (image, error, cacheType, imageUrl) in
        
        image       // 为 nil 时,表示下载失败
        
        error       // 为 nil 时,表示下载成功, 非 nil 时,就是下载失败的错误信息
        
        cacheType   // 缓存类型,是个枚举,分以下三种:
                    // .none    图片还没缓存(也就是第一次加载图片的时候)
                    // .memory  从内存中获取到的缓存图片(第二次及以上加载)
                    // .disk    从磁盘中获取到的缓存图片(第二次及以上加载)
        
        imageUrl    // 所要下载的图片的url
        
    })
}

加载菊花

Kingfisher提供了一个在下载图片过程中显示加载菊花的功能,图片加载成功后菊花自动消失,可以通过设置indicatorType来显示菊花

IndicatorType是一个枚举

public enum IndicatorType {
    /// 默认没有菊花
    case none
    /// 使用系统菊花
    case activity
    /// 使用一张图片作为菊花,支持gif图
    case image(imageData: Data)
    /// 使用自定义菊花,要遵循Indicator协议
    case custom(indicator: Indicator)
}

设置方式 1 : 使用系统菊花

imageView.kf.indicatorType = .activity
imageView.kf.setImage(with: url)

演示效果(加载的是我微博上的一张图片)

设置方式 2 :使用gif图作为加载菊花

let path = Bundle.main.path(forResource: "myImage", ofType: "gif")!
let data = try! Data(contentsOf: URL(fileURLWithPath: path))
imageView.kf.indicatorType = .image(imageData: data)
imageView.kf.setImage(with: url)

演示效果

设置方式 3 :自定义菊花,遵循 Indicator协议


let myIndicator = CustomIndicator()
imageView.kf.indicatorType = .custom(indicator: myIndicator)
imageView.kf.setImage(with: url)

struct CustomIndicator: Indicator {
    
    var view: IndicatorView = UIView()
    
    func startAnimatingView() {
        view.isHidden = false
    }
    
    func stopAnimatingView() {
        view.isHidden = true
    }
    
    init() {
        view.backgroundColor = UIColor.magenta
    }
}

诠释:Indicator是一个协议,如果我们要实现自定义菊花加载,只需要遵循这个协议即可,另外必须得说的是,Indicator协议中有一个属性viewCenter我们不需要在遵循协议的时候去实现,因为喵神已经为我们提供了默认实现(我还因为这个问题在github上问过他,突然觉得好蠢...)

设置方式 4 : 根据实时下载图片的数据做进度条加载或者菊花加载(灵活,比例为:图片已下载数据 / 图片总数据)

imageView.kf.setImage(with: url, placeholder: nil, options: nil, progressBlock: { (receivedData, totolData) in

    let percentage = (Float(receivedData) / Float(totolData)) * 100.0
    
    print("downloading progress is: \(percentage)%")
    
    // 这里用进度条或者绘制view都可以,然后根据 percentage% 表示进度就行了
    
}, completionHandler: nil)

设置过渡动画,渐变效果(图片下载结束后)

imageView.kf.setImage(with: url, placeholder: nil, options: [.transition(.fade(0.4))], progressBlock: nil, completionHandler: nil)

诠释: 在Kingfisher中,有一个options, 我们可以做一些选择,Kingfisher里面目前包含了18种选择,在图片下载结束后,为了实现一些效果,我们可以实现上面的渐变效果,或者还有更多的翻滚效果! 但是Kingfisher,上面的这个过渡动画值只是针对图片是从web下载时调用,如果是从内存或磁盘中取时是不会有这个效果的,虽然里面也有一个枚举值强制动画forceTransition,但是似乎暂时作者还没有实现这个功能。

设置图片模糊效果

let processor = BlurImageProcessor(blurRadius: 25)
imageView.kf.setImage(with: url, placeholder: nil, options: [.processor(processor)], progressBlock: nil, completionHandler: nil)

诠释:还有很多其他选择,可自行根据需要设置。

强制下载(跳过缓存,直接从web下载图片)

imageView.kf.setImage(with: url, placeholder: nil, options: [.forceRefresh], progressBlock: nil, completionHandler: nil)

强制从缓存中获取图片(缓存中没有也不会从网络下载)

imageView.kf.setImage(with: url, placeholder: nil, options: [.onlyFromCache], progressBlock: nil, completionHandler: nil)

给按钮(UIButton)设置图片或背景图片

同设置UIImageView一样, 我们也可以使用Kingfisher来给UIButton设置图片, 用法相同:

button.kf.setImage(with: url, for: .normal, placeholder: nil, options: nil, progressBlock: nil, completionHandler: nil)
button.kf.setBackgroundImage(with: url, for: .normal, placeholder: nil, options: nil, progressBlock: nil, completionHandler: nil)

缓存 和 下载器

Kingfisher 由两个主要组件组成:下载图片的图片下载器ImageDownloader、操作缓存的图片缓存ImageCache。我们也可以单独使用其中任意一个。

  • 使用 ImageDownloader 下载图片(不可以缓存图片)
ImageDownloader.default.downloadImage(with: url, retrieveImageTask: nil, options: nil, progressBlock: { (receivedData, totalData) in
    receivedData     // 已接收图片数据
    totalData           // 图片总大小
}, completionHandler: { (image, error, url, data) in
    image       // 图片
    error       // 下载失败时的错误信息
    url         // 所要下载的图片的url
    data        // 下载完成后的总数据
})
  • 使用 ImageCache存储获取图片

存储图片:

let image: UIImage = ...
ImageCache.default.store(image, forKey: "Specified_Keys")

获取图片:


// 获取缓存检查结果
let cacheCheckResult = ImageCache.default.isImageCached(forKey: "Specified_Keys")
print("cacheCheckResult is \(cacheCheckResult)")

// 从磁盘获取图片
let img1 = ImageCache.default.retrieveImageInDiskCache(forKey: "Specified_Keys")
if let image = img1 {
   imageView.image = image
}
print("Image1 in disk cache is \(String(describing: img1))")

// 从磁盘获取图片
let img2 = ImageCache.default.retrieveImageInDiskCache(forKey: "Specified_Keys", options: [.transition(.fade(0.5))])
print("Image2 in disk cache is \(String(describing: img2))")

// 从内存获取图片
let image1 = ImageCache.default.retrieveImageInMemoryCache(forKey: "Specified_Keys")
print("image1 in memory cache is \(String(describing: image1))")

// 从内存获取图片
let image2 = ImageCache.default.retrieveImageInMemoryCache(forKey: "Specified_Keys", options: [.transition(.fade(0.5))])
print("image2 in memory cache is \(String(describing: image2))")

// 直接获取图片(获取后才知道是 memory 还是 disk)
ImageCache.default.retrieveImage(forKey: "Specified_Keys", options: nil) { (image, cacheType) in
   print("image is \(String(describing: image))")
   print("cacheType is \(cacheType)")
   self.imageView.image = image
}

演示效果:

Markdown

打印结果:

test
cacheCheckResult is CacheCheckResult(cached: true, cacheType: Optional(Kingfisher.CacheType.memory))
Image1 in disk cache is Optional(<UIImage: 0x600000281b80>, {2448, 3264})
Image2 in disk cache is Optional(<UIImage: 0x600000281cc0>, {2448, 3264})
image1 in memory cache is Optional(<UIImage: 0x600000280910>, {2448, 3264})
image2 in memory cache is Optional(<UIImage: 0x600000280910>, {2448, 3264})
image is Optional(<UIImage: 0x600000280910>, {2448, 3264})
cacheType is memory

分析: 从结果分析来看,但你使用Kingfisher来存储图片时,默认是存储在memorydisk; 第一次run, 先是从内存中获取图片(此时内存中和磁盘中都有缓存);若我们不卸载模拟器上的app, 然后再次运行模拟器,也即是杀死进程,那么我们看到的将是从disk中获取图片,因为在杀死进程后,内存中的缓存被清空(回收内存),只有磁盘中有缓存; 若接着再次获取缓存图片(不杀死进程),那么我们又看到是从memory中获取到的图片,因为第一次从disk中获取图片后,就会将disk中的缓存图片放在内存中进行缓存,在不杀死进程的情况下,会直接从内存中获取!!!看一下下面的再次演示:

Markdown

当然,我们也可以直接指定是需要缓存到磁盘还是内存

ImageCache.default.store(image,                      // 图片
                         original: data,             // 原图片的data数据(Kingfisher推荐有,原因我后续有空会说)
                         forKey: "Specified_Keys",   // 指定key
                         processorIdentifier: "",    // 处理器标识符(使用处理器处理图片时,把这个标识传给它),标识会用来给 key 和 processor 的组合产生一个对应的key)
                         cacheSerializer: DefaultCacheSerializer.default,   // 缓存序列化,这里默认
                         toDisk: false,              // 是否缓存到disk(磁盘)
                         completionHandler: nil)     // 完成回调

移除缓存图片

// 移除 key 为 Specified_Keys 的缓存图片,从内存和磁盘中都移除
ImageCache.default.removeImage(forKey: "Specified_Keys")

// 只清理内存中的缓存
ImageCache.default.removeImage(forKey: "Specified_Keys", processorIdentifier: "", fromDisk: false, completionHandler: nil)

限制(设置)磁盘的最大缓存(单位:字节)

// 设置磁盘的最大缓存容量为 10 M, 默认是0,表示无限制
ImageCache.default.maxDiskCacheSize = 10 * 1024 * 1024

获取(计算)当前磁盘缓存大小

ImageCache.default.calculateDiskCacheSize { (usedDiskCacheSize) in
    print("usedDiskCacheSize is \(usedDiskCacheSize)")
}

手动清理缓存

  • 立即清除内存缓存
ImageCache.default.clearMemoryCache()
  • 清除磁盘缓存(异步操作)
ImageCache.default.clearDiskCache()
  • 清除过期或超过磁盘缓存大小的缓存(异步操作)
ImageCache.default.cleanExpiredDiskCache()

注意:当你的app接收到内存警告(memory warning)的时候,Kingfisher会净化内存缓存,也就是说,在需要的时候,Kingfisher会主动清理一些已过期或者超过缓存尺寸大小的缓存,因此一般而言,没有必要自己手动去清理缓存,而这些清理缓存方法的存在主要在于以防你想要用户有更多对缓存进行操作的情况。比如有时候,有部分app习惯在设置里面加一个清理缓存的交互,为了方便,你可以根据需要调用这些手动清理缓存的方法。

设置存储在磁盘中的最长的缓存持续时间

// 5天后磁盘缓存将过期
ImageCache.default.maxCachePeriodInSecond = 5 * 24 * 60 * 60

注意:默认是一个周(即7天),单位是 秒。必须注意的是,如果设置成一个负值(比如 -1 )那么磁盘缓存决不会过期!

将一个默认路径扩展添加到每个缓存文件

// set a default path extension
KingfisherManager.shared.cache.pathExtension = "jpg"

注意:如果你是在macOS上使用Kingfisher并且想要添加拖放的操作的时候特别有用,大多数web输入字段不接受没有路径扩展的文件。什么意思呢?比如,你想在一个可拖进图片进行识别的表单中拖拽一张没有后缀.jpgpng的格式图片进去,实际上你操作的时候表单框是拒绝你的,你无法拖进去,如果你加了图片后缀识别名jpg等后,就可以拖进去了,这就是这个方法的莫大好处,当然,如果你是iOS客户端开发,就忽略这个功能,因为作者说了,这个实在macOS桌面应用上特别有用。

为默认图片下载器设置超时持续时间

// 设置成30秒,默认是15秒
ImageDownloader.default.downloadTimeout = 30

好了,到此为止,对于Kingfisher的基本使用介绍差不多了,后面还有很多方法,包括自定义下载器和缓存、取消下载任务或获取任务、在发送前修改请求等等 我们将在后面陆续介绍,对于喜欢Kingfisher的小伙伴们也可以自己深入研究,基本的使用设置事实上很简单,但尽管如此,Kingfisher中的很多地方还是很值得一探究竟的。

欢迎加入 iOS(swift)开发互助群:QQ群号:558179558, 相互讨论和学习!

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,185评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,445评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,684评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,564评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,681评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,874评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,025评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,761评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,217评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,545评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,694评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,351评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,988评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,778评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,007评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,427评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,580评论 2 349

推荐阅读更多精彩内容