KingfisherOptionsInfo.swift 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364
  1. //
  2. // KingfisherOptionsInfo.swift
  3. // Kingfisher
  4. //
  5. // Created by Wei Wang on 15/4/23.
  6. //
  7. // Copyright (c) 2018 Wei Wang <onevcat@gmail.com>
  8. //
  9. // Permission is hereby granted, free of charge, to any person obtaining a copy
  10. // of this software and associated documentation files (the "Software"), to deal
  11. // in the Software without restriction, including without limitation the rights
  12. // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  13. // copies of the Software, and to permit persons to whom the Software is
  14. // furnished to do so, subject to the following conditions:
  15. //
  16. // The above copyright notice and this permission notice shall be included in
  17. // all copies or substantial portions of the Software.
  18. //
  19. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  20. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  21. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  22. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  23. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  24. // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  25. // THE SOFTWARE.
  26. #if os(macOS)
  27. import AppKit
  28. #else
  29. import UIKit
  30. #endif
  31. /**
  32. * KingfisherOptionsInfo is a typealias for [KingfisherOptionsInfoItem]. You can use the enum of option item with value to control some behaviors of Kingfisher.
  33. */
  34. public typealias KingfisherOptionsInfo = [KingfisherOptionsInfoItem]
  35. let KingfisherEmptyOptionsInfo = [KingfisherOptionsInfoItem]()
  36. /**
  37. Items could be added into KingfisherOptionsInfo.
  38. */
  39. public enum KingfisherOptionsInfoItem {
  40. /// The associated value of this member should be an ImageCache object. Kingfisher will use the specified
  41. /// cache object when handling related operations, including trying to retrieve the cached images and store
  42. /// the downloaded image to it.
  43. case targetCache(ImageCache)
  44. /// Cache for storing and retrieving original image.
  45. /// Preferred prior to targetCache for storing and retrieving original images if specified.
  46. /// Only used if a non-default image processor is involved.
  47. case originalCache(ImageCache)
  48. /// The associated value of this member should be an ImageDownloader object. Kingfisher will use this
  49. /// downloader to download the images.
  50. case downloader(ImageDownloader)
  51. /// Member for animation transition when using UIImageView. Kingfisher will use the `ImageTransition` of
  52. /// this enum to animate the image in if it is downloaded from web. The transition will not happen when the
  53. /// image is retrieved from either memory or disk cache by default. If you need to do the transition even when
  54. /// the image being retrieved from cache, set `ForceTransition` as well.
  55. case transition(ImageTransition)
  56. /// Associated `Float` value will be set as the priority of image download task. The value for it should be
  57. /// between 0.0~1.0. If this option not set, the default value (`NSURLSessionTaskPriorityDefault`) will be used.
  58. case downloadPriority(Float)
  59. /// If set, `Kingfisher` will ignore the cache and try to fire a download task for the resource.
  60. case forceRefresh
  61. /// If set, `Kingfisher` will try to retrieve the image from memory cache first. If the image is not in memory
  62. /// cache, then it will ignore the disk cache but download the image again from network. This is useful when
  63. /// you want to display a changeable image behind the same url, while avoiding download it again and again.
  64. case fromMemoryCacheOrRefresh
  65. /// If set, setting the image to an image view will happen with transition even when retrieved from cache.
  66. /// See `Transition` option for more.
  67. case forceTransition
  68. /// If set, `Kingfisher` will only cache the value in memory but not in disk.
  69. case cacheMemoryOnly
  70. /// If set, `Kingfisher` will wait for caching operation to be completed before calling the completion block.
  71. case waitForCache
  72. /// If set, `Kingfisher` will only try to retrieve the image from cache not from network.
  73. case onlyFromCache
  74. /// Decode the image in background thread before using.
  75. case backgroundDecode
  76. /// The associated value of this member will be used as the target queue of dispatch callbacks when
  77. /// retrieving images from cache. If not set, `Kingfisher` will use main queue for callbacks.
  78. case callbackDispatchQueue(DispatchQueue?)
  79. /// The associated value of this member will be used as the scale factor when converting retrieved data to an image.
  80. /// It is the image scale, instead of your screen scale. You may need to specify the correct scale when you dealing
  81. /// with 2x or 3x retina images.
  82. case scaleFactor(CGFloat)
  83. /// Whether all the animated image data should be preloaded. Default it false, which means following frames will be
  84. /// loaded on need. If true, all the animated image data will be loaded and decoded into memory. This option is mainly
  85. /// used for back compatibility internally. You should not set it directly. `AnimatedImageView` will not preload
  86. /// all data, while a normal image view (`UIImageView` or `NSImageView`) will load all data. Choose to use
  87. /// corresponding image view type instead of setting this option.
  88. case preloadAllAnimationData
  89. /// The `ImageDownloadRequestModifier` contained will be used to change the request before it being sent.
  90. /// This is the last chance you can modify the request. You can modify the request for some customizing purpose,
  91. /// such as adding auth token to the header, do basic HTTP auth or something like url mapping. The original request
  92. /// will be sent without any modification by default.
  93. case requestModifier(ImageDownloadRequestModifier)
  94. /// Processor for processing when the downloading finishes, a processor will convert the downloaded data to an image
  95. /// and/or apply some filter on it. If a cache is connected to the downloader (it happens when you are using
  96. /// KingfisherManager or the image extension methods), the converted image will also be sent to cache as well as the
  97. /// image view. `DefaultImageProcessor.default` will be used by default.
  98. case processor(ImageProcessor)
  99. /// Supply an `CacheSerializer` to convert some data to an image object for
  100. /// retrieving from disk cache or vice versa for storing to disk cache.
  101. /// `DefaultCacheSerializer.default` will be used by default.
  102. case cacheSerializer(CacheSerializer)
  103. /// Modifier for modifying an image right before it is used.
  104. /// If the image was fetched directly from the downloader, the modifier will
  105. /// run directly after the processor.
  106. /// If the image is being fetched from a cache, the modifier will run after
  107. /// the cacheSerializer.
  108. /// Use `ImageModifier` when you need to set properties on a concrete type
  109. /// of `Image`, such as a `UIImage`, that do not persist when caching the image.
  110. case imageModifier(ImageModifier)
  111. /// Keep the existing image while setting another image to an image view.
  112. /// By setting this option, the placeholder image parameter of imageview extension method
  113. /// will be ignored and the current image will be kept while loading or downloading the new image.
  114. case keepCurrentImageWhileLoading
  115. /// If set, Kingfisher will only load the first frame from a animated image data file as a single image.
  116. /// Loading a lot of animated images may take too much memory. It will be useful when you want to display a
  117. /// static preview of the first frame from a animated image.
  118. /// This option will be ignored if the target image is not animated image data.
  119. case onlyLoadFirstFrame
  120. /// If set and an `ImageProcessor` is used, Kingfisher will try to cache both
  121. /// the final result and original image. Kingfisher will have a chance to use
  122. /// the original image when another processor is applied to the same resource,
  123. /// instead of downloading it again.
  124. case cacheOriginalImage
  125. }
  126. precedencegroup ItemComparisonPrecedence {
  127. associativity: none
  128. higherThan: LogicalConjunctionPrecedence
  129. }
  130. infix operator <== : ItemComparisonPrecedence
  131. // This operator returns true if two `KingfisherOptionsInfoItem` enum is the same, without considering the associated values.
  132. func <== (lhs: KingfisherOptionsInfoItem, rhs: KingfisherOptionsInfoItem) -> Bool {
  133. switch (lhs, rhs) {
  134. case (.targetCache(_), .targetCache(_)): return true
  135. case (.originalCache(_), .originalCache(_)): return true
  136. case (.downloader(_), .downloader(_)): return true
  137. case (.transition(_), .transition(_)): return true
  138. case (.downloadPriority(_), .downloadPriority(_)): return true
  139. case (.forceRefresh, .forceRefresh): return true
  140. case (.fromMemoryCacheOrRefresh, .fromMemoryCacheOrRefresh): return true
  141. case (.forceTransition, .forceTransition): return true
  142. case (.cacheMemoryOnly, .cacheMemoryOnly): return true
  143. case (.waitForCache, .waitForCache): return true
  144. case (.onlyFromCache, .onlyFromCache): return true
  145. case (.backgroundDecode, .backgroundDecode): return true
  146. case (.callbackDispatchQueue(_), .callbackDispatchQueue(_)): return true
  147. case (.scaleFactor(_), .scaleFactor(_)): return true
  148. case (.preloadAllAnimationData, .preloadAllAnimationData): return true
  149. case (.requestModifier(_), .requestModifier(_)): return true
  150. case (.processor(_), .processor(_)): return true
  151. case (.cacheSerializer(_), .cacheSerializer(_)): return true
  152. case (.imageModifier(_), .imageModifier(_)): return true
  153. case (.keepCurrentImageWhileLoading, .keepCurrentImageWhileLoading): return true
  154. case (.onlyLoadFirstFrame, .onlyLoadFirstFrame): return true
  155. case (.cacheOriginalImage, .cacheOriginalImage): return true
  156. default: return false
  157. }
  158. }
  159. extension Collection where Iterator.Element == KingfisherOptionsInfoItem {
  160. func lastMatchIgnoringAssociatedValue(_ target: Iterator.Element) -> Iterator.Element? {
  161. return reversed().first { $0 <== target }
  162. }
  163. func removeAllMatchesIgnoringAssociatedValue(_ target: Iterator.Element) -> [Iterator.Element] {
  164. return filter { !($0 <== target) }
  165. }
  166. }
  167. public extension Collection where Iterator.Element == KingfisherOptionsInfoItem {
  168. /// The target `ImageCache` which is used.
  169. public var targetCache: ImageCache? {
  170. if let item = lastMatchIgnoringAssociatedValue(.targetCache(.default)),
  171. case .targetCache(let cache) = item
  172. {
  173. return cache
  174. }
  175. return nil
  176. }
  177. /// The original `ImageCache` which is used.
  178. public var originalCache: ImageCache? {
  179. if let item = lastMatchIgnoringAssociatedValue(.originalCache(.default)),
  180. case .originalCache(let cache) = item
  181. {
  182. return cache
  183. }
  184. return targetCache
  185. }
  186. /// The `ImageDownloader` which is specified.
  187. public var downloader: ImageDownloader? {
  188. if let item = lastMatchIgnoringAssociatedValue(.downloader(.default)),
  189. case .downloader(let downloader) = item
  190. {
  191. return downloader
  192. }
  193. return nil
  194. }
  195. /// Member for animation transition when using UIImageView.
  196. public var transition: ImageTransition {
  197. if let item = lastMatchIgnoringAssociatedValue(.transition(.none)),
  198. case .transition(let transition) = item
  199. {
  200. return transition
  201. }
  202. return ImageTransition.none
  203. }
  204. /// A `Float` value set as the priority of image download task. The value for it should be
  205. /// between 0.0~1.0.
  206. public var downloadPriority: Float {
  207. if let item = lastMatchIgnoringAssociatedValue(.downloadPriority(0)),
  208. case .downloadPriority(let priority) = item
  209. {
  210. return priority
  211. }
  212. return URLSessionTask.defaultPriority
  213. }
  214. /// Whether an image will be always downloaded again or not.
  215. public var forceRefresh: Bool {
  216. return contains{ $0 <== .forceRefresh }
  217. }
  218. /// Whether an image should be got only from memory cache or download.
  219. public var fromMemoryCacheOrRefresh: Bool {
  220. return contains{ $0 <== .fromMemoryCacheOrRefresh }
  221. }
  222. /// Whether the transition should always happen or not.
  223. public var forceTransition: Bool {
  224. return contains{ $0 <== .forceTransition }
  225. }
  226. /// Whether cache the image only in memory or not.
  227. public var cacheMemoryOnly: Bool {
  228. return contains{ $0 <== .cacheMemoryOnly }
  229. }
  230. /// Whether the caching operation will be waited or not.
  231. public var waitForCache: Bool {
  232. return contains{ $0 <== .waitForCache }
  233. }
  234. /// Whether only load the images from cache or not.
  235. public var onlyFromCache: Bool {
  236. return contains{ $0 <== .onlyFromCache }
  237. }
  238. /// Whether the image should be decoded in background or not.
  239. public var backgroundDecode: Bool {
  240. return contains{ $0 <== .backgroundDecode }
  241. }
  242. /// Whether the image data should be all loaded at once if it is an animated image.
  243. public var preloadAllAnimationData: Bool {
  244. return contains { $0 <== .preloadAllAnimationData }
  245. }
  246. /// The queue of callbacks should happen from Kingfisher.
  247. public var callbackDispatchQueue: DispatchQueue {
  248. if let item = lastMatchIgnoringAssociatedValue(.callbackDispatchQueue(nil)),
  249. case .callbackDispatchQueue(let queue) = item
  250. {
  251. return queue ?? DispatchQueue.main
  252. }
  253. return DispatchQueue.main
  254. }
  255. /// The scale factor which should be used for the image.
  256. public var scaleFactor: CGFloat {
  257. if let item = lastMatchIgnoringAssociatedValue(.scaleFactor(0)),
  258. case .scaleFactor(let scale) = item
  259. {
  260. return scale
  261. }
  262. return 1.0
  263. }
  264. /// The `ImageDownloadRequestModifier` will be used before sending a download request.
  265. public var modifier: ImageDownloadRequestModifier {
  266. if let item = lastMatchIgnoringAssociatedValue(.requestModifier(NoModifier.default)),
  267. case .requestModifier(let modifier) = item
  268. {
  269. return modifier
  270. }
  271. return NoModifier.default
  272. }
  273. /// `ImageProcessor` for processing when the downloading finishes.
  274. public var processor: ImageProcessor {
  275. if let item = lastMatchIgnoringAssociatedValue(.processor(DefaultImageProcessor.default)),
  276. case .processor(let processor) = item
  277. {
  278. return processor
  279. }
  280. return DefaultImageProcessor.default
  281. }
  282. /// `ImageModifier` for modifying right before the image is displayed.
  283. public var imageModifier: ImageModifier {
  284. if let item = lastMatchIgnoringAssociatedValue(.imageModifier(DefaultImageModifier.default)),
  285. case .imageModifier(let imageModifier) = item
  286. {
  287. return imageModifier
  288. }
  289. return DefaultImageModifier.default
  290. }
  291. /// `CacheSerializer` to convert image to data for storing in cache.
  292. public var cacheSerializer: CacheSerializer {
  293. if let item = lastMatchIgnoringAssociatedValue(.cacheSerializer(DefaultCacheSerializer.default)),
  294. case .cacheSerializer(let cacheSerializer) = item
  295. {
  296. return cacheSerializer
  297. }
  298. return DefaultCacheSerializer.default
  299. }
  300. /// Keep the existing image while setting another image to an image view.
  301. /// Or the placeholder will be used while downloading.
  302. public var keepCurrentImageWhileLoading: Bool {
  303. return contains { $0 <== .keepCurrentImageWhileLoading }
  304. }
  305. public var onlyLoadFirstFrame: Bool {
  306. return contains { $0 <== .onlyLoadFirstFrame }
  307. }
  308. public var cacheOriginalImage: Bool {
  309. return contains { $0 <== .cacheOriginalImage }
  310. }
  311. }