美文网首页
Kingfisher下载多张图片

Kingfisher下载多张图片

作者: liang1991 | 来源:发表于2020-01-10 12:09 被阅读0次

提到Kingfisher相信使用swift做日常开发的同学应该都知道,喵神开源的一个十分强大易用的网络图片加载库。但是只提供了单张图片加载的方法,嵌套加载代码结构不好看效率也低,使用信号量或者group也会加不少代码,这里使用gcd group做了一个简单的封装。

一、老生长谈的group enter、group level

由于Kingfisher里图片加载都是异步操作,我们希望可以监测到所有图片都加载完毕然后给一个回调,这其实就是多个异步任务的同步处理操作,而iOS里比较常用的也就是gcd里的信号量和group enter、group level了,关于这些知识网络上已经有比较多的资料了,这里就不再赘述了。废话不多说直接上代码。

  1. 单张图片加载方法,这里我们使用的是retrieveImage方法,这个方法会先从内存/硬盘缓存里查找图片,如果找到会直接返回,找不到则去加载并缓存图片资源。
 static func downloadWith(urlStr: String, complete: ((UIImage?) -> ())? = nil) {
        if let url = URL(string: urlStr) {
            KingfisherManager.shared.retrieveImage(with: url) { (result) in
                switch result {
                case .success(let imgResult):
                    complete?(imgResult.image)
                case .failure(let error):
                    print(error)
                    complete?(nil)
                }
            }
        } else {
            complete?(nil)
        }
    }
  1. 今天的\color{#FF3030}{猪脚}多张图片加载,这里使用的是dispatchgroup的enter、level来进行同步处理的。(我这边的需求是只要一张下载失败就直接返回,但是没有找到提前终止group或者释放的相关方法,这里处理是如果有下载失败直接回调,并将闭包置空)
 static func downloadWith(urlStrArray: [String], complete: @escaping AIKingfisherDownLoadResultBlock) {
        let group = DispatchGroup()
        let queue = DispatchQueue.main
        var imgDic = [String: UIImage]()
        var downLoadCount = 0
        var block: AIKingfisherDownLoadResultBlock? = complete
        for urlStr in urlStrArray {
            group.enter()
            queue.async(group: group) {
                AIKingfisher.downloadWith(urlStr: urlStr) { (image) in
                    if let img = image {
                        imgDic.updateValue(img, forKey: urlStr)
                        downLoadCount = downLoadCount + 1
                    } else { //有一个下载失败就提前终止
                        block?(nil)
                        block = nil
                    }
                    group.leave()
                }
            }
        }
        group.notify(queue: queue) {
            if downLoadCount != urlStrArray.count {
                block?(nil)
            } else {
                block?(AIKingfisherDownLoadResult(imgDic: imgDic))
            }
        }
    }

3、另外一种写法(使用group.wait设置超时时间,但要注意这个方法会阻塞当前线程所以不能放在主线程调用)

/// Kingfisher多图下载
    /// - Parameter urlStrArray: 图片链接str数组
    /// - Parameter timeout: 超时时间
    /// - Parameter complete: 回调
    static func downloadWith(urlStrArray: [String], timeout: TimeInterval = 10, complete: @escaping (AIKingfisherDownLoadResult?) -> ()) {
        let group = DispatchGroup()
        let queue = DispatchQueue.main
        var imgDic = [String: UIImage]()
        var downLoadCount = 0
        for urlStr in urlStrArray {
            group.enter()
            queue.async(group: group) {
                AIKingfisher.downloadWith(urlStr: urlStr) { (image) in
                    if let img = image {
                        imgDic.updateValue(img, forKey: urlStr)
                        downLoadCount = downLoadCount + 1
                    }
                    group.leave()
                }
            }
        }
        DispatchQueue.global().async {
            let result = group.wait(timeout: DispatchTime.now() + timeout)
            DispatchQueue.main.async {
                switch result {
                case .success:
                    if downLoadCount != urlStrArray.count {
                        complete(nil)
                    } else {
                        complete(AIKingfisherDownLoadResult(imgDic: imgDic))
                    }
                case .timedOut:
                    AILog("下载超时")
                    complete(nil)
                }
            }
        }
    }
二、Kingfisher里的kf是怎么回事

由于swift里是有命名空间的(默认是product name),所以大家在swift为了防止命名冲突不再像以前oc里加前缀了,而是使用kf、rx这种对对象做一层包装然后调用方法。(除了命名问题,也很好的做了隔离比如kf.setimage并不是给UIIMageView、UIButton添加了扩展方法...)

  1. 定义包装类型(添加类型泛型约束以使用所有类型)
    包装后的类型要能获取到原始类型以做处理,这里定义了成员变量base(被包装对象实例调用实例方法),baseType(被包装对象类型调用静态方法)
struct AIWrapper<Base> {
    public let base: Base
    static var baseType: Base.Type {
        return Base.self
    }
    public init(_ base: Base) {
        self.base = base
    }
}
  1. 定义协议添加计算型成员变量来实现kf、rx...
protocol AICompatible : AnyObject {}

extension AICompatible {
    public var ai: AIWrapper<Self> {
        return AIWrapper(self)
    }
    
    static var ai: AIWrapper<Self>.Type {
        return AIWrapper<Self>.self
    }
}
  1. 比葫芦画瓢其实很容易理解,这里我加了个类型包装可以调用静态方法。
extension UIImageView: AICompatible {}
extension UIButton: AICompatible {}

extension AIWrapper where Base: UIImageView {
    func setImage(imgUrl: String?, placeHolder: UIImage? = nil) {
        if let urlStr = imgUrl, let url = URL(string: urlStr) {
            self.base.kf.setImage(with: url, placeholder: placeHolder, options: [.transition(.fade(0.2))])
        }
    }
    
    static func imageViewTestFunc() {
        print(baseType)
    }
}

使用类似于下面的例子

  img.ai.setImage(imgUrl: url1)
  UIImageView.ai.imageViewTestFunc()

4、详见MBlogDemo/KingfisherDemo

相关文章

网友评论

      本文标题:Kingfisher下载多张图片

      本文链接:https://www.haomeiwen.com/subject/gjepactx.html