如何使用Kingfisher展示图片?

官方Demo中使用setImage展示图片

// source: Kingfisher-Demo > ViewController.swift_ = (cell as! CollectionViewCell).cellImageView.kf.setImage(with: url,placeholder: nil,options: [.transition(ImageTransition.fade(1))],progressBlock: { receivedSize, totalSize inprint("\(indexPath.row + 1): \(receivedSize)/\(totalSize)")},completionHandler: { image, error, cacheType, imageURL inprint("\(indexPath.row + 1): Finished")
})
复制代码

为什么要使用kf ?

Kingfisher.swift

KingfisherCompatible协议有个只读属性kf,它返回Kingfisher。Demo中的cellImageView.kf即为Kingfisher(ImageView)

//source: Kingfisher > Kingfisher.swiftpublic protocol KingfisherCompatible {associatedtype CompatibleTypevar kf: CompatibleType { get }
}public extension KingfisherCompatible {public var kf: Kingfisher<Self> {return Kingfisher(self)}
}复制代码

kingfisher中,对不同的种类可以进行各种不同的操作,比如对Image,你能对它设置图片、设置动画;对UIButton的不同状态设置图片;还有ImageView、WKInterfaceImage(watchOS中)。我们把这些不同的种类归位Base(泛型),它可以是Image,也可以是UIButton。不可继承的类Kingfisher就有属性Base,初始化时传入一个Base

//source: Kingfisher > Kingfisher.swiftpublic final class Kingfisher<Base> {public let base: Basepublic init(_ base: Base) {self.base = base}
}
复制代码
//source: Kingfisher > Kingfisher.swiftextension Image: KingfisherCompatible { }
复制代码

在Kingfisher.swift中,让ImageView实现KingfisherCompatible协议。

ImageView+Kingfisher.swift

在ImageView+Kingfisher.swift中,定义当BaseImageView的时候,给Kingfisher拓展方法,例如setImage方法。

//source: Kingfisher > Extensions > ImageView+Kingfisher.swiftextension Kingfisher where Base: ImageView public func setImage(with resource: Resource?,placeholder: Placeholder? = nil,options: KingfisherOptionsInfo? = nil,progressBlock: DownloadProgressBlock? = nil,completionHandler: CompletionHandler? = nil) -> RetrieveImageTask{// 代码太长,此处省略}复制代码

setImage方法主要做了以下事:

1. 如果resource不存在,比如没有传入图的url,则给一个默认的placeholder(占位图)。

2. 如果resource存在,显示加载动画(若存在),调用KingfisherManagerretrieveImage方法,在拉取数据过程中,实时回调当前已收到的资源数据和总数据;完成后,主线程异步变化UI,不再显示加载动画,没有收到image就回调错误,有收到image就将base(此处为UIImageView)的image设为返回的image。非macOS平台可设置相关动画。

KingfisherManager.swift

该类管理了Kingfisher的下载和缓存,可以使用这个类来从网络URL地址或者缓存获取图片,retrieveImage方法。

//source: Kingfisher > Core > KingfisherManager.swift@discardableResult
public func retrieveImage(with resource: Resource,options: KingfisherOptionsInfo?,progressBlock: DownloadProgressBlock?,completionHandler: CompletionHandler?) -> RetrieveImageTask
{let task = RetrieveImageTask()let options = currentDefaultOptions + (options ?? KingfisherEmptyOptionsInfo)if options.forceRefresh {_ = downloadAndCacheImage(with: resource.downloadURL,forKey: resource.cacheKey,retrieveImageTask: task,progressBlock: progressBlock,completionHandler: completionHandler,options: options)} else {tryToRetrieveImageFromCache(forKey: resource.cacheKey,with: resource.downloadURL,retrieveImageTask: task,progressBlock: progressBlock,completionHandler: completionHandler,options: options)}return task
}
复制代码

1. 新建获取图片的任务taskRetrieveImageTask类型),并读取配置optionsKingfisherOptionsInfo类型)

