KingfisherOptionsInfo.swift 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355
  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 only try to retrieve the image from cache not from network.
  71. case onlyFromCache
  72. /// Decode the image in background thread before using.
  73. case backgroundDecode
  74. /// The associated value of this member will be used as the target queue of dispatch callbacks when
  75. /// retrieving images from cache. If not set, `Kingfisher` will use main quese for callbacks.
  76. case callbackDispatchQueue(DispatchQueue?)
  77. /// The associated value of this member will be used as the scale factor when converting retrieved data to an image.
  78. /// It is the image scale, instead of your screen scale. You may need to specify the correct scale when you dealing
  79. /// with 2x or 3x retina images.
  80. case scaleFactor(CGFloat)
  81. /// Whether all the animated image data should be preloaded. Default it false, which means following frames will be
  82. /// loaded on need. If true, all the animated image data will be loaded and decoded into memory. This option is mainly
  83. /// used for back compatibility internally. You should not set it directly. `AnimatedImageView` will not preload
  84. /// all data, while a normal image view (`UIImageView` or `NSImageView`) will load all data. Choose to use
  85. /// corresponding image view type instead of setting this option.
  86. case preloadAllAnimationData
  87. /// The `ImageDownloadRequestModifier` contained will be used to change the request before it being sent.
  88. /// This is the last chance you can modify the request. You can modify the request for some customizing purpose,
  89. /// such as adding auth token to the header, do basic HTTP auth or something like url mapping. The original request
  90. /// will be sent without any modification by default.
  91. case requestModifier(ImageDownloadRequestModifier)
  92. /// Processor for processing when the downloading finishes, a processor will convert the downloaded data to an image
  93. /// and/or apply some filter on it. If a cache is connected to the downloader (it happens when you are using
  94. /// KingfisherManager or the image extension methods), the converted image will also be sent to cache as well as the
  95. /// image view. `DefaultImageProcessor.default` will be used by default.
  96. case processor(ImageProcessor)
  97. /// Supply an `CacheSerializer` to convert some data to an image object for
  98. /// retrieving from disk cache or vice versa for storing to disk cache.
  99. /// `DefaultCacheSerializer.default` will be used by default.
  100. case cacheSerializer(CacheSerializer)
  101. /// Modifier for modifying an image right before it is used.
  102. /// If the image was fetched directly from the downloader, the modifier will
  103. /// run directly after the processor.
  104. /// If the image is being fetched from a cache, the modifier will run after
  105. /// the cacheSerializer.
  106. /// Use `ImageModifier` when you need to set properties on a concrete type
  107. /// of `Image`, such as a `UIImage`, that do not persist when caching the image.
  108. case imageModifier(ImageModifier)
  109. /// Keep the existing image while setting another image to an image view.
  110. /// By setting this option, the placeholder image parameter of imageview extension method
  111. /// will be ignored and the current image will be kept while loading or downloading the new image.
  112. case keepCurrentImageWhileLoading
  113. /// If set, Kingfisher will only load the first frame from a animated image data file as a single image.
  114. /// Loading a lot of animated images may take too much memory. It will be useful when you want to display a
  115. /// static preview of the first frame from a animated image.
  116. /// This option will be ignored if the target image is not animated image data.
  117. case onlyLoadFirstFrame
  118. /// If set and an `ImageProcessor` is used, Kingfisher will try to cache both
  119. /// the final result and original image. Kingfisher will have a chance to use
  120. /// the original image when another processor is applied to the same resouce,
  121. /// instead of downloading it again.
  122. case cacheOriginalImage
  123. }
  124. precedencegroup ItemComparisonPrecedence {
  125. associativity: none
  126. higherThan: LogicalConjunctionPrecedence
  127. }
  128. infix operator <== : ItemComparisonPrecedence
  129. // This operator returns true if two `KingfisherOptionsInfoItem` enum is the same, without considering the associated values.
  130. func <== (lhs: KingfisherOptionsInfoItem, rhs: KingfisherOptionsInfoItem) -> Bool {
  131. switch (lhs, rhs) {
  132. case (.targetCache(_), .targetCache(_)): return true
  133. case (.originalCache(_), .originalCache(_)): return true
  134. case (.downloader(_), .downloader(_)): return true
  135. case (.transition(_), .transition(_)): return true
  136. case (.downloadPriority(_), .downloadPriority(_)): return true
  137. case (.forceRefresh, .forceRefresh): return true
  138. case (.fromMemoryCacheOrRefresh, .fromMemoryCacheOrRefresh): return true
  139. case (.forceTransition, .forceTransition): return true
  140. case (.cacheMemoryOnly, .cacheMemoryOnly): return true
  141. case (.onlyFromCache, .onlyFromCache): return true
  142. case (.backgroundDecode, .backgroundDecode): return true
  143. case (.callbackDispatchQueue(_), .callbackDispatchQueue(_)): return true
  144. case (.scaleFactor(_), .scaleFactor(_)): return true
  145. case (.preloadAllAnimationData, .preloadAllAnimationData): return true
  146. case (.requestModifier(_), .requestModifier(_)): return true
  147. case (.processor(_), .processor(_)): return true
  148. case (.cacheSerializer(_), .cacheSerializer(_)): return true
  149. case (.imageModifier(_), .imageModifier(_)): return true
  150. case (.keepCurrentImageWhileLoading, .keepCurrentImageWhileLoading): return true
  151. case (.onlyLoadFirstFrame, .onlyLoadFirstFrame): return true
  152. case (.cacheOriginalImage, .cacheOriginalImage): return true
  153. default: return false
  154. }
  155. }
  156. extension Collection where Iterator.Element == KingfisherOptionsInfoItem {
  157. func lastMatchIgnoringAssociatedValue(_ target: Iterator.Element) -> Iterator.Element? {
  158. return reversed().first { $0 <== target }
  159. }
  160. func removeAllMatchesIgnoringAssociatedValue(_ target: Iterator.Element) -> [Iterator.Element] {
  161. return filter { !($0 <== target) }
  162. }
  163. }
  164. public extension Collection where Iterator.Element == KingfisherOptionsInfoItem {
  165. /// The target `ImageCache` which is used.
  166. public var targetCache: ImageCache {
  167. if let item = lastMatchIgnoringAssociatedValue(.targetCache(.default)),
  168. case .targetCache(let cache) = item
  169. {
  170. return cache
  171. }
  172. return ImageCache.default
  173. }
  174. /// The original `ImageCache` which is used.
  175. public var originalCache: ImageCache {
  176. if let item = lastMatchIgnoringAssociatedValue(.originalCache(.default)),
  177. case .originalCache(let cache) = item
  178. {
  179. return cache
  180. }
  181. return targetCache
  182. }
  183. /// The `ImageDownloader` which is specified.
  184. public var downloader: ImageDownloader {
  185. if let item = lastMatchIgnoringAssociatedValue(.downloader(.default)),
  186. case .downloader(let downloader) = item
  187. {
  188. return downloader
  189. }
  190. return ImageDownloader.default
  191. }
  192. /// Member for animation transition when using UIImageView.
  193. public var transition: ImageTransition {
  194. if let item = lastMatchIgnoringAssociatedValue(.transition(.none)),
  195. case .transition(let transition) = item
  196. {
  197. return transition
  198. }
  199. return ImageTransition.none
  200. }
  201. /// A `Float` value set as the priority of image download task. The value for it should be
  202. /// between 0.0~1.0.
  203. public var downloadPriority: Float {
  204. if let item = lastMatchIgnoringAssociatedValue(.downloadPriority(0)),
  205. case .downloadPriority(let priority) = item
  206. {
  207. return priority
  208. }
  209. return URLSessionTask.defaultPriority
  210. }
  211. /// Whether an image will be always downloaded again or not.
  212. public var forceRefresh: Bool {
  213. return contains{ $0 <== .forceRefresh }
  214. }
  215. /// Whether an image should be got only from memory cache or download.
  216. public var fromMemoryCacheOrRefresh: Bool {
  217. return contains{ $0 <== .fromMemoryCacheOrRefresh }
  218. }
  219. /// Whether the transition should always happen or not.
  220. public var forceTransition: Bool {
  221. return contains{ $0 <== .forceTransition }
  222. }
  223. /// Whether cache the image only in memory or not.
  224. public var cacheMemoryOnly: Bool {
  225. return contains{ $0 <== .cacheMemoryOnly }
  226. }
  227. /// Whether only load the images from cache or not.
  228. public var onlyFromCache: Bool {
  229. return contains{ $0 <== .onlyFromCache }
  230. }
  231. /// Whether the image should be decoded in background or not.
  232. public var backgroundDecode: Bool {
  233. return contains{ $0 <== .backgroundDecode }
  234. }
  235. /// Whether the image data should be all loaded at once if it is an animated image.
  236. public var preloadAllAnimationData: Bool {
  237. return contains { $0 <== .preloadAllAnimationData }
  238. }
  239. /// The queue of callbacks should happen from Kingfisher.
  240. public var callbackDispatchQueue: DispatchQueue {
  241. if let item = lastMatchIgnoringAssociatedValue(.callbackDispatchQueue(nil)),
  242. case .callbackDispatchQueue(let queue) = item
  243. {
  244. return queue ?? DispatchQueue.main
  245. }
  246. return DispatchQueue.main
  247. }
  248. /// The scale factor which should be used for the image.
  249. public var scaleFactor: CGFloat {
  250. if let item = lastMatchIgnoringAssociatedValue(.scaleFactor(0)),
  251. case .scaleFactor(let scale) = item
  252. {
  253. return scale
  254. }
  255. return 1.0
  256. }
  257. /// The `ImageDownloadRequestModifier` will be used before sending a download request.
  258. public var modifier: ImageDownloadRequestModifier {
  259. if let item = lastMatchIgnoringAssociatedValue(.requestModifier(NoModifier.default)),
  260. case .requestModifier(let modifier) = item
  261. {
  262. return modifier
  263. }
  264. return NoModifier.default
  265. }
  266. /// `ImageProcessor` for processing when the downloading finishes.
  267. public var processor: ImageProcessor {
  268. if let item = lastMatchIgnoringAssociatedValue(.processor(DefaultImageProcessor.default)),
  269. case .processor(let processor) = item
  270. {
  271. return processor
  272. }
  273. return DefaultImageProcessor.default
  274. }
  275. /// `ImageModifier` for modifying right before the image is displayed.
  276. public var imageModifier: ImageModifier {
  277. if let item = lastMatchIgnoringAssociatedValue(.imageModifier(DefaultImageModifier.default)),
  278. case .imageModifier(let imageModifier) = item
  279. {
  280. return imageModifier
  281. }
  282. return DefaultImageModifier.default
  283. }
  284. /// `CacheSerializer` to convert image to data for storing in cache.
  285. public var cacheSerializer: CacheSerializer {
  286. if let item = lastMatchIgnoringAssociatedValue(.cacheSerializer(DefaultCacheSerializer.default)),
  287. case .cacheSerializer(let cacheSerializer) = item
  288. {
  289. return cacheSerializer
  290. }
  291. return DefaultCacheSerializer.default
  292. }
  293. /// Keep the existing image while setting another image to an image view.
  294. /// Or the placeholder will be used while downloading.
  295. public var keepCurrentImageWhileLoading: Bool {
  296. return contains { $0 <== .keepCurrentImageWhileLoading }
  297. }
  298. public var onlyLoadFirstFrame: Bool {
  299. return contains { $0 <== .onlyLoadFirstFrame }
  300. }
  301. public var cacheOriginalImage: Bool {
  302. return contains { $0 <== .cacheOriginalImage }
  303. }
  304. }