Cadre de chargement d'image rapide Kingfisher

Swift Image Loading Framework Kingfisher



Tout d'abord, l'architecture de Kingfisher

Lire l'excellent code des autres est un excellent moyen d'améliorer votre niveau de code. Il a fallu quelques jours pour lire le code source de Kingfisher, qui comprenait beaucoup de points de connaissance, ce qui m'a beaucoup profité. Un changement important dans la version 3.x par rapport à la version précédente est l'utilisation flexible du protocole, qui est plus orienté protocole. Bien sûr, il existe de nombreuses autres connaissances, telles que le multi-threading, l'énumération, la fermeture, l'extension, etc. Kingfisher a trois structures de répertoires: Core, Extension et Helpers. Un total de 20 fichiers.



Coeur
Le fichier image.swift étend en interne UIImage et NSData, y compris la détermination du type d'image, le décodage d'image et le traitement des données Gif.
Indicator.swift instructions de chargement lors du chargement d'images
ImageCache.swift est principalement responsable de la mise en cache des images chargées localement.
ImageDownloader.swift est responsable du téléchargement des images Web.
ImagePrefetcher.swift peut être utilisé pour spécifier certains téléchargements d'images à l'avance
ImageProcessor.swift peut être utilisé pour synthétiser les données téléchargées en objets image.
CacheSerializer.swift peut être utilisé pour sérialiser les objets image en données image et pour désérialiser les données image en objets disque à partir du cache disque.
RequestModifier.swift Télécharger le modificateur de demande d'image.
ImageTransition.swift Effets d'animation de transition Effets d'animation UIViewAnimationOptions
KingfisherManager.swift Classe de contrôle de gestion Kingfisher avec téléchargement d'image et cache
KingfisherOptionsInfo. mémoire, s'il faut autoriser le décodage de l'arrière-plan de l'image et d'autres paramètres.
Filtre d'image Filter.swift
Resource.swift enregistre l'adresse de téléchargement et la clé de cache de l'image.
Kingfisher.swift ajoute la nouvelle propriété KF de KingfisherCompatible General Protocol



Extension
ImageView + Kingfisher.swift UIButton + Kingfisher.swift NSButton + Kingfisher étend UIImageView UIButton NSButton principalement pour fournir l'interface externe de Kingfisher.



Aides
String + MD5.swift Responsable du cryptage MD5 des noms de fichiers lorsque l'image est mise en cache.
Box.swift Une classe générique simple
La fonction dispatch_async_safely_main_queue dans ThreadHelper.swift accepte une fermeture. Utilisez NSThread.isMainThread pour le déterminer et le placer dans le thread principal.

Deuxièmement, Kingfisher.swift

Les fichiers principaux ImageView + Kingfisher, KingfisherManager, ImageCache, ImageDownloader, absurdités, pas beaucoup d'apprentissage direct du code



Exécutez la démo ci-dessous avec un morceau de code:

let url = URL(string:'https://raw.githubusercontent.com/onevcat/Kingfisher/master/images/kingfisher-(indexPath.row + 1).jpg')! (cell as! CollectionViewCell).cellImageView.kf.setImage(with: url, placeholder: nil, options: [.transition(.fade(1))], progressBlock: { receivedSize, totalSize in print('(indexPath.row + 1): (receivedSize)/(totalSize)') }, completionHandler: { image, error, cacheType, imageURL in print('(indexPath.row + 1): Finished') })

La propriété kf de UIImageView qui est appelée en premier est kf_setImage dans l'extension qui appelle UIImageView, qui est désormais obsolète. Comment la propriété kf est-elle implémentée?
Voici le code source de Kingfisher.swift

Personnalisez certains alias de type sous différentes plates-formes. Typealias en swift équivaut à typedef en OC.

Troisièmement, ImageView + Kingfisher