2. 如果配置中forceRefresh(永远从网络读取图片数据)为真,则调用本类中的downloadAndCacheImage方法,其使用ImageDownloaderdownloadImage下载图片,下载完成后,如果返回的错误是notModified,尝试从targetCache读取已缓存的图片;如果下载成功则使用targetCache(ImageCache)类型的store方法缓存图片,然后如果配置中cacheOriginalImage为真且当前processor不是DefaultImageProcessor,使用originalCache缓存图片。

3. 如果配置中forceRefresh为假,则调用本类中的tryToRetrieveImageFromCache方法,其先从缓存targetCache里找,image存在则返回;如果image不存在,且正在使用DefaultImageProcessor,则去下载图片;如非使用DefaultImageProcessor,则用originalCache去看看原始图片是否已在缓存中,如果有则不再下载,直接用processor处理图片,使用targetCache存储再返回。

ImageCache.swift

//source: Kingfisher > Core > ImageCache.swiftopen func store(_ image: Image,original: Data? = nil,forKey key: String,processorIdentifier identifier: String = "",cacheSerializer serializer: CacheSerializer = DefaultCacheSerializer.default,toDisk: Bool = true,completionHandler: (() -> Void)? = nil){// 代码太长,此处省略}
复制代码

store方法给memoryCache设置key对应的image,如果toDisk(写入磁盘)为真,ioQueue异步在cache文件夹下根据keydata创建缓存文件(如果cache文件夹不存在先创建一个cache文件夹)。主线程异步处理completionHandler

ImageDownloader.swift

//source: Kingfisher > Core > ImageDownloader.swiftopen class ImageDownloader {class ImageFetchLoad {var contents = [(callback: CallbackPair, options: KingfisherOptionsInfo)]()var responseData = NSMutableData()var downloadTaskCount = 0var downloadTask: RetrieveImageDownloadTask?var cancelSemaphore: DispatchSemaphore?}var fetchLoads = [URL: ImageFetchLoad]()//其他属性省略
}复制代码

ImageDownloader类是根据URL从服务器下载图片的下载管理器,在fetchLoads中,不同的URL对应不同的ImageFetchLoadImageFetchLoad包含一个URL中下载的内容(下载进度、处理回调、配置)、返回数据、下载任务数、下载任务、取消信号量。

//source: Kingfisher > Core > ImageDownloader.swift@discardableResult
open func downloadImage(with url: URL,retrieveImageTask: RetrieveImageTask? = nil,options: KingfisherOptionsInfo? = nil,progressBlock: ImageDownloaderProgressBlock? = nil,completionHandler: ImageDownloaderCompletionHandler? = nil) -> RetrieveImageDownloadTask?{// 代码太长,此处省略}复制代码

downloadImage方法中,使用URL和配置option下载图片。使用URL作为loadkey,要开始进程之前要向requestModifier拿到最终的URL。可以在该方法中设置下载的timeout(超时)时长、是否使用HTTP pipelining。(使用HTTP pipelining则允许不必等到response, 就可以再次请求,会很大的提高网络请求的效率)。如果URLretrieveImageTaskmodifier有误,则在回调中显示错误信息,并返回nil。如果均无误,则调用该类的setup方法,设置并开启下载任务,fetchLoad下载任务数加一,retrieveImageTaskdownloadTask设为setup方法回调中的fetchLoaddownloadTask,后返回下载任务。

//source: Kingfisher > Core > ImageDownloader.swiftfunc setup(progressBlock: ImageDownloaderProgressBlock?, with completionHandler: ImageDownloaderCompletionHandler?, for url: URL, options: KingfisherOptionsInfo?, started: @escaping ((URLSession, ImageFetchLoad) -> Void)) {// 代码太长,此处省略}复制代码

setup方法中,如果URL传入fetchLoad方法返回的ImageFetchLoad存在且当前下载的任务数为0,则【一直等待到下载失败(下载失败会在该类的callCompletionHandlerFailure方法释放cancelSemaphore信号量),再尝试下载(调用该方法中的prepareFetchLoad函数)。】反之:尝试下载(调用prepareFetchLoad函数)。

prepareFetchLoad函数中,开启barrier(等待写入完成后才能读取)同步线程:1.拿到url对应的ImageFetchLoad(若空创建新的一个); 2.并对其content加上一条:callbackPair(由传入的progressBlockcompletionHandler所组成)以及当前配置(KingfisherOptionsInfoItem);3.最后组合成的新ImageFetchLoad替换旧的url对应的ImageFetchLoad。4.session存在则开启加载url对应图片的session

//source: Kingfisher > Core > ImageDownloader.swiftfunc fetchLoad(for url: URL) -> ImageFetchLoad? {var fetchLoad: ImageFetchLoad?barrierQueue.sync(flags: .barrier) { fetchLoad = fetchLoads[url] }return fetchLoad
}
复制代码

