美文网首页
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