Disons maintenant que la méthode setImage est implémentée. Cette méthode est implémentée dans l'extension de Kingfisher et nécessite que Base appartienne au type UIImageView. Où Base: ImageView est associé à Kingfisher en raison de l'attribut kf.
afin que vous puissiez appeler (cellule comme! CollectionViewCell) .cellImageView.kf.setImage
Les trois fichiers du répertoire Extensions sont des implémentations similaires. Voici ImageView + Kingfisher.swift.
Les méthodes suivantes sont les méthodes les plus fréquentes et les plus importantes d'utilisation de Kingfisher en externe.
Le premier paramètre Resource est un protocole auquel l'URL adhère, et l'URL de l'image entrante n'est généralement pas nullable.
Le deuxième espace réservé de paramètre est un espace réservé par défaut, qui peut être vide.
Le troisième paramètre, KingfisherOptionsInfo, est un tableau d'énumération qui configure certains comportements de Kingfisher pour télécharger des images.
Le quatrième paramètre, DownloadProgressBlock, est une fermeture de progression de téléchargement qui peut être utilisée pour mettre à jour l'interface utilisateur de téléchargement.
Le cinquième paramètre completionHandler est une fermeture de fin de téléchargement. Les paramètres de fermeture incluent les images, les erreurs, les types de cache et les informations d'URL.

