Swift画像読み込みフレームワークKingfisher



Swift Image Loading Framework Kingfisher



まず、カワセミの建築

他の人の優れたコードを読むことは、コードレベルを向上させるための優れた方法です。 Kingfisherのソースコードを読むのに数日かかりました。そこにはたくさんの知識ポイントが含まれていて、私にとって大きなメリットがありました。以前のバージョンと比較した3.xバージョンの重要な変更は、よりプロトコル指向のプロトコルの柔軟な使用です。もちろん、マルチスレッド、列挙、クロージャー、拡張など、他にも多くの知識があります。 Kingfisherには、Core、Extension、およびHelpersの3つのディレクトリ構造があります。合計20個のファイル。




image.swiftファイルは、画像タイプの決定、画像のデコード、Gifデータ処理など、UIImageとNSDataを内部的に拡張します。
画像をロードする際のIndicator.swiftのロード手順
ImageCache.swiftは、主にロードされた画像をローカルにキャッシュする役割を果たします。
ImageDownloader.swiftは、Web画像のダウンロードを担当します。
ImagePrefetcher.swiftを使用して、事前にいくつかの画像のダウンロードを指定できます
ImageProcessor.swiftを使用して、ダウンロードしたデータを画像オブジェクトに合成できます。
CacheSerializer.swiftを使用して、イメージオブジェクトをイメージデータにシリアル化し、イメージデータをディスクキャッシュからディスクオブジェクトにデシリアライズすることができます。
RequestModifier.swift Image RequestModifierをダウンロードします。
ImageTransition.swiftトランジションアニメーションエフェクトUIViewAnimationOptionsアニメーションエフェクト
KingfisherManager.swift画像のダウンロードとキャッシュを備えたKingfisher管理制御クラス
KingfisherOptionsInfo.swiftは、KingfisherOptionsInfoItemを列挙して、キャッシュオブジェクトをカスタマイズしてダウンローダーをカスタマイズするかどうか、トランジションアニメーションが低優先度のダウンロードに設定されているかどうか、キャッシュされた画像のみを取得するように更新を強制するかどうかなど、Kingfisherの動作のパラメーターを構成します。メモリ、画像の背景のデコードやその他の設定を許可するかどうか。
Filter.swift画像フィルター
Resource.swiftは、イメージのダウンロードアドレスとキャッシュキーを記録します。
Kingfisher.swiftは、KingfisherCompatibleの一般的なプロトコルkfの新しいプロパティを追加します

拡張
ImageView + Kingfisher.swift UIButton + Kingfisher.swift NSButton + Kingfisherは、主にKingfisherの外部インターフェイスを提供するためにUIImageView UIButtonNSButtonを拡張します。



ヘルパー
String + MD5.swift画像がキャッシュされるときのファイル名のMD5暗号化を担当します。
Box.swift単純なジェネリッククラス
ThreadHelper.swiftのdispatch_async_safely_main_queue関数はクロージャを受け入れます。 NSThread.isMainThreadを使用して、NSThread.isMainThreadを決定し、メインスレッドに配置します。

第二に、Kingfisher.swift

メインファイルImageView + Kingfisher、KingfisherManager、ImageCache、ImageDownloader、ナンセンス、あまり直接的なコード学習はありません



以下のデモをコードで実行します。

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') })

最初に呼び出されるUIImageViewのkfプロパティは、UIImageViewを呼び出す拡張機能のkf_setImageであり、現在は非推奨です。 kfプロパティはどのように実装されますか?
これがKingfisher.swiftのソースコードです

異なるプラットフォームでいくつかのタイプエイリアスをカスタマイズします。 swiftのTypealiasは、OCのtypedefと同等です。

第三に、ImageView + Kingfisher

