iOS 开发每天分享优质文章swift学习

Kingfisher使用方法

2023-03-08  本文已影响0人  霸_霸霸

1. 调用方法解析

public func setImage(
        with resource: Resource?,
 placeholder: Placeholder? = nil
    options: KingfisherOptionsInfo? = nil,
 completionHandler: ((Result<RetrieveImageResult, KingfisherError>) -> Void)? = nil) -> DownloadTask?

参数:

使用案例:

guard let url = URL(string: "https://pic.huitu.com/res/20221125/754309_20221125075727864209_1.jpg") else {
    return
}

let originalCache = ImageCache(name: "original_Cache")
let targetCache = ImageCache(name: "target_Cache")

let processor = RoundCornerImageProcessor(cornerRadius: 50) |> BlurImageProcessor(blurRadius: 5)

let options: [KingfisherOptionsInfoItem] = [
        .transition(.flipFromBottom(0.3)),          // 从底部翻转动画
        .forceTransition,                           // 每次都要执行翻转动画
        .processor(processor),                  // 添加processor
        .onFailureImage(UIImage(named: "hot")), // 下载/找回图片失败,显示hot图片
        .targetCache(targetCache),              // 指定被处理后图片缓存
        .originalCache(originalCache),          // 指定原始图片缓存
        .cacheOriginalImage,                    // 缓存原始图片
        .waitForCache,                                                // 等待缓存结束后在执行completionBlock
        .memoryCacheExpiration(.seconds(20)),   // 设置该图片在内存缓存过期时间
        .diskCacheExpiration(.seconds(20))      // 设置该图片在磁盘缓存过期时间
]
iv.kf.setImage(with: url, options: options) { result in
    switch result {
    case .success(let img):
            // ...
    case .failure(let error):
            // ...
    }
}

2. KingfisherOptionsInfoItem - 配置选项

3. ImageProcessor - 图片处理器

通过processor(ImageProcessor)设置ImageProcessor会对下载的原始图片进行处理并被缓存。

// 圆角
// let processor = RoundCornerImageProcessor(cornerRadius: 20)

// 降低采样率,会使图片变模糊
// let processor = DownsamplingImageProcessor(size: CGSize(width: 50, height: 50))

// 裁剪
// let processor = CroppingImageProcessor(size: CGSize(width: 100, height: 100), anchor: CGPoint(x: 0.5, y: 0.5))

// 毛玻璃
// let processor = BlurImageProcessor(blurRadius: 10)

// 在图片表面覆盖一层颜色
// let processor = OverlayImageProcessor(overlay: .white, fraction: 0.7)

// 用一种颜色进行着色,会覆盖原图
// let processor = TintImageProcessor(tint: .blue)

 // 饱和度、对比度等调整
// let processor = ColorControlsProcessor(brightness: 1.0, contrast: 0.7, saturation: 1.1, inputEV: 0.7)

// 黑白图
// let processor = BlackWhiteProcessor()

// 混合颜色
// let processor = BlendImageProcessor(blendMode: .colorBurn, alpha: 0.9, backgroundColor: .cyan)

// 重设图片大小
// let processor = ResizingImageProcessor(referenceSize: CGSize(width: 10, height: 10))

设置方法:

let processor = RoundCornerImageProcessor(cornerRadius: 20)
let options = [.processor(processor)]
iv.kf.setImage(with: url, options: options) { result in
        // ...                                             
}

多个processor结合使用

let processor = RoundCornerImageProcessor(cornerRadius: 20) |> BlurImageProcessor(blurRadius: 10)
let options = [.processor(processor)]
iv.kf.setImage(with: url, options: options) { result in
        // ...                                             
}

4. Cache - 缓存