extension Kingfisher où Base: ImageView {
/ **
Définissez une image avec une ressource, une image d'espace réservé, des options, un gestionnaire de progression et un gestionnaire d'achèvement.

- parameter resource: Resource object contains information such as `cacheKey` and `downloadURL`. - parameter placeholder: A placeholder image when retrieving the image at URL. - parameter options: A dictionary could control some behaviors. See `KingfisherOptionsInfo` for more. - parameter progressBlock: Called when the image downloading progress gets updated. - parameter completionHandler: Called when the image retrieved and set. - returns: A task represents the retrieving process. - note: Both the `progressBlock` and `completionHandler` will be invoked in main thread. The `CallbackDispatchQueue` specified in `optionsInfo` will not be used in callbacks of this method. */ @discardableResult Ignore return value warning public func setImage(with resource: Resource?, placeholder: Image? = nil, options: KingfisherOptionsInfo? = nil, progressBlock: DownloadProgressBlock? = nil, completionHandler: CompletionHandler? = nil) -> RetrieveImageTask { When the incoming resource is empty, use the guard statement to exit early. Resource is a protocol. URLs follow this protocol. Resource has two properties, cacheKey and downloadURL. guard let resource = resource else { base.image = placeholder completionHandler?(nil, nil, .none, nil) return .empty } Whether the placeholder is displayed during image loading var options = options ?? KingfisherEmptyOptionsInfo if !options.keepCurrentImageWhileLoading { base.image = placeholder } If the indicator is present, turn on the circle animation indicator. let maybeIndicator = indicator maybeIndicator?.startAnimatingView() Associated attribute binding downloaded URL setWebURL(resource.downloadURL) By default, all GIF image data is loaded to display GIF dynamic images. if base.shouldPreloadAllGIF() { options.append(.preloadAllGIFData) } Call the KingfisherManager method to get the image let task = KingfisherManager.shared.retrieveImage( with: resource, options: options, progressBlock: { receivedSize, totalSize in Download progress callback if let progressBlock = progressBlock { progressBlock(receivedSize, totalSize) } }, completionHandler: {[weak base] image, error, cacheType, imageURL in Ensure thread safety DispatchQueue.main.safeAsync { Make sure the returned image matches the URL guard let strongBase = base, imageURL == self.webURL else { return } self.setImageTask(nil) No picture returns stop animation return error guard let image = image else { maybeIndicator?.stopAnimatingView() completionHandler?(nil, error, cacheType, imageURL) return } Whether you need a transition animation transitionItem is the first .transition in options Need to transition animation needs to meet the following conditions 1.TransitionItem exists and is not .transition(.none) 2.options.forceTransition exists or cacheType == .none guard let transitionItem = options.firstMatchIgnoringAssociatedValue(.transition(.none)), case .transition(let transition) = transitionItem, ( options.forceTransition || cacheType == .none) else { maybeIndicator?.stopAnimatingView() strongBase.image = image completionHandler?(image, error, cacheType, imageURL) return } Transition animation #if !os(macOS) UIView.transition(with: strongBase, duration: 0.0, options: [], animations: { maybeIndicator?.stopAnimatingView() }, completion: { _ in UIView.transition(with: strongBase, duration: transition.duration, options: [transition.animationOptions, .allowUserInteraction], animations: { // Set image property in the animation. Set the image, if it is a custom animation Set the image in the definition animation callback, the code is in ImageTransition.swift transition.animations?(strongBase, image) }, completion: { finished in Animation end callback transition.completion?(finished) completionHandler?(image, error, cacheType, imageURL) }) }) #endif } }) setImageTask(task)

tâche de retour
}
/ **
Annulez la tâche de téléchargement d'image liée à la vue d'image si elle est en cours d'exécution.
Rien ne se passera si le téléchargement est déjà terminé.
* /
annuler le téléchargement
public func cancelDownloadTask () {
imageTask? .downloadTask? .cancel ()
}
}
Indicateur WebUR IndicatorType dans ImageView + Kingfisher L'attribut imageTask utilise la technologie d'association d'attributs pour accéder aux données.

Quatrièmement, KingfisherManager

Cette classe est la seule classe de répartition de gestion pour Kingfisher. Cette classe a deux modules fonctionnels majeurs pour le téléchargement et la mise en cache. Il contient principalement deux propriétés.
public var cache: ImageCache image cache properties
public var downloader: propriétés de téléchargement d'image ImageDownloader
func downloadAndCacheImage Méthodes de téléchargement et d'image de cache
func tryToRetrieveImageFromCache Récupère l'image en cache

L'image finale dans ImageView + Kingfisher est une retrieveImage implémentée par le singleton de KingfisherManager.

Appel externe pour obtenir la méthode d'image
func retrieveImage (avec la ressource: Ressource,
options: KingfisherOptionsInfo ?,
progressBlock: DownloadProgressBlock ?,
completeHandler: CompletionHandler?) -> RetrieveImageTask {
let task = RetrieveImageTask ()
if let options = options, options.forceRefresh {
Actualisation forcée Obtenir des images sur le Web
_ = downloadAndCacheImage (
avec: resource.downloadURL,
forKey: resource.cacheKey,
retrieveImageTask: tâche,
progressBlock: progressBlock,
completionHandler: completionHandler,
options: options)
} autre {
Récupérer des images du cache
tryToRetrieveImageFromCache (
forKey: resource.cacheKey,
avec: resource.downloadURL,
retrieveImageTask: tâche,
progressBlock: progressBlock,
completionHandler: completionHandler,
options: options)
}
tâche de retour
}
Comment télécharger et mettre en cache des images
func downloadAndCacheImage (avec url: URL,
clé forKey: chaîne,
retrieveImageTask: RetrieveImageTask,
progressBlock: DownloadProgressBlock ?,
completionHandler: CompletionHandler ?,
options: KingfisherOptionsInfo?) -> RetrieveImageDownloadTask?
{
Obtenez le téléchargeur et commencez le téléchargement
laissez options = options ?? KingfisherEmptyOptionsInfo
laissez downloader = options.downloader
renvoie downloader.downloadImage (avec: url, retrieveImageTask: retrieveImageTask, options: options,
progressBlock: {recuSize, totalSize dans
progressBlock? (reçuSize, totalSize)
},
completionHandler: {image, erreur, imageURL, originalData dans

let targetCache = options.targetCache if let error = error, error.code == KingfisherError.notModified.rawValue { // Not modified. Try to find the image from cache. // (The image should be in cache. It should be guaranteed by the framework users.) Return the cached image if there is an error and the URL has not been modified targetCache.retrieveImage(forKey: key, options: options, completionHandler: { (cacheImage, cacheType) -> () in completionHandler?(cacheImage, nil, cacheType, url) }) return } Cache image if let image = image, let originalData = originalData { targetCache.store(image, original: originalData, forKey: key, processorIdentifier:options.processor.identifier, cacheSerializer: options.cacheSerializer, toDisk: !options.cacheMemoryOnly, completionHandler: nil) } completionHandler?(image, error, .none, url) }) }

Priorité pour obtenir des images du cache, par exemple pas dans le cache, pour obtenir des images du réseau
func tryToRetrieveImageFromCache (clé forKey: String,
avec url: URL,
retrieveImageTask: RetrieveImageTask,
progressBlock: DownloadProgressBlock ?,
completionHandler: CompletionHandler ?,
options: KingfisherOptionsInfo?)
{
casse la référence circulaire maintenue par le disque interne Fermeture de la tâche, annule la référence de la tâche du disque après l'achèvement, évite les références circulaires, libère de la mémoire
laissez diskTaskCompletionHandler: CompletionHandler = {(image, error, cacheType, imageURL) -> () in
// Rompre le cycle de conservation créé à l'intérieur de la fermeture du disque
retrieveImageTask.diskRetrieveTask = nil
completionHandler? (image, erreur, cacheType, imageURL)
}

let targetCache = options?.targetCache ?? cache let diskTask = targetCache.retrieveImage(forKey: key, options: options, completionHandler: { image, cacheType in if image != nil { Successfully returning pictures diskTaskCompletionHandler(image, nil, cacheType, url) } else if let options = options, options.onlyFromCache { Return failed and set to get the image only from the cache. Return no cache error. let error = NSError(domain: KingfisherErrorDomain, code: KingfisherError.notCached.rawValue, userInfo: nil) diskTaskCompletionHandler(nil, error, .none, url) } else { Return failure and download images from the network self.downloadAndCacheImage( with: url, forKey: key, retrieveImageTask: retrieveImageTask, progressBlock: progressBlock, completionHandler: diskTaskCompletionHandler, options: options) } } ) retrieveImageTask.diskRetrieveTask = diskTask }

Cinq, KingfisherOptionsInfo

Le code ci-dessus utilise le paramètre options plusieurs fois. Son type de paramètre est KingfisherOptionsInfo est un alias de type.
public typealias KingfisherOptionsInfo = [KingfisherOptionsInfoItem]
KingfisherOptionsInfoItem est une énumération. Configurez toutes les fonctionnalités de Kingfisher. Ce qui suit est un commentaire chinois détaillé.

public enum KingfisherOptionsInfoItem {

The associated value of this member is an ImageCache object. Kingfisher uses the specified cache object to handle related services, including attempts to retrieve cached images and store downloaded images. case targetCache(ImageCache) The associated value of this member should be an ImageDownloader object. Kingfisher will use the images downloaded by this downloader. case downloader(ImageDownloader) If the image is downloaded from the network Kingfisher will use the 'ImageTransition' enumeration animation. The default transition does not occur when caching from memory or disk. If necessary, set ForceTransition case transition(ImageTransition) The 'floating' value will be set to the priority of the image download task. The value is between 0.0 and 1.0. If this option is not set, the default value ('NSURLSessionTaskPriorityDefault') will be used. case downloadPriority(Float) If set, will ignore the cache and open a resource for a download task case forceRefresh If set, the transition animation will be turned on even if the image is cached. case forceTransition If set, Kingfisher only caches values ​​in memory instead of disk case cacheMemoryOnly If you set Kingfisher, only the image will be loaded from the cache. case onlyFromCache Decode images in the background thread before use case backgroundDecode When an image is retrieved from the cache, the associated value of this member will be used as the dispatch time callback for the target queue. If not set, Kingfisher will use the main quese callback case callbackDispatchQueue(DispatchQueue?) When converting the retrieved image data into a graph, this member variable will be used as the image scaling factor. Image resolution, not screen size. You may need to specify the correct scaling factor @2x or @3x Retina image when processing. case scaleFactor(CGFloat) Whether all GIFs should load data. The default is false, only the first image in the GIF is displayed. If true, all GIF data will be loaded into memory for decoding. This option is primarily for internal compatibility. You should not set it directly. 'AnimatedImageView' does not preload all data, and a normal image view ('UIImageView' or 'NSImageView') will load all data. Choose to use the corresponding image view type instead of setting this option. case preloadAllGIFData Used to change the request before sending the request. This is the last chance you can modify the request. You can modify the request for some custom purposes, such as adding an authentication token header, performing basic HTTP authentication or similar url mapping. The original request will not be modified by default case requestModifier(ImageDownloadRequestModifier) When the download is complete, the processor converts the downloaded data into an image. If the cache is connected to the downloader (when you are using the KingfisherManager or image extension method), the converted image will also be cached. case processor(ImageProcessor) Provides a CacheSerializer for image object serialization into image data storage to disk cache and deserialization of image data into image objects from disk cache case cacheSerializer(CacheSerializer) Keep an existing image while setting another image image view. By setting this option, the imageview's placeholder parameter will be ignored and the current image will remain loaded with the new image. case keepCurrentImageWhileLoading

}
Voici la coutume<== operator Compares two KingfisherOptionsInfoItem equals Equal returns true otherwise returns false

precedencegroup ItemComparisonPrecedence {
associativité: aucune
HigherThan: LogicalConjunctionPrecedence
}

opérateur d'infixe<== : ItemComparisonPrecedence

// Cet opérateur renvoie vrai si deux KingfisherOptionsInfoItem enum est le même, sans tenir compte des valeurs associées.
func Bool {
commutateur (lhs, rhs) {
cas (.targetCache ( ), .targetCache ( )): retourne vrai
cas (.downloader ( ), .downloader ( )): retourne vrai
cas (.transition ( ), .transition( )): retourne vrai
cas (.downloadPriority ( ), .downloadPriority ( )): retourne vrai
case (.forceRefresh, .forceRefresh): retourne true
case (.forceTransition, .forceTransition): retourne true
case (.cacheMemoryOnly, .cacheMemoryOnly): retourne true
case (.onlyFromCache, .onlyFromCache): retourne true
case (.backgroundDecode, .backgroundDecode): retourne true
cas (.callbackDispatchQueue ( ), .callbackDispatchQueue ( )): retourne vrai
cas (.scaleFactor ( ), .facteur d'échelle( )): retourne vrai
case (.preloadAllGIFData, .preloadAllGIFData): retourne true
case (.requestModifier ( ), .requestModifier ( )): retourne vrai
cas (.processor ( ), .processeur ( )): retourne vrai
cas (.cacheSerializer ( ), .cacheSerializer ( )): retourne vrai
case (.keepCurrentImageWhileLoading, .keepCurrentImageWhileLoading): retourne true
par défaut: return false
}
}
Ce qui suit est une extension de CollectionType qui renvoie la première valeur d'énumération identique de la correspondance. L'animation de transition ci-dessus est utile.

Collection d'extensions publiques où Iterator.Element == KingfisherOptionsInfoItem {
func firstMatchIgnoringAssociatedValue (_ target: Iterator.Element) -> Iterator.Element? {
index de retour {$ 0<== target }.flatMap { self[$0] }
}

func removeAllMatchesIgnoringAssociatedValue(_ target: Iterator.Element) -> [Iterator.Element] { return self.filter { !($0 <== target) } }

}
Il existe de nombreuses méthodes d'obtention de propriétés similaires dans KingfisherOptionsInfo. Ce qui suit concerne le codage d'image. La valeur par défaut est DefaultCacheSerializer.default. Si vous souhaitez personnaliser l'encodage de l'image, vous pouvez ajouter un CacheSerializer personnalisé au tableau Options.

public var cacheSerializer: CacheSerializer {
si let item = firstMatchIgnoringAssociatedValue (.cacheSerializer (DefaultCacheSerializer.default)),
case .cacheSerializer (laissez cacheSerializer) = élément
{
retourner le cacheSerializer
}
retourne DefaultCacheSerializer.default
}

Auteur: plein souffle terminé
lien: https://www.jianshu.com/p/a47fefeed7f0
Source: Livre court
Le droit d'auteur appartient à l'auteur. Pour une réimpression commerciale, veuillez contacter l'auteur pour autorisation. Pour une réimpression non commerciale, veuillez indiquer la source.