ここで、setImageメソッドが実装されているとしましょう。このメソッドはKingfisher'sExtensionに実装されており、BaseがUIImageViewタイプに属している必要があります。ベース:ImageViewは、kf属性のためにKingfisherに関連付けられています。
したがって、(cell as!CollectionViewCell).cellImageView.kf.setImageを呼び出すことができます。
Extensionsディレクトリ内の3つのファイルは、同様の実装です。これがImageView + Kingfisher.swiftです。
以下の方法は、Kingfisherを外部で使用する最も頻繁で重要な方法です。
最初のパラメータResourceは、URLが準拠するプロトコルであり、受信画像のURLは通常null許容ではありません。
2番目のパラメーターのプレースホルダーはデフォルトのプレースホルダーであり、空にすることができます。
3番目のパラメーターであるKingfisherOptionsInfoは、画像をダウンロードするためのKingfisherのいくつかの動作を構成する列挙配列です。
4番目のパラメーターDownloadProgressBlockは、ダウンロードUIを更新するために使用できるダウンロード進行状況クロージャーです。
5番目のパラメーターcompletionHandlerは、ダウンロード完了クロージャーです。クロージャパラメータには、画像、エラー、キャッシュタイプ、およびURL情報が含まれます。

拡張Kingfisherwhere Base:ImageView {
/ **
リソース、プレースホルダー画像、オプション、進行状況ハンドラー、完了ハンドラーを使用して画像を設定します。

- 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)

タスクを返す
}
/ **
イメージビューが実行されている場合は、イメージビューにバインドされているイメージダウンロードタスクをキャンセルします。
ダウンロードがすでに完了している場合は、何も起こりません。
* /
ダウンロードをキャンセルする
public func cancelDownloadTask(){
imageTask?.downloadTask?.cancel()
}
}
ImageView + KingfisherのWebURindicatorTypeインジケーターimageTask属性は、属性関連付けテクノロジーを使用してデータにアクセスします。

第四に、KingfisherManager

このクラスは、Kingfisherの唯一の管理ディスパッチクラスです。このクラスには、ダウンロードとキャッシュのための2つの主要な機能モジュールがあります。主に2つのプロパティが含まれています。
public var cache:ImageCache画像キャッシュのプロパティ
public var downloader:ImageDownloader画像ダウンロードプロパティ
funcdownloadAndCacheImage画像メソッドのダウンロードとキャッシュ
functryToRetrieveImageFromCacheキャッシュされた画像を取得します

ImageView + Kingfisherの最終的な画像は、KingfisherManagerのシングルトンによって実装されたretrieveImageです。

イメージメソッドを取得するための外部呼び出し
func retrieveImage(with resource:Resource、
オプション:KingfisherOptionsInfo ?、
progressBlock:DownloadProgressBlock ?、
completeHandler:CompletionHandler?)-> RetrieveImageTask {
タスク= RetrieveImageTask()を許可します
オプション=オプションの場合、options.forceRefresh {
強制更新Webから画像を取得
_ = downloadAndCacheImage(
と:resource.downloadURL、
forKey:resource.cacheKey、
retrieveImageTask:タスク、
progressBlock:progressBlock、
completeHandler:completionHandler、
オプション:オプション)
} そうしないと {
キャッシュから画像を取得する
tryToRetrieveImageFromCache(
forKey:resource.cacheKey、
と:resource.downloadURL、
retrieveImageTask:タスク、
progressBlock:progressBlock、
completeHandler:completionHandler、
オプション:オプション)
}
タスクを返す
}
画像をダウンロードしてキャッシュする方法
func downloadAndCacheImage(with url:URL、
forKeyキー:文字列、
retrieveImageTask:RetrieveImageTask、
progressBlock:DownloadProgressBlock ?、
completeHandler:CompletionHandler ?、
オプション:KingfisherOptionsInfo?)-> RetrieveImageDownloadTask?
{{
ダウンローダーを入手してダウンロードを開始します
オプション=オプションを許可しますか? KingfisherEmptyOptionsInfo
downloader = options.downloaderにしましょう
downloader.downloadImage(with:url、retrieveImageTask:retrieveImageTask、options:options、
progressBlock:{receivedSize、totalSize in
progressBlock?(receivedSize、totalSize)
}、
completeHandler:{image、error、imageURL、originalData in

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) }) }