fetchLoad方法中,开启一个DispatchWorkItemFlagsbarrier的线程barrierQueue同步执行以下操作:根据url拿到fetchLoads里对应的fetchLoad,并返回fetchLoad

//source: Kingfisher > Core > ImageDownloader.swiftfunc urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {guard let url = task.originalRequest?.url else {return}if let downloader = downloadHolder {downloader.delegate?.imageDownloader(downloader, didFinishDownloadingImageForURL: url, with: task.response, error: error)}guard error == nil else {callCompletionHandlerFailure(error: error!, url: url)return}processImage(for: task, url: url)
}private func processImage(for task: URLSessionTask, url: URL) {// 代码太长,此处省略}复制代码

urlSession(_:task:didCompleteWithError)方法中,下载失败则使用该类callCompletionHandlerFailure方法处理失败结果,下载成功则使用processImage(for:url:)方法处理数据并执行回调,缓存图片数据并刷新界面。开启异步processQueue(任务并行运行),使用fetchload中的responseData得到data。若downloader存在delegate,可在创建UIImage之前对从url成功下载的raw image data再次处理,例如解密(decrypting)或验证(verification)得到data。缓存处理过的图片数据,如果使用相同的processor,就不用重复处理该图了。

KingfisherOptionsInfo.swift

KingfisherOptionsInfo.swift文件定义了一些配置。包括缓存,下载优先级,是否每次重新下载图片,是否只从缓存加载图片,在发送下载请求前使用的modifierImageDownloadRequestModifier类型),下载完的图片处理器processorImageProcessor类型),在图片显示前修改图片的imageModifierImageModifier类型)等。

ImageProcessor.swift

ImageProcessor是一个协议,将下载的数据转换为图片。

DefaultImageProcessor是默认的Processor,遵循ImageProcessor协议,将输入的数据变为有效的图片,支持PNG,JPEG,GIF等格式的图片,如果有ImageDefaultImageProcessor会返回图片;有Data,就将data转为Image

BlendImageProcessor为图片添加blend模式。

ImageTransition.swift

可以通过ImageTransition.swift文件来为已下载图片显示添加动画,例如渐入、从上下左右进入等动画,也可自定义动画。

转载于:https://juejin.im/post/5b72a2b06fb9a009a44a9089