Kingfisher使用ImageCache类来控制缓存,它采用混合缓存的模式,包含内存缓存和磁盘缓存。

  1. 指定图片的cacheKey

    let resource = ImageResource(downloadURL: url, cacheKey: "my_cache_key")
    imageView.kf.setImage(with: resource)
    
  2. 检查图片是否被缓存

    let cached = ImageCache.default.isCached(forKey: cacheKey)
    
  3. 检查图片被缓存的位置

    let cacheType = cache.imageCachedType(forKey: cacheKey)
    // .memory, .disk, .none
    
  4. 从缓存中找回图片

    cache.retrieveImage(forKey: "cacheKey") { result in
        switch result {
        case .success(let value):
            print(value.cacheType)
    
            // If the `cacheType is `.none`, `image` will be `nil`.
            print(value.image)
    
        case .failure(let error):
            print(error)
        }
    }
    
  1. 在下载或者从缓存找回图片时,使用processor对图片进行了一些处理,则经过处理的图片会被缓存下来,所以在获取该图片时,不仅需要图片的cacheKey,还需要processoridentifier,这样才能从缓存中找到经过处理的图片。

    let processor = RoundCornerImageProcessor(cornerRadius: 20)
    imageView.kf.setImage(with: url, options: [.processor(processor)])
    

    判断是否缓存了经过处理的图片

    cache.isCached(forKey: cacheKey, processorIdentifier: processor.identifier)
    

    找回经过处理的图片

    cache.retrieveImage(forKey: cacheKey, options: KingfisherParsedOptionsInfo([processor])) { result in
         switch result {
        case .success(let value):
            print(value.cacheType)
    
            // If the `cacheType is `.none`, `image` will be `nil`.
            print(value.image)
    
        case .failure(let error):
            print(error)
        }
    }
    
  2. 设置cache的内存大小

    设置内存缓存大小

    // 设置缓存内存最大为 300 MB.
    cache.memoryStorage.config.totalCostLimit = 300 * 1024 * 1024
    
    // 最多缓存150张图片 
    cache.memoryStorage.config.countLimit = 150
    

    设置磁盘缓存大小

    // 设置磁盘缓存内存最大 1 GB.
    cache.diskStorage.config.sizeLimit =  = 1000 * 1024 * 1024
    
  3. 设置缓存过期时间

    设置cache对象下的所有内存缓存过期时间

    // 内存缓存10分钟后过期
    cache.memoryStorage.config.expiration = .seconds(600)
    
    // 磁盘缓存永不过期
    cache.diskStorage.config.expiration = .never
    

    针对某张图片设置过期时间,内存缓存和磁盘缓存都可设置,还可以在访问缓存图片时设置memoryCacheAccessExtendingExpiration option来延长缓存时间

    imageView.kf.setImage(with: url, options: [.memoryCacheExpiration(.never)])
    
    // 在获取图片时延长缓存时间
    cache.retrieveImageInDiskCache(forKey: url.cacheKey, options: [.diskCacheAccessExtendingExpiration(.expirationTime(.seconds(100)))]) { result in
         // ...                    
    }
    
  4. 清理缓存

    1. 内存缓存每隔2分钟会清理一下缓存,如果想更频繁地清理缓存,可设置

      cache.memoryStorage.config.cleanInterval = 30
      
    2. 清理某一张图片

      cache.default.removeImage(forKey: cacheKey)
      

      或者调用更强大的方法

      cache.removeImage(
          forKey: cacheKey,
          processorIdentifier: processor.identifier,
          fromMemory: false,
          fromDisk: true)
      {
          print("Removed!")
      }
      
    3. 清理所有缓存

      cache.clearMemoryCache()
      cache.clearDiskCache { print("Done") }
      
    4. 清理过期缓存

      cache.cleanExpiredMemoryCache()
      cache.cleanExpiredDiskCache { print("Done") }
      
  5. 获取已用磁盘缓存大小

    ImageCache.default.calculateDiskStorageSize { result in
        switch result {
        case .success(let size):
            print("Disk cache size: \(Double(size) / 1024 / 1024) MB")
        case .failure(let error):
            print(error)
        }
    }
    
  6. 自定义cache

    let originalCache = ImageCache(name: "original_Cache")
    let targetCache = ImageCache(name: "target_Cache")
    

    结合option使用

    let options: [KingfisherOptionsInfoItem] = [
                        .cacheOriginalImage,                                        // 存储原始图片
                .originalCache(originalCache),                  // 设置存储原始图片的cache
                          .targetCache(targetCache),                            // 设置目标cache,上面设置了originCache,这里会存放处理后的图片
                .waitForCache,                                                  // 等待cache完成再执行completion closure
                .memoryCacheExpiration(.seconds(200)),  // 设置内存缓存过期时间
                .diskCacheExpiration(.seconds(400))         // 设置磁盘缓存过期时间
    ]
    

    上面的options实现了以下功能:

    • 存储原始图片并设置原始图片存储的cache对象为originCache,此时原始图片是存储在磁盘缓存中的;
    • 经过processor处理的图片是由targetCache负责存储的,可以结合processor.identifier在targetCache中找回图片
    • 设置了图片在内存和磁盘中缓存的过期时间