ネットワークから画像を取得するために、キャッシュ内にないなど、キャッシュから画像を取得する優先度
func tryToRetrieveImageFromCache(forKeyキー:文字列、
URL付き:URL、
retrieveImageTask:RetrieveImageTask、
progressBlock:DownloadProgressBlock ?、
completeHandler:CompletionHandler ?、
オプション:KingfisherOptionsInfo?)
{{
内部diskTaskクロージャーによって維持されている循環参照を中断し、完了後にディスクタスク参照をキャンセルし、循環参照を回避し、メモリを解放します
let diskTaskCompletionHandler:CompletionHandler = {(image、error、cacheType、imageURL)->()in
//以下のdiskTaskクロージャー内に作成された保持サイクルを中断します
retrieveImageTask.diskRetrieveTask = nil
completeHandler?(image、error、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 }

5、KingfisherOptionsInfo

上記のコードは、optionsパラメーターを複数回使用しています。そのパラメータタイプはKingfisherOptionsInfoであり、タイプエイリアスです。
public typealias KingfisherOptionsInfo = [KingfisherOptionsInfoItem]
KingfisherOptionsInfoItemは列挙型です。 Kingfisherのすべての機能を構成します。以下は詳細な中国語のコメントです。

パブリック列挙型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

}
これがカスタムです<== operator Compares two KingfisherOptionsInfoItem equals Equal returns true otherwise returns false

優先グループItemComparisonPrecedence {
結合性:なし
highThan:LogicalConjunctionPrecedence
}

中置演算子<== : ItemComparisonPrecedence

//この演算子は2つのKingfisherOptionsInfoItemの場合にtrueを返します列挙型は、関連する値を考慮せずに同じです。
func Bool {
スイッチ(lhs、rhs){
ケース(.targetCache( )、. targetCache( )):trueを返す
ケース(.downloader( )、. downloader( )):trueを返す
ケース(.transition( )、。 transition( )):trueを返す
ケース(.downloadPriority( )、. downloadPriority( )):trueを返す
ケース(.forceRefresh、.forceRefresh):trueを返します
ケース(.forceTransition、.forceTransition):trueを返します
ケース(.cacheMemoryOnly、.cacheMemoryOnly):trueを返します
ケース(.onlyFromCache、.onlyFromCache):trueを返します
ケース(.backgroundDecode、.backgroundDecode):trueを返します
ケース(.callbackDispatchQueue( )、. callbackDispatchQueue( )):trueを返す
ケース(.scaleFactor( )、. scaleFactor( )):trueを返す
ケース(.preloadAllGIFData、.preloadAllGIFData):trueを返します
ケース(.requestModifier( )、. requestModifier( )):trueを返す
ケース(.processor( )、. processor( )):trueを返す
ケース(.cacheSerializer( )、. cacheSerializer( )):trueを返す
ケース(.keepCurrentImageWhileLoading、.keepCurrentImageWhileLoading):trueを返します
デフォルト:falseを返します
}
}
以下は、一致の最初の同一の列挙値を返すCollectionTypeの拡張です。上記の遷移アニメーションは便利です。

Iterator.Element == KingfisherOptionsInfoItem {のパブリック拡張コレクション
func firstMatchIgnoringAssociatedValue(_ target:Iterator.Element)-> Iterator.Element? {{
インデックスを返す{$ 0<== target }.flatMap { self[$0] }
}

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

}
KingfisherOptionsInfoには、同様のプロパティgetメソッドが多数あります。以下は画像エンコーディングについてです。デフォルトはDefaultCacheSerializer.defaultです。画像のエンコーディングをカスタマイズする場合は、カスタムのCacheSerializerをOptions配列に追加できます。

public var cacheSerializer:CacheSerializer {
item = firstMatchIgnoringAssociatedValue(.cacheSerializer(DefaultCacheSerializer.default))の場合、
case .cacheSerializer(let cacheSerializer)= item
{{
cacheSerializerを返す
}
DefaultCacheSerializer.defaultを返します
}

著者:息を切らして
リンク: https://www.jianshu.com/p/a47fefeed7f0
出典:ショートブック
著作権は作者が所有します。商業的転載については、許可を得るために著者に連絡してください。非営利の転載については、出典を明記してください。