Kingfisher 4.x 从使用到源码相关推荐

  1. Kingfisher源码分析

    源码地址: https://github.com/onevcat/Kingfisher Kingfisher是iOS圈内有名的王威写的三方库,参考SDWebImage用Swift语言实现.  三方图片 ...

  2. 逗视 iOS客户端应用源码

    逗视 iOS客户端 支持iPhone,iPad 最新版本1.2.2 简介 在这一个高速运转的社会中,大家真的太忙了,没有了欢笑,没有了生活! 但是我们生活中不能缺少欢乐,搞笑!那么,逗视来了!! 你可 ...

  3. 高仿最美应用项目源码

    源码下载: http://code.662p.com/view/12434.html 用到的第三方库 AFNetworking : OC的网络请求库 YYWebImageView : YY大神的图片加 ...

  4. 已上线运营的斗地主APP项目源码,出售(超低价)----(Android商业项目客户端)

    已上线运营的斗地主APP商业项目源码,低价售于热爱学习的同行.您只需支付167元,即可拥有这套商业源码,提高您的技术开发能力.增色您的项目经历.增涨您的工资收入. 您可以运行此商业项目APK,尝试客户 ...

  5. 查看Hotspot源码,查看java各个版本源码的网站,如何查看jdk源码

    java开发必知必会之看源码,而看源码的第一步则是找到源码

  6. 【Golang源码分析】Go Web常用程序包gorilla/mux的使用与源码简析

    目录[阅读时间:约10分钟] 一.概述 二.对比: gorilla/mux与net/http DefaultServeMux 三.简单使用 四.源码简析 1.NewRouter函数 2.HandleF ...

  7. liunx上mysql源码安装mysql,搞定linux上MySQL编程(一):linux上源码安装MySQL

    [版权声明:尊重原创,转载请保留出处:blog.csdn.net/shallnet,文章仅供学习交流,请勿用于商业用途] 1. 首先下载源码包: ftp://ftp.jaist.ac.jp/pub/m ...

  8. java调用clang编译的so_写Java这么久,JDK源码编译过没?编译JDK源码踩坑纪实

    好奇害死羊 很多小伙伴们做Java开发,天天写Java代码,肯定离不开Java基础环境:JDK,毕竟我们写好的Java代码也是跑在JVM虚拟机上. 一般来说,我们学Java之前,第一步就是安装JDK环 ...

  9. Go 源码里的这些 //go: 指令,go:linkname 你知道吗?

    原文地址: Go 源码里的这些 //go: 指令,你知道吗? 一文解惑 //go:linkname 指令

最新文章

  1. 3dmax导出fbx时如何带贴图_houdini | 第一章 第三节 贴图与顶点动画
  2. SDN/NFV:现状,挑战和未来
  3. Apache2 httpd.conf 配置详解(一)
  4. 计算智能-群智能算法-粒子群算法matlab实现
  5. 王力宏《十八般武艺》全部歌曲试听
  6. OpenStack、Docker、KVM被评为最火的云开源项目
  7. Qt中查看ui_xxx.h文件方法
  8. 2018.7.28 二叉树的遍历规则(前序遍历、后序遍历、中序遍历)
  9. (C)libnet-发送arp/tcp/icmp数据包
  10. 北漂的程序员们,这寒冬你是否“有枝可依”?
  11. Define a New Server 没有tomcat选项
  12. PowerShell 远程连接与其它技巧
  13. Netty 源码分析之 零 磨刀不误砍柴工 源码分析环境搭建
  14. PAT甲之初窥门径(上)
  15. Latex在footnote或在bib中插入链接时出现波浪号~的解决方案
  16. 如何查看ps图片中边框的背景颜色
  17. 融合通讯四大关键词和三个应用场景
  18. java使用poi实现excel保护工作表实例代码(支持.xls和.xlsx)
  19. SET NOCOUNT { ON | OFF }
  20. win10 python3.8.10下ipython无响应处理

热门文章

  1. 第二期:如何通过知晓云快速生成分享海报
  2. arm spi 接口概述
  3. SPI接口的FPGA实现(一)——SPI接口的相关基础知识
  4. python-时间序列之ADF检验(1)
  5. web自动化如何在不同浏览器运行_2020自动化测试岗位需求的7项必备技能(更新版)...
  6. 2021世界移动通信大会 | 5G时代下,苏宁如何看待未来零售发展?
  7. Pytorch 常用 optimizer
  8. Flashlight App Android示例
  9. 流形学习(一)LLE 在 MATLAB 中的实现及实例
  10. Minecraft Forge API 类帮助文档(1.12.2)