5. Downloader - 下载器

  1. 一般情况下,建议使用UIImageView等的扩展方法或者KingfisherManager来加载图片,因为它们会将图片缓存到本地,防止重复下载;如果我们只是下载图片,不需要缓存图片,则可以这样:

    let downloader = ImageDownloader.default
    downloader.downloadImage(with: url) { result in
        switch result {
        case .success(let value):
            print(value.image)
        case .failure(let error):
            print(error)
        }
    }
    
  2. 取消下载

    如果在取消下载前,下载已经完成,则不会有任何影响。

    let task = downloader.downloadImage(with: url) { result in
        // ...
        case .failure(let error):
            print(error.isTaskCancelled) // true
        }
    
    }
    
    // 在下载完成前,取消下载
    task?.cancel()
    

    可以通过UI类的扩展方法来取消下载

    let task = imageView.kf.set(with: url)
    task?.cancel()
    

    还可以通过调用cancelDownloadTask方法来取消下载

    let task1 = imageView.kf.set(with: url1)
    let task2 = imageView.kf.set(with: url2)
    
    imageView.kf.cancelDownloadTask()
    

    task2会被取消,但是task1还是会执行。

    但是task1下载的图片不会被设置到ImageView上,因为ImageView期望的是通过url2返回的图片

  3. 自定义对服务端challenge的回应

    ImageDownloader有一个默认的.performDefaultHandling来应对服务端的挑战,我们也可以自定义对服务端挑战的回应

    // In ViewController
    ImageDownloader.default.authenticationChallengeResponder = self
    
    extension ViewController: AuthenticationChallengeResponsable {
    
        var disposition: URLSession.AuthChallengeDisposition { /* */ }
        let credential: URLCredential? { /* */ }
    
        func downloader(
            _ downloader: ImageDownloader,
            didReceive challenge: URLAuthenticationChallenge,
            completionHandler: (URLSession.AuthChallengeDisposition, URLCredential?) -> Void)
        {
            // Provide your `AuthChallengeDisposition` and `URLCredential`
            completionHandler(disposition, credential)
        }
    
        func downloader(
            _ downloader: ImageDownloader,
            task: URLSessionTask,
            didReceive challenge: URLAuthenticationChallenge,
            completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void)
        {
            // Provide your `AuthChallengeDisposition` and `URLCredential`
            completionHandler(disposition, credential)
        }
    }
    
  4. 设置下载超时时间

    // 设置超时时间为1分钟
    downloader.downloadTimeout = 60
    
  5. 对某一个特定的下载设置超时时间

    let modifier = AnyModifier { request in
        var r = request
        r.timeoutInterval = 60
        return r
    }
    downloader.downloadImage(with: url, options: [.requestModifier(modifier)])
    

6. Serializer - 序列化

序列化指的就是将Image转换成data以便进行缓存,或将data转换成Image以便展示在页面上。

默认情况下使用的是DefaultCacheSerializer,它支持PNG,JPEG和GIF。如果我们想支持WEBP,可以通过实现CacheSerializer协议来自定义一个Serializer:

struct WebpCacheSerializer: CacheSerializer {
    func data(with image: Image, original: Data?) -> Data? {
        return WebpFramework.webpData(of: image)
    }
    
    func image(with data: Data, options: KingfisherParsedOptionsInfo?) -> Image? {
        return WebpFramework.createImage(from: webpData)
    }
}

// 使用
let serializer = WebpCacheSerializer()
let url = URL(string: "https://yourdomain.com/example.webp")
imageView.kf.setImage(with: url, options: [.cacheSerializer(serializer)])

7. Prefetch - 预获取图片

如果我们确定接下来将要使用一些图片 ,那么我们可以使用预获取功能

let urls = ["https://example.com/image1.jpg", "https://example.com/image2.jpg"]
           .map { URL(string: $0)! }
let prefetcher = ImagePrefetcher(urls: urls) {
    skippedResources, failedResources, completedResources in
    print("These resources are prefetched: \(completedResources)")
}
prefetcher.start()

// 使用方法
imageView.kf.setImage(with: urls[0])
anotherImageView.kf.setImage(with: urls[1])

可以结合iOS10以后提供的UICollectionViewDataSourcePrefetchingUITableViewDataSourcePrefetching来使用:

override func viewDidLoad() {
    super.viewDidLoad()
    collectionView?.prefetchDataSource = self
}

