SmartImages

CI

简单轻量级的图像加载库,可以快速加载图像,因为它优先对图像进行排队,并按照请求的顺序加载图像。如果 ImageView 正在等待加载图像,则会优先于其他图像加载。它使用原生的 Image 对象加载图像,并提供了一种在内存中缓存它们的方法,您还可以设置自定义缓存大小。

ImageDownloader

负责从互联网下载图像的管理器。

ImageCache

负责在内存中缓存图像的管理器。 默认情况下

ImageDownloadQueue

负责将图像排队等待下载,并优先处理正在被请求的图像的管理器。 优先级在运行时发生变化,因此在添加到下载队列之前会进行计算。 当图像 URL 附加到 UI 视图(也支持 SwiftUI)并且队列时间戳更接近当前时间时,优先级最高。

示例

for i in 0..<5 {
    imageDownloader.prefetch(url: URL(string: "apple.com/image_\(i)")!)
}
let imageView = UIImageView()
imageView.setImage(withURL: URL(string: "apple.com/image\_\\(99)")!) // new URL
imageView.setImage(withURL: URL(string: "apple.com/image\_\\(3)")!)  // <-- the same URL in queue

ImageDownloaderNetwork

应用程序必须实现的协议,代表网络层。

如何使用

应用程序中的任何位置的示例

planeImageView.setImage(withURL: url, placeholder: .image(.planePlaceholder))
planeImageView.setImage(withURL: url, placeholder: .clear)
planeImageView.setImage(withURL: url)

UIImageView 扩展,便于使用

// extend UIImageView for loading images
public extension UIImageView {
    func setImage(withURL url: URL,
                  animated animation: ImageAnimation? = nil,
                  placeholder: ImagePlaceholder = .none,
                  completion: ImageClosure? = nil) {
        let info = ImageInfo(url: url)
        ImageDownloader.shared.download(of: info,
                                        for: self,
                                        animated: animation,
                                        placeholder: placeholder,
                                        completion: completion ?? { _ in })
    }

    func cancelImageRequest() {
        ImageDownloader.shared.cancel(for: self)
    }
}

// MARK: - ImageDownloader + ImageDownloading

extension ImageDownloader: ImageDownloading {
    public var imageCache: ImageCaching? {
        return Self.shared.imageCache
    }

    public func prefetch(of info: ImageInfo,
                         completion: @escaping ImageClosure) {
        Self.shared.prefetch(of: info, completion: completion)
    }

    public func prefetching(of info: SmartImages.ImageInfo, completion: @escaping SmartImages.ImageClosure) -> AnyCancellable {
        return Self.shared.prefetching(of: info, completion: completion)
    }

    public func download(of info: ImageInfo,
                         completion: @escaping ImageClosure) -> AnyCancellable {
        Self.shared.download(of: info,
                             completion: completion)
    }

    public func download(of info: ImageInfo,
                         for imageView: ImageView,
                         animated animation: ImageAnimation?,
                         placeholder: ImagePlaceholder = .none,
                         completion: @escaping ImageClosure) {
        Self.shared.download(of: info,
                             for: imageView,
                             animated: animation,
                             placeholder: placeholder,
                             completion: completion)
    }

    public func cancel(for imageView: ImageView) {
        Self.shared.cancel(for: imageView)
    }
}

如何与 SmartNetwork 一起使用

import Combine
import Foundation
import SmartImages
import SmartNetwork
import UIKit

public typealias ImageAnimation = SmartImages.ImageAnimation
public typealias ImageClosure = SmartImages.ImageClosure
public typealias ImageInfo = SmartImages.ImageInfo
public typealias ImagePlaceholder = SmartImages.ImagePlaceholder

public struct ImageDownloader {
    private static let manager: RequestManagering = RequestManager.create() // <---- this is the place of your custom plugins, StopTheLine etc.

    public static let shared: ImageDownloading = {
        return SmartImages.ImageDownloader(network: ImageDownloaderNetworkAdaptor(manager: manager),
                                           cache: .init(folderName: "DownloadedImages"),
                                           concurrentLimit: 8)
    }()

    private init() {}
}

extension RequestingTask: @retroactive ImageDownloaderTask {}

private struct ImageDownloaderNetworkAdaptor: ImageDownloaderNetwork {
    let manager: RequestManagering

    func request(with url: URL,
                 cachePolicy: URLRequest.CachePolicy?,
                 timeoutInterval: TimeInterval?,
                 completion: @escaping ResultCompletion,
                 finishedOrCancelled finished: FinishedCompletion?) -> ImageDownloaderTask {
        return manager.data.request(address: .init(url),
                                    with: .init(requestPolicy: cachePolicy ?? .useProtocolCachePolicy,
                                                timeoutInterval: timeoutInterval ?? RequestSettings.timeoutInterval),
                                    inQueue: .absent,
                                    completion: completion)
    }
}

如何与 URLSession 一起使用

import Foundation
import SmartImages
import UIKit

public enum ImageDownloader {
    private static let imageDownloader: ImageDownloading = {
        return SmartImages.ImageDownloader.create(network: ImageDownloaderNetworkAdaptor(),
                                                  cache: .init(folderName: "DownloadedImages"),
                                                  concurrentImagesLimit: 8)
    }()

    public init() {}
}

private struct ImageDownloaderTaskAdaptor: ImageDownloaderTask {
    let task: URLSessionTask

    func start() {
        task.resume()
    }

    func cancel() {
        task.cancel()
    }
}

private struct ImageDownloaderNetworkAdaptor: ImageDownloaderNetwork {
    private let session: URLSession = .shared

    func request(with url: URL,
                 cachePolicy: URLRequest.CachePolicy,
                 timeoutInterval: TimeInterval,
                 completion: @escaping (Result<Data, Error>) -> Void) -> ImageDownloaderTask {
        let dataTask = session.dataTask(with: url) { data, _, error in
            if let error {
                completion(.failure(error))
            } else if let data {
                completion(.success(data))
            } else {
                completion(.failure(NSError(domain: "ImageDownloader", code: 0, userInfo: ["url": url])))
            }
        }

        return ImageDownloaderTaskAdaptor(task: dataTask)
    }
}