extension ViewController: UICollectionViewDataSourcePrefetching {
    func collectionView(_ collectionView: UICollectionView, prefetchItemsAt indexPaths: [IndexPath]) {
        let urls = indexPaths.flatMap { URL(string: $0.urlString) }
        ImagePrefetcher(urls: urls).start()
    }
}

8. ImageDataProvider - 以其它形式加载图片

Kingfisher还支持从本地data获取图片,并结合processor来对图片进行加工处理。

  1. 从本地url获取图片

    let url = URL(fileURLWithPath: path)
    let provider = LocalFileImageDataProvider(fileURL: url)
    // 结合processor
    let processor = RoundCornerImageProcessor(cornerRadius: 20)
    imageView.kf.setImage(with: provider, options: [.processor(processor)])  
    
  2. 从base64获取图片

    let provider = Base64ImageDataProvider(base64String: "\/9j\/4AAQSkZJRgABAQA...", cacheKey: "some-cache-key")
    imageView.kf.setImage(with: provider)
    
  3. Video URLAVAsset的指定时间生成一张图片

    let provider = AVAssetImageDataProvider(
        assetURL: URL(string: "https://example.com/your_video.mp4")!,
        seconds: 15.0
    )
    
  4. 自定义ImageDataProvider

    遵守ImageDataProvider协议需要实现cacheKeydata(handler:)方法,data(handler:)方法的参数handler是一个闭包,闭包的参数是Result类型,所以,我们需要将经过处理的最终image data传递给Result。

    struct UserNameLetterIconImageProvider: ImageDataProvider {
        var cacheKey: String { return letter }
        let letter: String
        
        init(userNameFirstLetter: String) {
            self.letter = userNameFirstLetter
        }
        
        func data(handler: @escaping (Result<Data, Error>) -> Void) {
            
            // You can ignore these detail below.
            // It generates some data for an image with `letter` being rendered in the center.
    
            let letter = self.letter as NSString
            let rect = CGRect(x: 0, y: 0, width: 250, height: 250)
            let renderer = UIGraphicsImageRenderer(size: rect.size)
            let data = renderer.pngData { context in
                UIColor.black.setFill()
                context.fill(rect)
                
                let attributes = [
                    NSAttributedString.Key.foregroundColor: UIColor.white,
                                          .font: UIFont.systemFont(ofSize: 200)
                ]
                
                let textSize = letter.size(withAttributes: attributes)
                let textRect = CGRect(
                    x: (rect.width - textSize.width) / 2,
                    y: (rect.height - textSize.height) / 2,
                    width: textSize.width,
                    height: textSize.height)
                letter.draw(in: textRect, withAttributes: attributes)
            }
    
            // Provide the image data in `handler`.
            handler(.success(data))
        }
    }
    
    // Set image for user "John"
    let provider = UserNameLetterIconImageProvider(userNameFirstLetter: "J")
    imageView.kf.setImage(with: provider)
    

9. Indicator - 加载指示器

  1. 使用gif作为指示器

    let path = Bundle.main.path(forResource: "loader", ofType: "gif")!
    let data = try! Data(contentsOf: URL(fileURLWithPath: path))
    
    imageView.kf.indicatorType = .image(imageData: data)
    imageView.kf.setImage(with: url)
    
  2. 自定义的Indicator

    struct MyIndicator: Indicator {
        let view: UIView = UIView()
        
        func startAnimatingView() { view.isHidden = false }
        func stopAnimatingView() { view.isHidden = true }
        
        init() {
            view.backgroundColor = .red
        }
    }
    
    // 调用
    let i = MyIndicator()
    imageView.kf.indicatorType = .custom(indicator: i)
    
  3. 自定义Indicator结合progressBlock使用

    imageView.kf.setImage(with: url, progressBlock: {
        receivedSize, totalSize in
        let percentage = (Float(receivedSize) / Float(totalSize)) * 100.0
        print("downloading progress: \(percentage)%")
        myIndicator.percentage = percentage
    })
    

    注意:只有当服务端返回的response的header中包含Content-Length时,progressBlock才会执行。

10. Retry - 重试

// 最多重试5次,每次间隔3秒
let retry = DelayRetryStrategy(maxRetryCount: 5, retryInterval: .seconds(3))
// 结合option使用
imageView.kf.setImage(with: url, options: [.retryStrategy(retry)])

.second(3)DelayRetryStrategy.Interval的一个枚举项,还可以选择accumulated(3),这样重试的触发间隔时间就是3, 6, 9, 12, ...

上一篇 下一篇

猜你喜欢

热点阅读