17 |
18 | Kingfisher is a lightweight, pure-Swift library for downloading and caching images from the web. This project is heavily inspired by the popular [SDWebImage](https://github.com/rs/SDWebImage). It provides you a chance to use a pure-Swift alternative in your next app.
19 |
20 | ## Features
21 |
22 | - [x] Asynchronous image downloading and caching.
23 | - [x] `URLSession`-based networking. Basic image processors and filters supplied.
24 | - [x] Multiple-layer cache for both memory and disk.
25 | - [x] Cancelable downloading and processing tasks to improve performance.
26 | - [x] Independent components. Use the downloader or caching system separately as you need.
27 | - [x] Prefetching images and showing them from cache later when necessary.
28 | - [x] Extensions for `UIImageView`, `NSImage` and `UIButton` to directly set an image from a URL.
29 | - [x] Built-in transition animation when setting images.
30 | - [x] Customizable placeholder while loading images.
31 | - [x] Extensible image processing and image format support.
32 |
33 | The simplest use-case is setting an image to an image view with the `UIImageView` extension:
34 |
35 | ```swift
36 | let url = URL(string: "url_of_your_image")
37 | imageView.kf.setImage(with: url)
38 | ```
39 |
40 | Kingfisher will download the image from `url`, send it to both the memory cache and the disk cache, and display it in `imageView`. When you use the same code later, the image will be retrieved from cache and shown immediately.
41 |
42 | For more examples of using Kingfisher, take a look at the [Cheat Sheet](https://github.com/onevcat/Kingfisher/wiki/Cheat-Sheet).
43 |
44 | ## Requirements
45 |
46 | - iOS 8.0+ / macOS 10.10+ / tvOS 9.0+ / watchOS 2.0+
47 | - Swift 4 (Kingfisher 4.x), Swift 3 (Kingfisher 3.x)
48 |
49 | Main development of Kingfisher is based on Swift 4. Only critical bug fixes will be applied to Kingfisher 3.x.
50 |
51 | - Kingfisher 4.0 Migration - Kingfisher 3.x should be source compatible to Kingfisher 4. The reason for a major update is that we need to specify the Swift version explicitly for Xcode. All deprecated methods in Kingfisher 3 has been removed, so please ensure you have no warning left before you migrate from Kingfisher 3 to Kingfisher 4. If you have any trouble in migrating, please open an issue to discuss.
52 | - [Kingfisher 3.0 Migration Guide](https://github.com/onevcat/Kingfisher/wiki/Kingfisher-3.0-Migration-Guide) - If you are upgrading to Kingfisher 3.x from an earlier version, please read this for more information.
53 |
54 | ## Next Steps
55 |
56 | We prepared a [wiki page](https://github.com/onevcat/Kingfisher/wiki). You can find tons of useful things there.
57 |
58 | * [Installation Guide](https://github.com/onevcat/Kingfisher/wiki/Installation-Guide) - Follow it to integrate Kingfisher into your project.
59 | * [Cheat Sheet](https://github.com/onevcat/Kingfisher/wiki/Cheat-Sheet)- Curious about what Kingfisher could do and how would it look like when used in your project? See this page for useful code snippets. If you are already familiar with Kingfisher, you could also learn new tricks to improve the way you use Kingfisher!
60 | * [API Reference](http://onevcat.github.io/Kingfisher/) - Lastly, please remember to read the full whenever you may need a more detailed reference.
61 |
62 | ## Other
63 |
64 | ### Future of Kingfisher
65 |
66 | I want to keep Kingfisher lightweight. This framework will focus on providing a simple solution for downloading and caching images. This doesn’t mean the framework can’t be improved. Kingfisher is far from perfect, so necessary and useful updates will be made to make it better.
67 |
68 | ### Developments and Tests
69 |
70 | Any contributing and pull requests are warmly welcome. However, before you plan to implement some features or try to fix an uncertain issue, it is recommended to open a discussion first.
71 |
72 | The test images are contained in another project to keep this project repo fast and slim. You could run `./setup.sh` in the root folder of Kingfisher to clone the test images when you need to run the tests target. It would be appreciated if your pull requests could build and with all tests green. :)
73 |
74 | ### About the logo
75 |
76 | The logo of Kingfisher is inspired by [Tangram (七巧板)](http://en.wikipedia.org/wiki/Tangram), a dissection puzzle consisting of seven flat shapes from China. I believe she's a kingfisher bird instead of a swift, but someone insists that she is a pigeon. I guess I should give her a name. Hi, guys, do you have any suggestions?
77 |
78 | ### Contact
79 |
80 | Follow and contact me on [Twitter](http://twitter.com/onevcat) or [Sina Weibo](http://weibo.com/onevcat). If you find an issue, just [open a ticket](https://github.com/onevcat/Kingfisher/issues/new). Pull requests are warmly welcome as well.
81 |
82 | ## Contributors
83 |
84 | This project exists thanks to all the people who contribute. [[Contribute]](https://github.com/onevcat/Kingfisher/blob/master/CONTRIBUTING.md).
85 |
86 |
87 |
88 | ## Backers
89 |
90 | Thank you to all our backers! 🙏 [[Become a backer](https://opencollective.com/kingfisher#backer)]
91 |
92 |
93 |
94 |
95 | ## Sponsors
96 |
97 | Support this project by becoming a sponsor. Your logo will show up here with a link to your website. [[Become a sponsor](https://opencollective.com/kingfisher#sponsor)]
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 | ### License
113 |
114 | Kingfisher is released under the MIT license. See LICENSE for details.
115 |
116 |
117 |
--------------------------------------------------------------------------------
/Example/Pods/Kingfisher/Sources/Box.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Box.swift
3 | // Kingfisher
4 | //
5 | // Created by Wei Wang on 2018/3/17.
6 | // Copyright (c) 2018 Wei Wang
7 | //
8 | // Permission is hereby granted, free of charge, to any person obtaining a copy
9 | // of this software and associated documentation files (the "Software"), to deal
10 | // in the Software without restriction, including without limitation the rights
11 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12 | // copies of the Software, and to permit persons to whom the Software is
13 | // furnished to do so, subject to the following conditions:
14 | //
15 | // The above copyright notice and this permission notice shall be included in
16 | // all copies or substantial portions of the Software.
17 | //
18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24 | // THE SOFTWARE.
25 |
26 | import Foundation
27 |
28 | class Box {
29 | let value: T
30 |
31 | init(_ value: T) {
32 | self.value = value
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/Example/Pods/Kingfisher/Sources/CacheSerializer.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CacheSerializer.swift
3 | // Kingfisher
4 | //
5 | // Created by Wei Wang on 2016/09/02.
6 | //
7 | // Copyright (c) 2018 Wei Wang
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 |
27 | import Foundation
28 |
29 | /// An `CacheSerializer` would be used to convert some data to an image object for
30 | /// retrieving from disk cache and vice versa for storing to disk cache.
31 | public protocol CacheSerializer {
32 |
33 | /// Get the serialized data from a provided image
34 | /// and optional original data for caching to disk.
35 | ///
36 | ///
37 | /// - parameter image: The image needed to be serialized.
38 | /// - parameter original: The original data which is just downloaded.
39 | /// If the image is retrieved from cache instead of
40 | /// downloaded, it will be `nil`.
41 | ///
42 | /// - returns: A data which will be stored to cache, or `nil` when no valid
43 | /// data could be serialized.
44 | func data(with image: Image, original: Data?) -> Data?
45 |
46 | /// Get an image deserialized from provided data.
47 | ///
48 | /// - parameter data: The data from which an image should be deserialized.
49 | /// - parameter options: Options for deserialization.
50 | ///
51 | /// - returns: An image deserialized or `nil` when no valid image
52 | /// could be deserialized.
53 | func image(with data: Data, options: KingfisherOptionsInfo?) -> Image?
54 | }
55 |
56 |
57 | /// `DefaultCacheSerializer` is a basic `CacheSerializer` used in default cache of
58 | /// Kingfisher. It could serialize and deserialize PNG, JEPG and GIF images. For
59 | /// image other than these formats, a normalized `pngRepresentation` will be used.
60 | public struct DefaultCacheSerializer: CacheSerializer {
61 |
62 | public static let `default` = DefaultCacheSerializer()
63 | private init() {}
64 |
65 | public func data(with image: Image, original: Data?) -> Data? {
66 | let imageFormat = original?.kf.imageFormat ?? .unknown
67 |
68 | let data: Data?
69 | switch imageFormat {
70 | case .PNG: data = image.kf.pngRepresentation()
71 | case .JPEG: data = image.kf.jpegRepresentation(compressionQuality: 1.0)
72 | case .GIF: data = image.kf.gifRepresentation()
73 | case .unknown: data = original ?? image.kf.normalized.kf.pngRepresentation()
74 | }
75 |
76 | return data
77 | }
78 |
79 | public func image(with data: Data, options: KingfisherOptionsInfo?) -> Image? {
80 | let options = options ?? KingfisherEmptyOptionsInfo
81 | return Kingfisher.image(
82 | data: data,
83 | scale: options.scaleFactor,
84 | preloadAllAnimationData: options.preloadAllAnimationData,
85 | onlyFirstFrame: options.onlyLoadFirstFrame)
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/Example/Pods/Kingfisher/Sources/Filter.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Filter.swift
3 | // Kingfisher
4 | //
5 | // Created by Wei Wang on 2016/08/31.
6 | //
7 | // Copyright (c) 2018 Wei Wang
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 |
27 |
28 |
29 | import CoreImage
30 | import Accelerate
31 |
32 | // Reuse the same CI Context for all CI drawing.
33 | private let ciContext = CIContext(options: nil)
34 |
35 | /// Transformer method which will be used in to provide a `Filter`.
36 | public typealias Transformer = (CIImage) -> CIImage?
37 |
38 | /// Supply a filter to create an `ImageProcessor`.
39 | public protocol CIImageProcessor: ImageProcessor {
40 | var filter: Filter { get }
41 | }
42 |
43 | extension CIImageProcessor {
44 | public func process(item: ImageProcessItem, options: KingfisherOptionsInfo) -> Image? {
45 | switch item {
46 | case .image(let image):
47 | return image.kf.apply(filter)
48 | case .data(_):
49 | return (DefaultImageProcessor.default >> self).process(item: item, options: options)
50 | }
51 | }
52 | }
53 |
54 | /// Wrapper for a `Transformer` of CIImage filters.
55 | public struct Filter {
56 |
57 | let transform: Transformer
58 |
59 | public init(tranform: @escaping Transformer) {
60 | self.transform = tranform
61 | }
62 |
63 | /// Tint filter which will apply a tint color to images.
64 | public static var tint: (Color) -> Filter = {
65 | color in
66 | Filter { input in
67 | let colorFilter = CIFilter(name: "CIConstantColorGenerator")!
68 | colorFilter.setValue(CIColor(color: color), forKey: kCIInputColorKey)
69 |
70 | let colorImage = colorFilter.outputImage
71 | let filter = CIFilter(name: "CISourceOverCompositing")!
72 | filter.setValue(colorImage, forKey: kCIInputImageKey)
73 | filter.setValue(input, forKey: kCIInputBackgroundImageKey)
74 | #if swift(>=4.0)
75 | return filter.outputImage?.cropped(to: input.extent)
76 | #else
77 | return filter.outputImage?.cropping(to: input.extent)
78 | #endif
79 | }
80 | }
81 |
82 | public typealias ColorElement = (CGFloat, CGFloat, CGFloat, CGFloat)
83 |
84 | /// Color control filter which will apply color control change to images.
85 | public static var colorControl: (ColorElement) -> Filter = { arg -> Filter in
86 | let (brightness, contrast, saturation, inputEV) = arg
87 | return Filter { input in
88 | let paramsColor = [kCIInputBrightnessKey: brightness,
89 | kCIInputContrastKey: contrast,
90 | kCIInputSaturationKey: saturation]
91 |
92 | let paramsExposure = [kCIInputEVKey: inputEV]
93 | #if swift(>=4.0)
94 | let blackAndWhite = input.applyingFilter("CIColorControls", parameters: paramsColor)
95 | return blackAndWhite.applyingFilter("CIExposureAdjust", parameters: paramsExposure)
96 | #else
97 | let blackAndWhite = input.applyingFilter("CIColorControls", withInputParameters: paramsColor)
98 | return blackAndWhite.applyingFilter("CIExposureAdjust", withInputParameters: paramsExposure)
99 | #endif
100 | }
101 |
102 | }
103 | }
104 |
105 | extension Kingfisher where Base: Image {
106 | /// Apply a `Filter` containing `CIImage` transformer to `self`.
107 | ///
108 | /// - parameter filter: The filter used to transform `self`.
109 | ///
110 | /// - returns: A transformed image by input `Filter`.
111 | ///
112 | /// - Note: Only CG-based images are supported. If any error happens during transforming, `self` will be returned.
113 | public func apply(_ filter: Filter) -> Image {
114 |
115 | guard let cgImage = cgImage else {
116 | assertionFailure("[Kingfisher] Tint image only works for CG-based image.")
117 | return base
118 | }
119 |
120 | let inputImage = CIImage(cgImage: cgImage)
121 | guard let outputImage = filter.transform(inputImage) else {
122 | return base
123 | }
124 |
125 | guard let result = ciContext.createCGImage(outputImage, from: outputImage.extent) else {
126 | assertionFailure("[Kingfisher] Can not make an tint image within context.")
127 | return base
128 | }
129 |
130 | #if os(macOS)
131 | return fixedForRetinaPixel(cgImage: result, to: size)
132 | #else
133 | return Image(cgImage: result, scale: base.scale, orientation: base.imageOrientation)
134 | #endif
135 | }
136 |
137 | }
138 |
--------------------------------------------------------------------------------
/Example/Pods/Kingfisher/Sources/FormatIndicatedCacheSerializer.swift:
--------------------------------------------------------------------------------
1 | //
2 | // RequestModifier.swift
3 | // Kingfisher
4 | //
5 | // Created by Junyu Kuang on 5/28/17.
6 | //
7 | // Copyright (c) 2018 Wei Wang
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 |
27 | import Foundation
28 |
29 | /// `FormatIndicatedCacheSerializer` let you indicate an image format for serialized caches.
30 | ///
31 | /// It could serialize and deserialize PNG, JEPG and GIF images. For
32 | /// image other than these formats, a normalized `pngRepresentation` will be used.
33 | ///
34 | /// Example:
35 | /// ````
36 | /// private let profileImageSize = CGSize(width: 44, height: 44)
37 | ///
38 | /// private let imageProcessor = RoundCornerImageProcessor(
39 | /// cornerRadius: profileImageSize.width / 2, targetSize: profileImageSize)
40 | ///
41 | /// private let optionsInfo: KingfisherOptionsInfo = [
42 | /// .cacheSerializer(FormatIndicatedCacheSerializer.png),
43 | /// .backgroundDecode, .processor(imageProcessor), .scaleFactor(UIScreen.main.scale)]
44 | ///
45 | /// extension UIImageView {
46 | /// func setProfileImage(with url: URL) {
47 | /// // Image will always cached as PNG format to preserve alpha channel for round rect.
48 | /// _ = kf.setImage(with: url, options: optionsInfo)
49 | /// }
50 | ///}
51 | /// ````
52 | public struct FormatIndicatedCacheSerializer: CacheSerializer {
53 |
54 | public static let png = FormatIndicatedCacheSerializer(imageFormat: .PNG)
55 | public static let jpeg = FormatIndicatedCacheSerializer(imageFormat: .JPEG)
56 | public static let gif = FormatIndicatedCacheSerializer(imageFormat: .GIF)
57 |
58 | /// The indicated image format.
59 | private let imageFormat: ImageFormat
60 |
61 | public func data(with image: Image, original: Data?) -> Data? {
62 |
63 | func imageData(withFormat imageFormat: ImageFormat) -> Data? {
64 | switch imageFormat {
65 | case .PNG: return image.kf.pngRepresentation()
66 | case .JPEG: return image.kf.jpegRepresentation(compressionQuality: 1.0)
67 | case .GIF: return image.kf.gifRepresentation()
68 | case .unknown: return nil
69 | }
70 | }
71 |
72 | // generate data with indicated image format
73 | if let data = imageData(withFormat: imageFormat) {
74 | return data
75 | }
76 |
77 | let originalFormat = original?.kf.imageFormat ?? .unknown
78 |
79 | // generate data with original image's format
80 | if originalFormat != imageFormat, let data = imageData(withFormat: originalFormat) {
81 | return data
82 | }
83 |
84 | return original ?? image.kf.normalized.kf.pngRepresentation()
85 | }
86 |
87 | /// Same implementation as `DefaultCacheSerializer`.
88 | public func image(with data: Data, options: KingfisherOptionsInfo?) -> Image? {
89 | let options = options ?? KingfisherEmptyOptionsInfo
90 | return Kingfisher.image(
91 | data: data,
92 | scale: options.scaleFactor,
93 | preloadAllAnimationData: options.preloadAllAnimationData,
94 | onlyFirstFrame: options.onlyLoadFirstFrame)
95 | }
96 | }
97 |
--------------------------------------------------------------------------------
/Example/Pods/Kingfisher/Sources/ImageModifier.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ImageModifier.swift
3 | // Kingfisher
4 | //
5 | // Created by Ethan Gill on 2017/11/28.
6 | //
7 | // Copyright (c) 2018 Ethan Gill
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 |
27 | import Foundation
28 |
29 | /// An `ImageModifier` can be used to change properties on an Image in between
30 | /// cache serialization and use of the image.
31 | public protocol ImageModifier {
32 | /// Modify an input `Image`.
33 | ///
34 | /// - parameter image: Image which will be modified by `self`
35 | ///
36 | /// - returns: The modified image.
37 | ///
38 | /// - Note: The return value will be unmodified if modifying is not possible on
39 | /// the current platform.
40 | /// - Note: Most modifiers support UIImage or NSImage, but not CGImage.
41 | func modify(_ image: Image) -> Image
42 | }
43 |
44 | extension ImageModifier {
45 | func modify(_ image: Image?) -> Image? {
46 | guard let image = image else {
47 | return nil
48 | }
49 | return modify(image)
50 | }
51 | }
52 |
53 | typealias ModifierImp = ((Image) -> Image)
54 |
55 | fileprivate struct GeneralModifier: ImageModifier {
56 | let identifier: String
57 | let m: ModifierImp
58 | func modify(_ image: Image) -> Image {
59 | return m(image)
60 | }
61 | }
62 |
63 | /// The default modifier.
64 | /// Does nothing and returns the image it was given
65 | public struct DefaultImageModifier: ImageModifier {
66 |
67 | /// A default `DefaultImageModifier` which can be used everywhere.
68 | public static let `default` = DefaultImageModifier()
69 |
70 | /// Initialize a `DefaultImageModifier`
71 | private init() {}
72 |
73 | /// Modify an input `Image`.
74 | ///
75 | /// - parameter image: Image which will be modified by `self`
76 | ///
77 | /// - returns: The modified image.
78 | ///
79 | /// - Note: See documentation of `ImageModifier` protocol for more.
80 | public func modify(_ image: Image) -> Image {
81 | return image
82 | }
83 | }
84 |
85 | /// A custom modifier.
86 | /// Can be initialized with a block to modify images in a custom way
87 | public struct AnyImageModifier: ImageModifier {
88 |
89 | /// A block which modifies images, or returns the original image
90 | /// if modification cannot be performed.
91 | let block: (Image) -> Image
92 |
93 | /// Initialize an `AnyImageModifier`
94 | public init(modify: @escaping (Image) -> Image) {
95 | block = modify
96 | }
97 |
98 | /// Modifies an input `Image` using this `AnyImageModifier`'s `block`.
99 | ///
100 | /// - parameter image: Image which will be modified by `self`
101 | ///
102 | /// - returns: The modified image.
103 | ///
104 | /// - Note: See documentation of `ImageModifier` protocol for more.
105 | public func modify(_ image: Image) -> Image {
106 | return block(image)
107 | }
108 | }
109 |
110 | #if os(iOS) || os(tvOS) || os(watchOS)
111 | import UIKit
112 |
113 | /// Modifier for setting the rendering mode of images.
114 | /// Only UI-based images are supported; if a non-UI image is passed in, the
115 | /// modifier will do nothing.
116 | public struct RenderingModeImageModifier: ImageModifier {
117 |
118 | /// The rendering mode to apply to the image.
119 | public let renderingMode: UIImageRenderingMode
120 |
121 | /// Initialize a `RenderingModeImageModifier`
122 | ///
123 | /// - parameter renderingMode: The rendering mode to apply to the image.
124 | /// Default is .automatic
125 | public init(renderingMode: UIImageRenderingMode = .automatic) {
126 | self.renderingMode = renderingMode
127 | }
128 |
129 | /// Modify an input `Image`.
130 | ///
131 | /// - parameter image: Image which will be modified by `self`
132 | ///
133 | /// - returns: The modified image.
134 | ///
135 | /// - Note: See documentation of `ImageModifier` protocol for more.
136 | public func modify(_ image: Image) -> Image {
137 | return image.withRenderingMode(renderingMode)
138 | }
139 | }
140 |
141 | /// Modifier for setting the `flipsForRightToLeftLayoutDirection` property of images.
142 | /// Only UI-based images are supported; if a non-UI image is passed in, the
143 | /// modifier will do nothing.
144 | public struct FlipsForRightToLeftLayoutDirectionImageModifier: ImageModifier {
145 | /// Initialize a `FlipsForRightToLeftLayoutDirectionImageModifier`
146 | ///
147 | /// - Note: On versions of iOS lower than 9.0, the image will be returned
148 | /// unmodified.
149 | public init() {}
150 |
151 | /// Modify an input `Image`.
152 | ///
153 | /// - parameter image: Image which will be modified by `self`
154 | ///
155 | /// - returns: The modified image.
156 | ///
157 | /// - Note: See documentation of `ImageModifier` protocol for more.
158 | public func modify(_ image: Image) -> Image {
159 | if #available(iOS 9.0, *) {
160 | return image.imageFlippedForRightToLeftLayoutDirection()
161 | } else {
162 | return image
163 | }
164 | }
165 | }
166 |
167 | /// Modifier for setting the `alignmentRectInsets` property of images.
168 | /// Only UI-based images are supported; if a non-UI image is passed in, the
169 | /// modifier will do nothing.
170 | public struct AlignmentRectInsetsImageModifier: ImageModifier {
171 |
172 | /// The alignment insets to apply to the image
173 | public let alignmentInsets: UIEdgeInsets
174 |
175 | /// Initialize a `AlignmentRectInsetsImageModifier`
176 | public init(alignmentInsets: UIEdgeInsets) {
177 | self.alignmentInsets = alignmentInsets
178 | }
179 |
180 | /// Modify an input `Image`.
181 | ///
182 | /// - parameter image: Image which will be modified by `self`
183 | ///
184 | /// - returns: The modified image.
185 | ///
186 | /// - Note: See documentation of `ImageModifier` protocol for more.
187 | public func modify(_ image: Image) -> Image {
188 | return image.withAlignmentRectInsets(alignmentInsets)
189 | }
190 | }
191 | #endif
192 |
--------------------------------------------------------------------------------
/Example/Pods/Kingfisher/Sources/ImagePrefetcher.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ImagePrefetcher.swift
3 | // Kingfisher
4 | //
5 | // Created by Claire Knight on 24/02/2016
6 | //
7 | // Copyright (c) 2018 Wei Wang
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 |
27 |
28 | #if os(macOS)
29 | import AppKit
30 | #else
31 | import UIKit
32 | #endif
33 |
34 |
35 | /// Progress update block of prefetcher.
36 | ///
37 | /// - `skippedResources`: An array of resources that are already cached before the prefetching starting.
38 | /// - `failedResources`: An array of resources that fail to be downloaded. It could because of being cancelled while downloading, encountered an error when downloading or the download not being started at all.
39 | /// - `completedResources`: An array of resources that are downloaded and cached successfully.
40 | public typealias PrefetcherProgressBlock = ((_ skippedResources: [Resource], _ failedResources: [Resource], _ completedResources: [Resource]) -> Void)
41 |
42 | /// Completion block of prefetcher.
43 | ///
44 | /// - `skippedResources`: An array of resources that are already cached before the prefetching starting.
45 | /// - `failedResources`: An array of resources that fail to be downloaded. It could because of being cancelled while downloading, encountered an error when downloading or the download not being started at all.
46 | /// - `completedResources`: An array of resources that are downloaded and cached successfully.
47 | public typealias PrefetcherCompletionHandler = ((_ skippedResources: [Resource], _ failedResources: [Resource], _ completedResources: [Resource]) -> Void)
48 |
49 | /// `ImagePrefetcher` represents a downloading manager for requesting many images via URLs, then caching them.
50 | /// This is useful when you know a list of image resources and want to download them before showing.
51 | public class ImagePrefetcher {
52 |
53 | /// The maximum concurrent downloads to use when prefetching images. Default is 5.
54 | public var maxConcurrentDownloads = 5
55 |
56 | private let prefetchResources: [Resource]
57 | private let optionsInfo: KingfisherOptionsInfo
58 | private var progressBlock: PrefetcherProgressBlock?
59 | private var completionHandler: PrefetcherCompletionHandler?
60 |
61 | private var tasks = [URL: RetrieveImageDownloadTask]()
62 |
63 | private var pendingResources: ArraySlice
64 | private var skippedResources = [Resource]()
65 | private var completedResources = [Resource]()
66 | private var failedResources = [Resource]()
67 |
68 | private var stopped = false
69 |
70 | // The created manager used for prefetch. We will use the helper method in manager.
71 | private let manager: KingfisherManager
72 |
73 | private var finished: Bool {
74 | return failedResources.count + skippedResources.count + completedResources.count == prefetchResources.count && self.tasks.isEmpty
75 | }
76 |
77 | /**
78 | Init an image prefetcher with an array of URLs.
79 |
80 | The prefetcher should be initiated with a list of prefetching targets. The URLs list is immutable.
81 | After you get a valid `ImagePrefetcher` object, you could call `start()` on it to begin the prefetching process.
82 | The images already cached will be skipped without downloading again.
83 |
84 | - parameter urls: The URLs which should be prefetched.
85 | - parameter options: A dictionary could control some behaviors. See `KingfisherOptionsInfo` for more.
86 | - parameter progressBlock: Called every time an resource is downloaded, skipped or cancelled.
87 | - parameter completionHandler: Called when the whole prefetching process finished.
88 |
89 | - returns: An `ImagePrefetcher` object.
90 |
91 | - Note: By default, the `ImageDownloader.defaultDownloader` and `ImageCache.defaultCache` will be used as
92 | the downloader and cache target respectively. You can specify another downloader or cache by using a customized `KingfisherOptionsInfo`.
93 | Both the progress and completion block will be invoked in main thread. The `CallbackDispatchQueue` in `optionsInfo` will be ignored in this method.
94 | */
95 | public convenience init(urls: [URL],
96 | options: KingfisherOptionsInfo? = nil,
97 | progressBlock: PrefetcherProgressBlock? = nil,
98 | completionHandler: PrefetcherCompletionHandler? = nil)
99 | {
100 | let resources: [Resource] = urls.map { $0 }
101 | self.init(resources: resources, options: options, progressBlock: progressBlock, completionHandler: completionHandler)
102 | }
103 |
104 | /**
105 | Init an image prefetcher with an array of resources.
106 |
107 | The prefetcher should be initiated with a list of prefetching targets. The resources list is immutable.
108 | After you get a valid `ImagePrefetcher` object, you could call `start()` on it to begin the prefetching process.
109 | The images already cached will be skipped without downloading again.
110 |
111 | - parameter resources: The resources which should be prefetched. See `Resource` type for more.
112 | - parameter options: A dictionary could control some behaviors. See `KingfisherOptionsInfo` for more.
113 | - parameter progressBlock: Called every time an resource is downloaded, skipped or cancelled.
114 | - parameter completionHandler: Called when the whole prefetching process finished.
115 |
116 | - returns: An `ImagePrefetcher` object.
117 |
118 | - Note: By default, the `ImageDownloader.defaultDownloader` and `ImageCache.defaultCache` will be used as
119 | the downloader and cache target respectively. You can specify another downloader or cache by using a customized `KingfisherOptionsInfo`.
120 | Both the progress and completion block will be invoked in main thread. The `CallbackDispatchQueue` in `optionsInfo` will be ignored in this method.
121 | */
122 | public init(resources: [Resource],
123 | options: KingfisherOptionsInfo? = nil,
124 | progressBlock: PrefetcherProgressBlock? = nil,
125 | completionHandler: PrefetcherCompletionHandler? = nil)
126 | {
127 | prefetchResources = resources
128 | pendingResources = ArraySlice(resources)
129 |
130 | // We want all callbacks from main queue, so we ignore the call back queue in options
131 | let optionsInfoWithoutQueue = options?.removeAllMatchesIgnoringAssociatedValue(.callbackDispatchQueue(nil))
132 | self.optionsInfo = optionsInfoWithoutQueue ?? KingfisherEmptyOptionsInfo
133 |
134 | let cache = self.optionsInfo.targetCache
135 | let downloader = self.optionsInfo.downloader
136 | manager = KingfisherManager(downloader: downloader, cache: cache)
137 |
138 | self.progressBlock = progressBlock
139 | self.completionHandler = completionHandler
140 | }
141 |
142 | /**
143 | Start to download the resources and cache them. This can be useful for background downloading
144 | of assets that are required for later use in an app. This code will not try and update any UI
145 | with the results of the process.
146 | */
147 | public func start()
148 | {
149 | // Since we want to handle the resources cancellation in main thread only.
150 | DispatchQueue.main.safeAsync {
151 |
152 | guard !self.stopped else {
153 | assertionFailure("You can not restart the same prefetcher. Try to create a new prefetcher.")
154 | self.handleComplete()
155 | return
156 | }
157 |
158 | guard self.maxConcurrentDownloads > 0 else {
159 | assertionFailure("There should be concurrent downloads value should be at least 1.")
160 | self.handleComplete()
161 | return
162 | }
163 |
164 | guard self.prefetchResources.count > 0 else {
165 | self.handleComplete()
166 | return
167 | }
168 |
169 | let initialConcurentDownloads = min(self.prefetchResources.count, self.maxConcurrentDownloads)
170 | for _ in 0 ..< initialConcurentDownloads {
171 | if let resource = self.pendingResources.popFirst() {
172 | self.startPrefetching(resource)
173 | }
174 | }
175 | }
176 | }
177 |
178 |
179 | /**
180 | Stop current downloading progress, and cancel any future prefetching activity that might be occuring.
181 | */
182 | public func stop() {
183 | DispatchQueue.main.safeAsync {
184 | if self.finished { return }
185 | self.stopped = true
186 | self.tasks.values.forEach { $0.cancel() }
187 | }
188 | }
189 |
190 | func downloadAndCache(_ resource: Resource) {
191 |
192 | let downloadTaskCompletionHandler: CompletionHandler = { (image, error, _, _) -> Void in
193 | self.tasks.removeValue(forKey: resource.downloadURL)
194 | if let _ = error {
195 | self.failedResources.append(resource)
196 | } else {
197 | self.completedResources.append(resource)
198 | }
199 |
200 | self.reportProgress()
201 | if self.stopped {
202 | if self.tasks.isEmpty {
203 | self.failedResources.append(contentsOf: self.pendingResources)
204 | self.handleComplete()
205 | }
206 | } else {
207 | self.reportCompletionOrStartNext()
208 | }
209 | }
210 |
211 | let downloadTask = manager.downloadAndCacheImage(
212 | with: resource.downloadURL,
213 | forKey: resource.cacheKey,
214 | retrieveImageTask: RetrieveImageTask(),
215 | progressBlock: nil,
216 | completionHandler: downloadTaskCompletionHandler,
217 | options: optionsInfo)
218 |
219 | if let downloadTask = downloadTask {
220 | tasks[resource.downloadURL] = downloadTask
221 | }
222 | }
223 |
224 | func append(cached resource: Resource) {
225 | skippedResources.append(resource)
226 |
227 | reportProgress()
228 | reportCompletionOrStartNext()
229 | }
230 |
231 | func startPrefetching(_ resource: Resource)
232 | {
233 | if optionsInfo.forceRefresh {
234 | downloadAndCache(resource)
235 | } else {
236 | let alreadyInCache = manager.cache.imageCachedType(forKey: resource.cacheKey,
237 | processorIdentifier: optionsInfo.processor.identifier).cached
238 | if alreadyInCache {
239 | append(cached: resource)
240 | } else {
241 | downloadAndCache(resource)
242 | }
243 | }
244 | }
245 |
246 | func reportProgress() {
247 | progressBlock?(skippedResources, failedResources, completedResources)
248 | }
249 |
250 | func reportCompletionOrStartNext() {
251 | DispatchQueue.main.async {
252 | if let resource = self.pendingResources.popFirst() {
253 | self.startPrefetching(resource)
254 | } else {
255 | guard self.tasks.isEmpty else { return }
256 | self.handleComplete()
257 | }
258 | }
259 | }
260 |
261 | func handleComplete() {
262 | completionHandler?(skippedResources, failedResources, completedResources)
263 | completionHandler = nil
264 | progressBlock = nil
265 | }
266 | }
267 |
--------------------------------------------------------------------------------
/Example/Pods/Kingfisher/Sources/ImageTransition.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ImageTransition.swift
3 | // Kingfisher
4 | //
5 | // Created by Wei Wang on 15/9/18.
6 | //
7 | // Copyright (c) 2018 Wei Wang
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 |
27 | #if os(macOS)
28 | // Not implemented for macOS and watchOS yet.
29 |
30 | import AppKit
31 |
32 | /// Image transition is not supported on macOS.
33 | public enum ImageTransition {
34 | case none
35 | var duration: TimeInterval {
36 | return 0
37 | }
38 | }
39 |
40 | #elseif os(watchOS)
41 | import UIKit
42 | /// Image transition is not supported on watchOS.
43 | public enum ImageTransition {
44 | case none
45 | var duration: TimeInterval {
46 | return 0
47 | }
48 | }
49 | #else
50 | import UIKit
51 |
52 | /**
53 | Transition effect which will be used when an image downloaded and set by `UIImageView` extension API in Kingfisher.
54 | You can assign an enum value with transition duration as an item in `KingfisherOptionsInfo`
55 | to enable the animation transition.
56 |
57 | Apple's UIViewAnimationOptions is used under the hood.
58 | For custom transition, you should specified your own transition options, animations and
59 | comletion handler as well.
60 | */
61 | public enum ImageTransition {
62 | /// No animation transistion.
63 | case none
64 |
65 | /// Fade in the loaded image.
66 | case fade(TimeInterval)
67 |
68 | /// Flip from left transition.
69 | case flipFromLeft(TimeInterval)
70 |
71 | /// Flip from right transition.
72 | case flipFromRight(TimeInterval)
73 |
74 | /// Flip from top transition.
75 | case flipFromTop(TimeInterval)
76 |
77 | /// Flip from bottom transition.
78 | case flipFromBottom(TimeInterval)
79 |
80 | /// Custom transition.
81 | case custom(duration: TimeInterval,
82 | options: UIViewAnimationOptions,
83 | animations: ((UIImageView, UIImage) -> Void)?,
84 | completion: ((Bool) -> Void)?)
85 |
86 | var duration: TimeInterval {
87 | switch self {
88 | case .none: return 0
89 | case .fade(let duration): return duration
90 |
91 | case .flipFromLeft(let duration): return duration
92 | case .flipFromRight(let duration): return duration
93 | case .flipFromTop(let duration): return duration
94 | case .flipFromBottom(let duration): return duration
95 |
96 | case .custom(let duration, _, _, _): return duration
97 | }
98 | }
99 |
100 | var animationOptions: UIViewAnimationOptions {
101 | switch self {
102 | case .none: return []
103 | case .fade(_): return .transitionCrossDissolve
104 |
105 | case .flipFromLeft(_): return .transitionFlipFromLeft
106 | case .flipFromRight(_): return .transitionFlipFromRight
107 | case .flipFromTop(_): return .transitionFlipFromTop
108 | case .flipFromBottom(_): return .transitionFlipFromBottom
109 |
110 | case .custom(_, let options, _, _): return options
111 | }
112 | }
113 |
114 | var animations: ((UIImageView, UIImage) -> Void)? {
115 | switch self {
116 | case .custom(_, _, let animations, _): return animations
117 | default: return { $0.image = $1 }
118 | }
119 | }
120 |
121 | var completion: ((Bool) -> Void)? {
122 | switch self {
123 | case .custom(_, _, _, let completion): return completion
124 | default: return nil
125 | }
126 | }
127 | }
128 | #endif
129 |
--------------------------------------------------------------------------------
/Example/Pods/Kingfisher/Sources/ImageView+Kingfisher.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ImageView+Kingfisher.swift
3 | // Kingfisher
4 | //
5 | // Created by Wei Wang on 15/4/6.
6 | //
7 | // Copyright (c) 2018 Wei Wang
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 |
27 |
28 | #if os(macOS)
29 | import AppKit
30 | #else
31 | import UIKit
32 | #endif
33 |
34 | // MARK: - Extension methods.
35 | /**
36 | * Set image to use from web.
37 | */
38 | extension Kingfisher where Base: ImageView {
39 | /**
40 | Set an image with a resource, a placeholder image, options, progress handler and completion handler.
41 |
42 | - parameter resource: Resource object contains information such as `cacheKey` and `downloadURL`.
43 | - parameter placeholder: A placeholder image when retrieving the image at URL.
44 | - parameter options: A dictionary could control some behaviors. See `KingfisherOptionsInfo` for more.
45 | - parameter progressBlock: Called when the image downloading progress gets updated.
46 | - parameter completionHandler: Called when the image retrieved and set.
47 |
48 | - returns: A task represents the retrieving process.
49 |
50 | - note: Both the `progressBlock` and `completionHandler` will be invoked in main thread.
51 | The `CallbackDispatchQueue` specified in `optionsInfo` will not be used in callbacks of this method.
52 |
53 | If `resource` is `nil`, the `placeholder` image will be set and
54 | `completionHandler` will be called with both `error` and `image` being `nil`.
55 | */
56 | @discardableResult
57 | public func setImage(with resource: Resource?,
58 | placeholder: Placeholder? = nil,
59 | options: KingfisherOptionsInfo? = nil,
60 | progressBlock: DownloadProgressBlock? = nil,
61 | completionHandler: CompletionHandler? = nil) -> RetrieveImageTask
62 | {
63 | guard let resource = resource else {
64 | self.placeholder = placeholder
65 | setWebURL(nil)
66 | completionHandler?(nil, nil, .none, nil)
67 | return .empty
68 | }
69 |
70 | var options = KingfisherManager.shared.defaultOptions + (options ?? KingfisherEmptyOptionsInfo)
71 | let noImageOrPlaceholderSet = base.image == nil && self.placeholder == nil
72 |
73 | if !options.keepCurrentImageWhileLoading || noImageOrPlaceholderSet { // Always set placeholder while there is no image/placehoer yet.
74 | self.placeholder = placeholder
75 | }
76 |
77 | let maybeIndicator = indicator
78 | maybeIndicator?.startAnimatingView()
79 |
80 | setWebURL(resource.downloadURL)
81 |
82 | if base.shouldPreloadAllAnimation() {
83 | options.append(.preloadAllAnimationData)
84 | }
85 |
86 | let task = KingfisherManager.shared.retrieveImage(
87 | with: resource,
88 | options: options,
89 | progressBlock: { receivedSize, totalSize in
90 | guard resource.downloadURL == self.webURL else {
91 | return
92 | }
93 | if let progressBlock = progressBlock {
94 | progressBlock(receivedSize, totalSize)
95 | }
96 | },
97 | completionHandler: {[weak base] image, error, cacheType, imageURL in
98 | DispatchQueue.main.safeAsync {
99 | maybeIndicator?.stopAnimatingView()
100 | guard let strongBase = base, imageURL == self.webURL else {
101 | completionHandler?(image, error, cacheType, imageURL)
102 | return
103 | }
104 |
105 | self.setImageTask(nil)
106 | guard let image = image else {
107 | completionHandler?(nil, error, cacheType, imageURL)
108 | return
109 | }
110 |
111 | guard let transitionItem = options.lastMatchIgnoringAssociatedValue(.transition(.none)),
112 | case .transition(let transition) = transitionItem, ( options.forceTransition || cacheType == .none) else
113 | {
114 | self.placeholder = nil
115 | strongBase.image = image
116 | completionHandler?(image, error, cacheType, imageURL)
117 | return
118 | }
119 |
120 | #if !os(macOS)
121 | UIView.transition(with: strongBase, duration: 0.0, options: [],
122 | animations: { maybeIndicator?.stopAnimatingView() },
123 | completion: { _ in
124 |
125 | self.placeholder = nil
126 | UIView.transition(with: strongBase, duration: transition.duration,
127 | options: [transition.animationOptions, .allowUserInteraction],
128 | animations: {
129 | // Set image property in the animation.
130 | transition.animations?(strongBase, image)
131 | },
132 | completion: { finished in
133 | transition.completion?(finished)
134 | completionHandler?(image, error, cacheType, imageURL)
135 | })
136 | })
137 | #endif
138 | }
139 | })
140 |
141 | setImageTask(task)
142 |
143 | return task
144 | }
145 |
146 | /**
147 | Cancel the image download task bounded to the image view if it is running.
148 | Nothing will happen if the downloading has already finished.
149 | */
150 | public func cancelDownloadTask() {
151 | imageTask?.cancel()
152 | }
153 | }
154 |
155 | // MARK: - Associated Object
156 | private var lastURLKey: Void?
157 | private var indicatorKey: Void?
158 | private var indicatorTypeKey: Void?
159 | private var placeholderKey: Void?
160 | private var imageTaskKey: Void?
161 |
162 | extension Kingfisher where Base: ImageView {
163 | /// Get the image URL binded to this image view.
164 | public var webURL: URL? {
165 | return objc_getAssociatedObject(base, &lastURLKey) as? URL
166 | }
167 |
168 | fileprivate func setWebURL(_ url: URL?) {
169 | objc_setAssociatedObject(base, &lastURLKey, url, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
170 | }
171 |
172 | /// Holds which indicator type is going to be used.
173 | /// Default is .none, means no indicator will be shown.
174 | public var indicatorType: IndicatorType {
175 | get {
176 | let indicator = objc_getAssociatedObject(base, &indicatorTypeKey) as? IndicatorType
177 | return indicator ?? .none
178 | }
179 |
180 | set {
181 | switch newValue {
182 | case .none:
183 | indicator = nil
184 | case .activity:
185 | indicator = ActivityIndicator()
186 | case .image(let data):
187 | indicator = ImageIndicator(imageData: data)
188 | case .custom(let anIndicator):
189 | indicator = anIndicator
190 | }
191 |
192 | objc_setAssociatedObject(base, &indicatorTypeKey, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
193 | }
194 | }
195 |
196 | /// Holds any type that conforms to the protocol `Indicator`.
197 | /// The protocol `Indicator` has a `view` property that will be shown when loading an image.
198 | /// It will be `nil` if `indicatorType` is `.none`.
199 | public fileprivate(set) var indicator: Indicator? {
200 | get {
201 | guard let box = objc_getAssociatedObject(base, &indicatorKey) as? Box else {
202 | return nil
203 | }
204 | return box.value
205 | }
206 |
207 | set {
208 | // Remove previous
209 | if let previousIndicator = indicator {
210 | previousIndicator.view.removeFromSuperview()
211 | }
212 |
213 | // Add new
214 | if var newIndicator = newValue {
215 | // Set default indicator frame if the view's frame not set.
216 | if newIndicator.view.frame == .zero {
217 | newIndicator.view.frame = base.frame
218 | }
219 | newIndicator.viewCenter = CGPoint(x: base.bounds.midX, y: base.bounds.midY)
220 | newIndicator.view.isHidden = true
221 | base.addSubview(newIndicator.view)
222 | }
223 |
224 | // Save in associated object
225 | // Wrap newValue with Box to workaround an issue that Swift does not recognize
226 | // and casting protocol for associate object correctly. https://github.com/onevcat/Kingfisher/issues/872
227 | objc_setAssociatedObject(base, &indicatorKey, newValue.map(Box.init), .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
228 | }
229 | }
230 |
231 | fileprivate var imageTask: RetrieveImageTask? {
232 | return objc_getAssociatedObject(base, &imageTaskKey) as? RetrieveImageTask
233 | }
234 |
235 | fileprivate func setImageTask(_ task: RetrieveImageTask?) {
236 | objc_setAssociatedObject(base, &imageTaskKey, task, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
237 | }
238 |
239 | public fileprivate(set) var placeholder: Placeholder? {
240 | get {
241 | return objc_getAssociatedObject(base, &placeholderKey) as? Placeholder
242 | }
243 |
244 | set {
245 | if let previousPlaceholder = placeholder {
246 | previousPlaceholder.remove(from: base)
247 | }
248 |
249 | if let newPlaceholder = newValue {
250 | newPlaceholder.add(to: base)
251 | } else {
252 | base.image = nil
253 | }
254 |
255 | objc_setAssociatedObject(base, &placeholderKey, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
256 | }
257 | }
258 | }
259 |
260 |
261 | @objc extension ImageView {
262 | func shouldPreloadAllAnimation() -> Bool { return true }
263 | }
264 |
--------------------------------------------------------------------------------
/Example/Pods/Kingfisher/Sources/Indicator.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Indicator.swift
3 | // Kingfisher
4 | //
5 | // Created by João D. Moreira on 30/08/16.
6 | //
7 | // Copyright (c) 2018 Wei Wang
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 |
27 | #if os(macOS)
28 | import AppKit
29 | #else
30 | import UIKit
31 | #endif
32 |
33 | #if os(macOS)
34 | public typealias IndicatorView = NSView
35 | #else
36 | public typealias IndicatorView = UIView
37 | #endif
38 |
39 | public enum IndicatorType {
40 | /// No indicator.
41 | case none
42 | /// Use system activity indicator.
43 | case activity
44 | /// Use an image as indicator. GIF is supported.
45 | case image(imageData: Data)
46 | /// Use a custom indicator, which conforms to the `Indicator` protocol.
47 | case custom(indicator: Indicator)
48 | }
49 |
50 | // MARK: - Indicator Protocol
51 | public protocol Indicator {
52 | func startAnimatingView()
53 | func stopAnimatingView()
54 |
55 | var viewCenter: CGPoint { get set }
56 | var view: IndicatorView { get }
57 | }
58 |
59 | extension Indicator {
60 | #if os(macOS)
61 | public var viewCenter: CGPoint {
62 | get {
63 | let frame = view.frame
64 | return CGPoint(x: frame.origin.x + frame.size.width / 2.0, y: frame.origin.y + frame.size.height / 2.0 )
65 | }
66 | set {
67 | let frame = view.frame
68 | let newFrame = CGRect(x: newValue.x - frame.size.width / 2.0,
69 | y: newValue.y - frame.size.height / 2.0,
70 | width: frame.size.width,
71 | height: frame.size.height)
72 | view.frame = newFrame
73 | }
74 | }
75 | #else
76 | public var viewCenter: CGPoint {
77 | get {
78 | return view.center
79 | }
80 | set {
81 | view.center = newValue
82 | }
83 | }
84 | #endif
85 | }
86 |
87 | // MARK: - ActivityIndicator
88 | // Displays a NSProgressIndicator / UIActivityIndicatorView
89 | final class ActivityIndicator: Indicator {
90 |
91 | #if os(macOS)
92 | private let activityIndicatorView: NSProgressIndicator
93 | #else
94 | private let activityIndicatorView: UIActivityIndicatorView
95 | #endif
96 | private var animatingCount = 0
97 |
98 | var view: IndicatorView {
99 | return activityIndicatorView
100 | }
101 |
102 | func startAnimatingView() {
103 | animatingCount += 1
104 | // Alrady animating
105 | if animatingCount == 1 {
106 | #if os(macOS)
107 | activityIndicatorView.startAnimation(nil)
108 | #else
109 | activityIndicatorView.startAnimating()
110 | #endif
111 | activityIndicatorView.isHidden = false
112 | }
113 | }
114 |
115 | func stopAnimatingView() {
116 | animatingCount = max(animatingCount - 1, 0)
117 | if animatingCount == 0 {
118 | #if os(macOS)
119 | activityIndicatorView.stopAnimation(nil)
120 | #else
121 | activityIndicatorView.stopAnimating()
122 | #endif
123 | activityIndicatorView.isHidden = true
124 | }
125 | }
126 |
127 | init() {
128 | #if os(macOS)
129 | activityIndicatorView = NSProgressIndicator(frame: CGRect(x: 0, y: 0, width: 16, height: 16))
130 | activityIndicatorView.controlSize = .small
131 | activityIndicatorView.style = .spinning
132 | #else
133 | #if os(tvOS)
134 | let indicatorStyle = UIActivityIndicatorViewStyle.white
135 | #else
136 | let indicatorStyle = UIActivityIndicatorViewStyle.gray
137 | #endif
138 | activityIndicatorView = UIActivityIndicatorView(activityIndicatorStyle:indicatorStyle)
139 | activityIndicatorView.autoresizingMask = [.flexibleLeftMargin, .flexibleRightMargin, .flexibleBottomMargin, .flexibleTopMargin]
140 | #endif
141 | }
142 | }
143 |
144 | // MARK: - ImageIndicator
145 | // Displays an ImageView. Supports gif
146 | final class ImageIndicator: Indicator {
147 | private let animatedImageIndicatorView: ImageView
148 |
149 | var view: IndicatorView {
150 | return animatedImageIndicatorView
151 | }
152 |
153 | init?(imageData data: Data, processor: ImageProcessor = DefaultImageProcessor.default, options: KingfisherOptionsInfo = KingfisherEmptyOptionsInfo) {
154 |
155 | var options = options
156 | // Use normal image view to show animations, so we need to preload all animation data.
157 | if !options.preloadAllAnimationData {
158 | options.append(.preloadAllAnimationData)
159 | }
160 |
161 | guard let image = processor.process(item: .data(data), options: options) else {
162 | return nil
163 | }
164 |
165 | animatedImageIndicatorView = ImageView()
166 | animatedImageIndicatorView.image = image
167 | animatedImageIndicatorView.frame = CGRect(x: 0, y: 0, width: image.size.width, height: image.size.height)
168 |
169 | #if os(macOS)
170 | // Need for gif to animate on macOS
171 | self.animatedImageIndicatorView.imageScaling = .scaleNone
172 | self.animatedImageIndicatorView.canDrawSubviewsIntoLayer = true
173 | #else
174 | animatedImageIndicatorView.contentMode = .center
175 | animatedImageIndicatorView.autoresizingMask = [.flexibleLeftMargin,
176 | .flexibleRightMargin,
177 | .flexibleBottomMargin,
178 | .flexibleTopMargin]
179 | #endif
180 | }
181 |
182 | func startAnimatingView() {
183 | #if os(macOS)
184 | animatedImageIndicatorView.animates = true
185 | #else
186 | animatedImageIndicatorView.startAnimating()
187 | #endif
188 | animatedImageIndicatorView.isHidden = false
189 | }
190 |
191 | func stopAnimatingView() {
192 | #if os(macOS)
193 | animatedImageIndicatorView.animates = false
194 | #else
195 | animatedImageIndicatorView.stopAnimating()
196 | #endif
197 | animatedImageIndicatorView.isHidden = true
198 | }
199 | }
200 |
--------------------------------------------------------------------------------
/Example/Pods/Kingfisher/Sources/Kingfisher.h:
--------------------------------------------------------------------------------
1 | //
2 | // Kingfisher.h
3 | // Kingfisher
4 | //
5 | // Created by Wei Wang on 15/4/6.
6 | //
7 | // Copyright (c) 2018 Wei Wang
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 |
27 | #import
28 |
29 | //! Project version number for Kingfisher.
30 | FOUNDATION_EXPORT double KingfisherVersionNumber;
31 |
32 | //! Project version string for Kingfisher.
33 | FOUNDATION_EXPORT const unsigned char KingfisherVersionString[];
34 |
35 | // In this header, you should import all the public headers of your framework using statements like #import
36 |
37 |
38 |
--------------------------------------------------------------------------------
/Example/Pods/Kingfisher/Sources/Kingfisher.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Kingfisher.swift
3 | // Kingfisher
4 | //
5 | // Created by Wei Wang on 16/9/14.
6 | //
7 | // Copyright (c) 2018 Wei Wang
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 |
27 | import Foundation
28 | import ImageIO
29 |
30 | #if os(macOS)
31 | import AppKit
32 | public typealias Image = NSImage
33 | public typealias View = NSView
34 | public typealias Color = NSColor
35 | public typealias ImageView = NSImageView
36 | public typealias Button = NSButton
37 | #else
38 | import UIKit
39 | public typealias Image = UIImage
40 | public typealias Color = UIColor
41 | #if !os(watchOS)
42 | public typealias ImageView = UIImageView
43 | public typealias View = UIView
44 | public typealias Button = UIButton
45 | #endif
46 | #endif
47 |
48 | public final class Kingfisher {
49 | public let base: Base
50 | public init(_ base: Base) {
51 | self.base = base
52 | }
53 | }
54 |
55 | /**
56 | A type that has Kingfisher extensions.
57 | */
58 | public protocol KingfisherCompatible {
59 | associatedtype CompatibleType
60 | var kf: CompatibleType { get }
61 | }
62 |
63 | public extension KingfisherCompatible {
64 | public var kf: Kingfisher {
65 | get { return Kingfisher(self) }
66 | }
67 | }
68 |
69 | extension Image: KingfisherCompatible { }
70 | #if !os(watchOS)
71 | extension ImageView: KingfisherCompatible { }
72 | extension Button: KingfisherCompatible { }
73 | #endif
74 |
--------------------------------------------------------------------------------
/Example/Pods/Kingfisher/Sources/KingfisherManager.swift:
--------------------------------------------------------------------------------
1 | //
2 | // KingfisherManager.swift
3 | // Kingfisher
4 | //
5 | // Created by Wei Wang on 15/4/6.
6 | //
7 | // Copyright (c) 2018 Wei Wang
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 |
27 | #if os(macOS)
28 | import AppKit
29 | #else
30 | import UIKit
31 | #endif
32 |
33 | public typealias DownloadProgressBlock = ((_ receivedSize: Int64, _ totalSize: Int64) -> Void)
34 | public typealias CompletionHandler = ((_ image: Image?, _ error: NSError?, _ cacheType: CacheType, _ imageURL: URL?) -> Void)
35 |
36 | /// RetrieveImageTask represents a task of image retrieving process.
37 | /// It contains an async task of getting image from disk and from network.
38 | public final class RetrieveImageTask {
39 |
40 | public static let empty = RetrieveImageTask()
41 |
42 | // If task is canceled before the download task started (which means the `downloadTask` is nil),
43 | // the download task should not begin.
44 | var cancelledBeforeDownloadStarting: Bool = false
45 |
46 | /// The network retrieve task in this image task.
47 | public var downloadTask: RetrieveImageDownloadTask?
48 |
49 | /**
50 | Cancel current task. If this task is already done, do nothing.
51 | */
52 | public func cancel() {
53 | if let downloadTask = downloadTask {
54 | downloadTask.cancel()
55 | } else {
56 | cancelledBeforeDownloadStarting = true
57 | }
58 | }
59 | }
60 |
61 | /// Error domain of Kingfisher
62 | public let KingfisherErrorDomain = "com.onevcat.Kingfisher.Error"
63 |
64 | /// Main manager class of Kingfisher. It connects Kingfisher downloader and cache.
65 | /// You can use this class to retrieve an image via a specified URL from web or cache.
66 | public class KingfisherManager {
67 |
68 | /// Shared manager used by the extensions across Kingfisher.
69 | public static let shared = KingfisherManager()
70 |
71 | /// Cache used by this manager
72 | public var cache: ImageCache
73 |
74 | /// Downloader used by this manager
75 | public var downloader: ImageDownloader
76 |
77 | /// Default options used by the manager. This option will be used in
78 | /// Kingfisher manager related methods, including all image view and
79 | /// button extension methods. You can also passing the options per image by
80 | /// sending an `options` parameter to Kingfisher's APIs, the per image option
81 | /// will overwrite the default ones if exist.
82 | ///
83 | /// - Note: This option will not be applied to independent using of `ImageDownloader` or `ImageCache`.
84 | public var defaultOptions = KingfisherEmptyOptionsInfo
85 |
86 | var currentDefaultOptions: KingfisherOptionsInfo {
87 | return [.downloader(downloader), .targetCache(cache)] + defaultOptions
88 | }
89 |
90 | convenience init() {
91 | self.init(downloader: .default, cache: .default)
92 | }
93 |
94 | init(downloader: ImageDownloader, cache: ImageCache) {
95 | self.downloader = downloader
96 | self.cache = cache
97 | }
98 |
99 | /**
100 | Get an image with resource.
101 | If KingfisherOptions.None is used as `options`, Kingfisher will seek the image in memory and disk first.
102 | If not found, it will download the image at `resource.downloadURL` and cache it with `resource.cacheKey`.
103 | These default behaviors could be adjusted by passing different options. See `KingfisherOptions` for more.
104 |
105 | - parameter resource: Resource object contains information such as `cacheKey` and `downloadURL`.
106 | - parameter options: A dictionary could control some behaviors. See `KingfisherOptionsInfo` for more.
107 | - parameter progressBlock: Called every time downloaded data changed. This could be used as a progress UI.
108 | - parameter completionHandler: Called when the whole retrieving process finished.
109 |
110 | - returns: A `RetrieveImageTask` task object. You can use this object to cancel the task.
111 | */
112 | @discardableResult
113 | public func retrieveImage(with resource: Resource,
114 | options: KingfisherOptionsInfo?,
115 | progressBlock: DownloadProgressBlock?,
116 | completionHandler: CompletionHandler?) -> RetrieveImageTask
117 | {
118 | let task = RetrieveImageTask()
119 | let options = currentDefaultOptions + (options ?? KingfisherEmptyOptionsInfo)
120 | if options.forceRefresh {
121 | _ = downloadAndCacheImage(
122 | with: resource.downloadURL,
123 | forKey: resource.cacheKey,
124 | retrieveImageTask: task,
125 | progressBlock: progressBlock,
126 | completionHandler: completionHandler,
127 | options: options)
128 | } else {
129 | tryToRetrieveImageFromCache(
130 | forKey: resource.cacheKey,
131 | with: resource.downloadURL,
132 | retrieveImageTask: task,
133 | progressBlock: progressBlock,
134 | completionHandler: completionHandler,
135 | options: options)
136 | }
137 |
138 | return task
139 | }
140 |
141 | @discardableResult
142 | func downloadAndCacheImage(with url: URL,
143 | forKey key: String,
144 | retrieveImageTask: RetrieveImageTask,
145 | progressBlock: DownloadProgressBlock?,
146 | completionHandler: CompletionHandler?,
147 | options: KingfisherOptionsInfo) -> RetrieveImageDownloadTask?
148 | {
149 | let downloader = options.downloader
150 | return downloader.downloadImage(with: url, retrieveImageTask: retrieveImageTask, options: options,
151 | progressBlock: { receivedSize, totalSize in
152 | progressBlock?(receivedSize, totalSize)
153 | },
154 | completionHandler: { image, error, imageURL, originalData in
155 |
156 | let targetCache = options.targetCache
157 | if let error = error, error.code == KingfisherError.notModified.rawValue {
158 | // Not modified. Try to find the image from cache.
159 | // (The image should be in cache. It should be guaranteed by the framework users.)
160 | targetCache.retrieveImage(forKey: key, options: options, completionHandler: { (cacheImage, cacheType) -> Void in
161 | completionHandler?(cacheImage, nil, cacheType, url)
162 | })
163 | return
164 | }
165 |
166 | if let image = image, let originalData = originalData {
167 | targetCache.store(image,
168 | original: originalData,
169 | forKey: key,
170 | processorIdentifier:options.processor.identifier,
171 | cacheSerializer: options.cacheSerializer,
172 | toDisk: !options.cacheMemoryOnly,
173 | completionHandler: nil)
174 | if options.cacheOriginalImage && options.processor != DefaultImageProcessor.default {
175 | let originalCache = options.originalCache
176 | let defaultProcessor = DefaultImageProcessor.default
177 | if let originalImage = defaultProcessor.process(item: .data(originalData), options: options) {
178 | originalCache.store(originalImage,
179 | original: originalData,
180 | forKey: key,
181 | processorIdentifier: defaultProcessor.identifier,
182 | cacheSerializer: options.cacheSerializer,
183 | toDisk: !options.cacheMemoryOnly,
184 | completionHandler: nil)
185 | }
186 | }
187 | }
188 |
189 | completionHandler?(image, error, .none, url)
190 |
191 | })
192 | }
193 |
194 | func tryToRetrieveImageFromCache(forKey key: String,
195 | with url: URL,
196 | retrieveImageTask: RetrieveImageTask,
197 | progressBlock: DownloadProgressBlock?,
198 | completionHandler: CompletionHandler?,
199 | options: KingfisherOptionsInfo)
200 | {
201 |
202 | let diskTaskCompletionHandler: CompletionHandler = { (image, error, cacheType, imageURL) -> Void in
203 | completionHandler?(image, error, cacheType, imageURL)
204 | }
205 |
206 | func handleNoCache() {
207 | if options.onlyFromCache {
208 | let error = NSError(domain: KingfisherErrorDomain, code: KingfisherError.notCached.rawValue, userInfo: nil)
209 | diskTaskCompletionHandler(nil, error, .none, url)
210 | return
211 | }
212 | self.downloadAndCacheImage(
213 | with: url,
214 | forKey: key,
215 | retrieveImageTask: retrieveImageTask,
216 | progressBlock: progressBlock,
217 | completionHandler: diskTaskCompletionHandler,
218 | options: options)
219 |
220 | }
221 |
222 | let targetCache = options.targetCache
223 | // First, try to get the exactly image from cache
224 | targetCache.retrieveImage(forKey: key, options: options) { image, cacheType in
225 | // If found, we could finish now.
226 | if image != nil {
227 | diskTaskCompletionHandler(image, nil, cacheType, url)
228 | return
229 | }
230 |
231 | // If not found, and we are using a default processor, download it!
232 | let processor = options.processor
233 | guard processor != DefaultImageProcessor.default else {
234 | handleNoCache()
235 | return
236 | }
237 |
238 | // If processor is not the default one, we have a chance to check whether
239 | // the original image is already in cache.
240 | let originalCache = options.originalCache
241 | let optionsWithoutProcessor = options.removeAllMatchesIgnoringAssociatedValue(.processor(processor))
242 | originalCache.retrieveImage(forKey: key, options: optionsWithoutProcessor) { image, cacheType in
243 | // If we found the original image, there is no need to download it again.
244 | // We could just apply processor to it now.
245 | guard let image = image else {
246 | handleNoCache()
247 | return
248 | }
249 |
250 | guard let processedImage = processor.process(item: .image(image), options: options) else {
251 | diskTaskCompletionHandler(nil, nil, .none, url)
252 | return
253 | }
254 | targetCache.store(processedImage,
255 | original: nil,
256 | forKey: key,
257 | processorIdentifier:options.processor.identifier,
258 | cacheSerializer: options.cacheSerializer,
259 | toDisk: !options.cacheMemoryOnly,
260 | completionHandler: nil)
261 | diskTaskCompletionHandler(processedImage, nil, .none, url)
262 | }
263 | }
264 | }
265 | }
266 |
--------------------------------------------------------------------------------
/Example/Pods/Kingfisher/Sources/KingfisherOptionsInfo.swift:
--------------------------------------------------------------------------------
1 | //
2 | // KingfisherOptionsInfo.swift
3 | // Kingfisher
4 | //
5 | // Created by Wei Wang on 15/4/23.
6 | //
7 | // Copyright (c) 2018 Wei Wang
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 |
27 | #if os(macOS)
28 | import AppKit
29 | #else
30 | import UIKit
31 | #endif
32 |
33 |
34 | /**
35 | * KingfisherOptionsInfo is a typealias for [KingfisherOptionsInfoItem]. You can use the enum of option item with value to control some behaviors of Kingfisher.
36 | */
37 | public typealias KingfisherOptionsInfo = [KingfisherOptionsInfoItem]
38 | let KingfisherEmptyOptionsInfo = [KingfisherOptionsInfoItem]()
39 |
40 | /**
41 | Items could be added into KingfisherOptionsInfo.
42 | */
43 | public enum KingfisherOptionsInfoItem {
44 | /// The associated value of this member should be an ImageCache object. Kingfisher will use the specified
45 | /// cache object when handling related operations, including trying to retrieve the cached images and store
46 | /// the downloaded image to it.
47 | case targetCache(ImageCache)
48 |
49 | /// Cache for storing and retrieving original image.
50 | /// Preferred prior to targetCache for storing and retrieving original images if specified.
51 | /// Only used if a non-default image processor is involved.
52 | case originalCache(ImageCache)
53 |
54 | /// The associated value of this member should be an ImageDownloader object. Kingfisher will use this
55 | /// downloader to download the images.
56 | case downloader(ImageDownloader)
57 |
58 | /// Member for animation transition when using UIImageView. Kingfisher will use the `ImageTransition` of
59 | /// this enum to animate the image in if it is downloaded from web. The transition will not happen when the
60 | /// image is retrieved from either memory or disk cache by default. If you need to do the transition even when
61 | /// the image being retrieved from cache, set `ForceTransition` as well.
62 | case transition(ImageTransition)
63 |
64 | /// Associated `Float` value will be set as the priority of image download task. The value for it should be
65 | /// between 0.0~1.0. If this option not set, the default value (`NSURLSessionTaskPriorityDefault`) will be used.
66 | case downloadPriority(Float)
67 |
68 | /// If set, `Kingfisher` will ignore the cache and try to fire a download task for the resource.
69 | case forceRefresh
70 |
71 | /// If set, `Kingfisher` will try to retrieve the image from memory cache first. If the image is not in memory
72 | /// cache, then it will ignore the disk cache but download the image again from network. This is useful when
73 | /// you want to display a changeable image behind the same url, while avoiding download it again and again.
74 | case fromMemoryCacheOrRefresh
75 |
76 | /// If set, setting the image to an image view will happen with transition even when retrieved from cache.
77 | /// See `Transition` option for more.
78 | case forceTransition
79 |
80 | /// If set, `Kingfisher` will only cache the value in memory but not in disk.
81 | case cacheMemoryOnly
82 |
83 | /// If set, `Kingfisher` will only try to retrieve the image from cache not from network.
84 | case onlyFromCache
85 |
86 | /// Decode the image in background thread before using.
87 | case backgroundDecode
88 |
89 | /// The associated value of this member will be used as the target queue of dispatch callbacks when
90 | /// retrieving images from cache. If not set, `Kingfisher` will use main quese for callbacks.
91 | case callbackDispatchQueue(DispatchQueue?)
92 |
93 | /// The associated value of this member will be used as the scale factor when converting retrieved data to an image.
94 | /// It is the image scale, instead of your screen scale. You may need to specify the correct scale when you dealing
95 | /// with 2x or 3x retina images.
96 | case scaleFactor(CGFloat)
97 |
98 | /// Whether all the animated image data should be preloaded. Default it false, which means following frames will be
99 | /// loaded on need. If true, all the animated image data will be loaded and decoded into memory. This option is mainly
100 | /// used for back compatibility internally. You should not set it directly. `AnimatedImageView` will not preload
101 | /// all data, while a normal image view (`UIImageView` or `NSImageView`) will load all data. Choose to use
102 | /// corresponding image view type instead of setting this option.
103 | case preloadAllAnimationData
104 |
105 | /// The `ImageDownloadRequestModifier` contained will be used to change the request before it being sent.
106 | /// This is the last chance you can modify the request. You can modify the request for some customizing purpose,
107 | /// such as adding auth token to the header, do basic HTTP auth or something like url mapping. The original request
108 | /// will be sent without any modification by default.
109 | case requestModifier(ImageDownloadRequestModifier)
110 |
111 | /// Processor for processing when the downloading finishes, a processor will convert the downloaded data to an image
112 | /// and/or apply some filter on it. If a cache is connected to the downloader (it happens when you are using
113 | /// KingfisherManager or the image extension methods), the converted image will also be sent to cache as well as the
114 | /// image view. `DefaultImageProcessor.default` will be used by default.
115 | case processor(ImageProcessor)
116 |
117 | /// Supply an `CacheSerializer` to convert some data to an image object for
118 | /// retrieving from disk cache or vice versa for storing to disk cache.
119 | /// `DefaultCacheSerializer.default` will be used by default.
120 | case cacheSerializer(CacheSerializer)
121 |
122 | /// Modifier for modifying an image right before it is used.
123 | /// If the image was fetched directly from the downloader, the modifier will
124 | /// run directly after the processor.
125 | /// If the image is being fetched from a cache, the modifier will run after
126 | /// the cacheSerializer.
127 | /// Use `ImageModifier` when you need to set properties on a concrete type
128 | /// of `Image`, such as a `UIImage`, that do not persist when caching the image.
129 | case imageModifier(ImageModifier)
130 |
131 | /// Keep the existing image while setting another image to an image view.
132 | /// By setting this option, the placeholder image parameter of imageview extension method
133 | /// will be ignored and the current image will be kept while loading or downloading the new image.
134 | case keepCurrentImageWhileLoading
135 |
136 | /// If set, Kingfisher will only load the first frame from a animated image data file as a single image.
137 | /// Loading a lot of animated images may take too much memory. It will be useful when you want to display a
138 | /// static preview of the first frame from a animated image.
139 | /// This option will be ignored if the target image is not animated image data.
140 | case onlyLoadFirstFrame
141 |
142 | /// If set and an `ImageProcessor` is used, Kingfisher will try to cache both
143 | /// the final result and original image. Kingfisher will have a chance to use
144 | /// the original image when another processor is applied to the same resouce,
145 | /// instead of downloading it again.
146 | case cacheOriginalImage
147 | }
148 |
149 | precedencegroup ItemComparisonPrecedence {
150 | associativity: none
151 | higherThan: LogicalConjunctionPrecedence
152 | }
153 |
154 | infix operator <== : ItemComparisonPrecedence
155 |
156 | // This operator returns true if two `KingfisherOptionsInfoItem` enum is the same, without considering the associated values.
157 | func <== (lhs: KingfisherOptionsInfoItem, rhs: KingfisherOptionsInfoItem) -> Bool {
158 | switch (lhs, rhs) {
159 | case (.targetCache(_), .targetCache(_)): return true
160 | case (.originalCache(_), .originalCache(_)): return true
161 | case (.downloader(_), .downloader(_)): return true
162 | case (.transition(_), .transition(_)): return true
163 | case (.downloadPriority(_), .downloadPriority(_)): return true
164 | case (.forceRefresh, .forceRefresh): return true
165 | case (.fromMemoryCacheOrRefresh, .fromMemoryCacheOrRefresh): return true
166 | case (.forceTransition, .forceTransition): return true
167 | case (.cacheMemoryOnly, .cacheMemoryOnly): return true
168 | case (.onlyFromCache, .onlyFromCache): return true
169 | case (.backgroundDecode, .backgroundDecode): return true
170 | case (.callbackDispatchQueue(_), .callbackDispatchQueue(_)): return true
171 | case (.scaleFactor(_), .scaleFactor(_)): return true
172 | case (.preloadAllAnimationData, .preloadAllAnimationData): return true
173 | case (.requestModifier(_), .requestModifier(_)): return true
174 | case (.processor(_), .processor(_)): return true
175 | case (.cacheSerializer(_), .cacheSerializer(_)): return true
176 | case (.imageModifier(_), .imageModifier(_)): return true
177 | case (.keepCurrentImageWhileLoading, .keepCurrentImageWhileLoading): return true
178 | case (.onlyLoadFirstFrame, .onlyLoadFirstFrame): return true
179 | case (.cacheOriginalImage, .cacheOriginalImage): return true
180 | default: return false
181 | }
182 | }
183 |
184 |
185 | extension Collection where Iterator.Element == KingfisherOptionsInfoItem {
186 | func lastMatchIgnoringAssociatedValue(_ target: Iterator.Element) -> Iterator.Element? {
187 | return reversed().first { $0 <== target }
188 | }
189 |
190 | func removeAllMatchesIgnoringAssociatedValue(_ target: Iterator.Element) -> [Iterator.Element] {
191 | return filter { !($0 <== target) }
192 | }
193 | }
194 |
195 | public extension Collection where Iterator.Element == KingfisherOptionsInfoItem {
196 | /// The target `ImageCache` which is used.
197 | public var targetCache: ImageCache {
198 | if let item = lastMatchIgnoringAssociatedValue(.targetCache(.default)),
199 | case .targetCache(let cache) = item
200 | {
201 | return cache
202 | }
203 | return ImageCache.default
204 | }
205 |
206 | /// The original `ImageCache` which is used.
207 | public var originalCache: ImageCache {
208 | if let item = lastMatchIgnoringAssociatedValue(.originalCache(.default)),
209 | case .originalCache(let cache) = item
210 | {
211 | return cache
212 | }
213 | return targetCache
214 | }
215 |
216 | /// The `ImageDownloader` which is specified.
217 | public var downloader: ImageDownloader {
218 | if let item = lastMatchIgnoringAssociatedValue(.downloader(.default)),
219 | case .downloader(let downloader) = item
220 | {
221 | return downloader
222 | }
223 | return ImageDownloader.default
224 | }
225 |
226 | /// Member for animation transition when using UIImageView.
227 | public var transition: ImageTransition {
228 | if let item = lastMatchIgnoringAssociatedValue(.transition(.none)),
229 | case .transition(let transition) = item
230 | {
231 | return transition
232 | }
233 | return ImageTransition.none
234 | }
235 |
236 | /// A `Float` value set as the priority of image download task. The value for it should be
237 | /// between 0.0~1.0.
238 | public var downloadPriority: Float {
239 | if let item = lastMatchIgnoringAssociatedValue(.downloadPriority(0)),
240 | case .downloadPriority(let priority) = item
241 | {
242 | return priority
243 | }
244 | return URLSessionTask.defaultPriority
245 | }
246 |
247 | /// Whether an image will be always downloaded again or not.
248 | public var forceRefresh: Bool {
249 | return contains{ $0 <== .forceRefresh }
250 | }
251 |
252 | /// Whether an image should be got only from memory cache or download.
253 | public var fromMemoryCacheOrRefresh: Bool {
254 | return contains{ $0 <== .fromMemoryCacheOrRefresh }
255 | }
256 |
257 | /// Whether the transition should always happen or not.
258 | public var forceTransition: Bool {
259 | return contains{ $0 <== .forceTransition }
260 | }
261 |
262 | /// Whether cache the image only in memory or not.
263 | public var cacheMemoryOnly: Bool {
264 | return contains{ $0 <== .cacheMemoryOnly }
265 | }
266 |
267 | /// Whether only load the images from cache or not.
268 | public var onlyFromCache: Bool {
269 | return contains{ $0 <== .onlyFromCache }
270 | }
271 |
272 | /// Whether the image should be decoded in background or not.
273 | public var backgroundDecode: Bool {
274 | return contains{ $0 <== .backgroundDecode }
275 | }
276 |
277 | /// Whether the image data should be all loaded at once if it is an animated image.
278 | public var preloadAllAnimationData: Bool {
279 | return contains { $0 <== .preloadAllAnimationData }
280 | }
281 |
282 | /// The queue of callbacks should happen from Kingfisher.
283 | public var callbackDispatchQueue: DispatchQueue {
284 | if let item = lastMatchIgnoringAssociatedValue(.callbackDispatchQueue(nil)),
285 | case .callbackDispatchQueue(let queue) = item
286 | {
287 | return queue ?? DispatchQueue.main
288 | }
289 | return DispatchQueue.main
290 | }
291 |
292 | /// The scale factor which should be used for the image.
293 | public var scaleFactor: CGFloat {
294 | if let item = lastMatchIgnoringAssociatedValue(.scaleFactor(0)),
295 | case .scaleFactor(let scale) = item
296 | {
297 | return scale
298 | }
299 | return 1.0
300 | }
301 |
302 | /// The `ImageDownloadRequestModifier` will be used before sending a download request.
303 | public var modifier: ImageDownloadRequestModifier {
304 | if let item = lastMatchIgnoringAssociatedValue(.requestModifier(NoModifier.default)),
305 | case .requestModifier(let modifier) = item
306 | {
307 | return modifier
308 | }
309 | return NoModifier.default
310 | }
311 |
312 | /// `ImageProcessor` for processing when the downloading finishes.
313 | public var processor: ImageProcessor {
314 | if let item = lastMatchIgnoringAssociatedValue(.processor(DefaultImageProcessor.default)),
315 | case .processor(let processor) = item
316 | {
317 | return processor
318 | }
319 | return DefaultImageProcessor.default
320 | }
321 |
322 | /// `ImageModifier` for modifying right before the image is displayed.
323 | public var imageModifier: ImageModifier {
324 | if let item = lastMatchIgnoringAssociatedValue(.imageModifier(DefaultImageModifier.default)),
325 | case .imageModifier(let imageModifier) = item
326 | {
327 | return imageModifier
328 | }
329 | return DefaultImageModifier.default
330 | }
331 |
332 | /// `CacheSerializer` to convert image to data for storing in cache.
333 | public var cacheSerializer: CacheSerializer {
334 | if let item = lastMatchIgnoringAssociatedValue(.cacheSerializer(DefaultCacheSerializer.default)),
335 | case .cacheSerializer(let cacheSerializer) = item
336 | {
337 | return cacheSerializer
338 | }
339 | return DefaultCacheSerializer.default
340 | }
341 |
342 | /// Keep the existing image while setting another image to an image view.
343 | /// Or the placeholder will be used while downloading.
344 | public var keepCurrentImageWhileLoading: Bool {
345 | return contains { $0 <== .keepCurrentImageWhileLoading }
346 | }
347 |
348 | public var onlyLoadFirstFrame: Bool {
349 | return contains { $0 <== .onlyLoadFirstFrame }
350 | }
351 |
352 | public var cacheOriginalImage: Bool {
353 | return contains { $0 <== .cacheOriginalImage }
354 | }
355 | }
356 |
--------------------------------------------------------------------------------
/Example/Pods/Kingfisher/Sources/Placeholder.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Placeholder.swift
3 | // Kingfisher
4 | //
5 | // Created by Tieme van Veen on 28/08/2017.
6 | //
7 | // Copyright (c) 2018 Wei Wang
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 |
27 | #if os(macOS)
28 | import AppKit
29 | #else
30 | import UIKit
31 | #endif
32 |
33 |
34 | /// Represent a placeholder type which could be set while loading as well as
35 | /// loading finished without getting an image.
36 | public protocol Placeholder {
37 |
38 | /// How the placeholder should be added to a given image view.
39 | func add(to imageView: ImageView)
40 |
41 | /// How the placeholder should be removed from a given image view.
42 | func remove(from imageView: ImageView)
43 | }
44 |
45 | /// Default implementation of an image placeholder. The image will be set or
46 | /// reset directly for `image` property of the image view.
47 | extension Placeholder where Self: Image {
48 |
49 | /// How the placeholder should be added to a given image view.
50 | public func add(to imageView: ImageView) { imageView.image = self }
51 |
52 | /// How the placeholder should be removed from a given image view.
53 | public func remove(from imageView: ImageView) { imageView.image = nil }
54 | }
55 |
56 | extension Image: Placeholder {}
57 |
58 | /// Default implementation of an arbitrary view as placeholder. The view will be
59 | /// added as a subview when adding and be removed from its super view when removing.
60 | ///
61 | /// To use your customize View type as placeholder, simply let it conforming to
62 | /// `Placeholder` by `extension MyView: Placeholder {}`.
63 | extension Placeholder where Self: View {
64 |
65 | /// How the placeholder should be added to a given image view.
66 | public func add(to imageView: ImageView) {
67 | imageView.addSubview(self)
68 |
69 | self.translatesAutoresizingMaskIntoConstraints = false
70 | NSLayoutConstraint.activate([
71 | NSLayoutConstraint(item: self, attribute: .centerX, relatedBy: .equal, toItem: imageView, attribute: .centerX, multiplier: 1, constant: 0),
72 | NSLayoutConstraint(item: self, attribute: .centerY, relatedBy: .equal, toItem: imageView, attribute: .centerY, multiplier: 1, constant: 0),
73 | NSLayoutConstraint(item: self, attribute: .height, relatedBy: .equal, toItem: imageView, attribute: .height, multiplier: 1, constant: 0),
74 | NSLayoutConstraint(item: self, attribute: .width, relatedBy: .equal, toItem: imageView, attribute: .width, multiplier: 1, constant: 0)
75 | ])
76 | }
77 |
78 | /// How the placeholder should be removed from a given image view.
79 | public func remove(from imageView: ImageView) {
80 | self.removeFromSuperview()
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/Example/Pods/Kingfisher/Sources/RequestModifier.swift:
--------------------------------------------------------------------------------
1 | //
2 | // RequestModifier.swift
3 | // Kingfisher
4 | //
5 | // Created by Wei Wang on 2016/09/05.
6 | //
7 | // Copyright (c) 2018 Wei Wang
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 |
27 | import Foundation
28 |
29 | /// Request modifier of image downloader.
30 | public protocol ImageDownloadRequestModifier {
31 | func modified(for request: URLRequest) -> URLRequest?
32 | }
33 |
34 | struct NoModifier: ImageDownloadRequestModifier {
35 | static let `default` = NoModifier()
36 | private init() {}
37 | func modified(for request: URLRequest) -> URLRequest? {
38 | return request
39 | }
40 | }
41 |
42 | public struct AnyModifier: ImageDownloadRequestModifier {
43 |
44 | let block: (URLRequest) -> URLRequest?
45 |
46 | public func modified(for request: URLRequest) -> URLRequest? {
47 | return block(request)
48 | }
49 |
50 | public init(modify: @escaping (URLRequest) -> URLRequest? ) {
51 | block = modify
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/Example/Pods/Kingfisher/Sources/Resource.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Resource.swift
3 | // Kingfisher
4 | //
5 | // Created by Wei Wang on 15/4/6.
6 | //
7 | // Copyright (c) 2018 Wei Wang
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 |
27 | import Foundation
28 |
29 |
30 | /// `Resource` protocol defines how to download and cache a resource from network.
31 | public protocol Resource {
32 | /// The key used in cache.
33 | var cacheKey: String { get }
34 |
35 | /// The target image URL.
36 | var downloadURL: URL { get }
37 | }
38 |
39 | /**
40 | ImageResource is a simple combination of `downloadURL` and `cacheKey`.
41 |
42 | When passed to image view set methods, Kingfisher will try to download the target
43 | image from the `downloadURL`, and then store it with the `cacheKey` as the key in cache.
44 | */
45 | public struct ImageResource: Resource {
46 | /// The key used in cache.
47 | public let cacheKey: String
48 |
49 | /// The target image URL.
50 | public let downloadURL: URL
51 |
52 | /**
53 | Create a resource.
54 |
55 | - parameter downloadURL: The target image URL.
56 | - parameter cacheKey: The cache key. If `nil`, Kingfisher will use the `absoluteString` of `downloadURL` as the key.
57 |
58 | - returns: A resource.
59 | */
60 | public init(downloadURL: URL, cacheKey: String? = nil) {
61 | self.downloadURL = downloadURL
62 | self.cacheKey = cacheKey ?? downloadURL.absoluteString
63 | }
64 | }
65 |
66 | /**
67 | URL conforms to `Resource` in Kingfisher.
68 | The `absoluteString` of this URL is used as `cacheKey`. And the URL itself will be used as `downloadURL`.
69 | If you need customize the url and/or cache key, use `ImageResource` instead.
70 | */
71 | extension URL: Resource {
72 | public var cacheKey: String { return absoluteString }
73 | public var downloadURL: URL { return self }
74 | }
75 |
--------------------------------------------------------------------------------
/Example/Pods/Kingfisher/Sources/String+MD5.swift:
--------------------------------------------------------------------------------
1 | //
2 | // String+MD5.swift
3 | // Kingfisher
4 | //
5 | // To date, adding CommonCrypto to a Swift framework is problematic. See:
6 | // http://stackoverflow.com/questions/25248598/importing-commoncrypto-in-a-swift-framework
7 | // We're using a subset and modified version of CryptoSwift as an alternative.
8 | // The following is an altered source version that only includes MD5. The original software can be found at:
9 | // https://github.com/krzyzanowskim/CryptoSwift
10 | // This is the original copyright notice:
11 |
12 | /*
13 | Copyright (C) 2014 Marcin Krzyżanowski
14 | This software is provided 'as-is', without any express or implied warranty.
15 | In no event will the authors be held liable for any damages arising from the use of this software.
16 | Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
17 | - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required.
18 | - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
19 | - This notice may not be removed or altered from any source or binary distribution.
20 | */
21 |
22 | import Foundation
23 |
24 | public struct StringProxy {
25 | fileprivate let base: String
26 | init(proxy: String) {
27 | base = proxy
28 | }
29 | }
30 |
31 | extension String: KingfisherCompatible {
32 | public typealias CompatibleType = StringProxy
33 | public var kf: CompatibleType {
34 | return StringProxy(proxy: self)
35 | }
36 | }
37 |
38 | extension StringProxy {
39 | var md5: String {
40 | if let data = base.data(using: .utf8, allowLossyConversion: true) {
41 |
42 | let message = data.withUnsafeBytes { bytes -> [UInt8] in
43 | return Array(UnsafeBufferPointer(start: bytes, count: data.count))
44 | }
45 |
46 | let MD5Calculator = MD5(message)
47 | let MD5Data = MD5Calculator.calculate()
48 |
49 | var MD5String = String()
50 | for c in MD5Data {
51 | MD5String += String(format: "%02x", c)
52 | }
53 | return MD5String
54 |
55 | } else {
56 | return base
57 | }
58 | }
59 | }
60 |
61 |
62 | /** array of bytes, little-endian representation */
63 | func arrayOfBytes(_ value: T, length: Int? = nil) -> [UInt8] {
64 | let totalBytes = length ?? (MemoryLayout.size * 8)
65 |
66 | let valuePointer = UnsafeMutablePointer.allocate(capacity: 1)
67 | valuePointer.pointee = value
68 |
69 | let bytes = valuePointer.withMemoryRebound(to: UInt8.self, capacity: totalBytes) { (bytesPointer) -> [UInt8] in
70 | var bytes = [UInt8](repeating: 0, count: totalBytes)
71 | for j in 0...size, totalBytes) {
72 | bytes[totalBytes - 1 - j] = (bytesPointer + j).pointee
73 | }
74 | return bytes
75 | }
76 |
77 | #if swift(>=4.1)
78 | valuePointer.deinitialize(count: 1)
79 | valuePointer.deallocate()
80 | #else
81 | valuePointer.deinitialize()
82 | valuePointer.deallocate(capacity: 1)
83 | #endif
84 |
85 | return bytes
86 | }
87 |
88 | extension Int {
89 | /** Array of bytes with optional padding (little-endian) */
90 | func bytes(_ totalBytes: Int = MemoryLayout.size) -> [UInt8] {
91 | return arrayOfBytes(self, length: totalBytes)
92 | }
93 |
94 | }
95 |
96 | extension NSMutableData {
97 |
98 | /** Convenient way to append bytes */
99 | func appendBytes(_ arrayOfBytes: [UInt8]) {
100 | append(arrayOfBytes, length: arrayOfBytes.count)
101 | }
102 |
103 | }
104 |
105 | protocol HashProtocol {
106 | var message: Array { get }
107 |
108 | /** Common part for hash calculation. Prepare header data. */
109 | func prepare(_ len: Int) -> Array
110 | }
111 |
112 | extension HashProtocol {
113 |
114 | func prepare(_ len: Int) -> Array {
115 | var tmpMessage = message
116 |
117 | // Step 1. Append Padding Bits
118 | tmpMessage.append(0x80) // append one bit (UInt8 with one bit) to message
119 |
120 | // append "0" bit until message length in bits ≡ 448 (mod 512)
121 | var msgLength = tmpMessage.count
122 | var counter = 0
123 |
124 | while msgLength % len != (len - 8) {
125 | counter += 1
126 | msgLength += 1
127 | }
128 |
129 | tmpMessage += Array(repeating: 0, count: counter)
130 | return tmpMessage
131 | }
132 | }
133 |
134 | func toUInt32Array(_ slice: ArraySlice) -> Array {
135 | var result = Array()
136 | result.reserveCapacity(16)
137 |
138 | for idx in stride(from: slice.startIndex, to: slice.endIndex, by: MemoryLayout.size) {
139 | let d0 = UInt32(slice[idx.advanced(by: 3)]) << 24
140 | let d1 = UInt32(slice[idx.advanced(by: 2)]) << 16
141 | let d2 = UInt32(slice[idx.advanced(by: 1)]) << 8
142 | let d3 = UInt32(slice[idx])
143 | let val: UInt32 = d0 | d1 | d2 | d3
144 |
145 | result.append(val)
146 | }
147 | return result
148 | }
149 |
150 | struct BytesIterator: IteratorProtocol {
151 |
152 | let chunkSize: Int
153 | let data: [UInt8]
154 |
155 | init(chunkSize: Int, data: [UInt8]) {
156 | self.chunkSize = chunkSize
157 | self.data = data
158 | }
159 |
160 | var offset = 0
161 |
162 | mutating func next() -> ArraySlice? {
163 | let end = min(chunkSize, data.count - offset)
164 | let result = data[offset.. 0 ? result : nil
167 | }
168 | }
169 |
170 | struct BytesSequence: Sequence {
171 | let chunkSize: Int
172 | let data: [UInt8]
173 |
174 | func makeIterator() -> BytesIterator {
175 | return BytesIterator(chunkSize: chunkSize, data: data)
176 | }
177 | }
178 |
179 | func rotateLeft(_ value: UInt32, bits: UInt32) -> UInt32 {
180 | return ((value << bits) & 0xFFFFFFFF) | (value >> (32 - bits))
181 | }
182 |
183 | class MD5: HashProtocol {
184 |
185 | static let size = 16 // 128 / 8
186 | let message: [UInt8]
187 |
188 | init (_ message: [UInt8]) {
189 | self.message = message
190 | }
191 |
192 | /** specifies the per-round shift amounts */
193 | private let shifts: [UInt32] = [7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22,
194 | 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20,
195 | 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23,
196 | 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21]
197 |
198 | /** binary integer part of the sines of integers (Radians) */
199 | private let sines: [UInt32] = [0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee,
200 | 0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501,
201 | 0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be,
202 | 0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821,
203 | 0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa,
204 | 0xd62f105d, 0x02441453, 0xd8a1e681, 0xe7d3fbc8,
205 | 0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed,
206 | 0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a,
207 | 0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c,
208 | 0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70,
209 | 0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x4881d05,
210 | 0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665,
211 | 0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039,
212 | 0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1,
213 | 0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1,
214 | 0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391]
215 |
216 | private let hashes: [UInt32] = [0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476]
217 |
218 | func calculate() -> [UInt8] {
219 | var tmpMessage = prepare(64)
220 | tmpMessage.reserveCapacity(tmpMessage.count + 4)
221 |
222 | // hash values
223 | var hh = hashes
224 |
225 | // Step 2. Append Length a 64-bit representation of lengthInBits
226 | let lengthInBits = (message.count * 8)
227 | let lengthBytes = lengthInBits.bytes(64 / 8)
228 | tmpMessage += lengthBytes.reversed()
229 |
230 | // Process the message in successive 512-bit chunks:
231 | let chunkSizeBytes = 512 / 8 // 64
232 |
233 | for chunk in BytesSequence(chunkSize: chunkSizeBytes, data: tmpMessage) {
234 | // break chunk into sixteen 32-bit words M[j], 0 ≤ j ≤ 15
235 | var M = toUInt32Array(chunk)
236 | assert(M.count == 16, "Invalid array")
237 |
238 | // Initialize hash value for this chunk:
239 | var A: UInt32 = hh[0]
240 | var B: UInt32 = hh[1]
241 | var C: UInt32 = hh[2]
242 | var D: UInt32 = hh[3]
243 |
244 | var dTemp: UInt32 = 0
245 |
246 | // Main loop
247 | for j in 0 ..< sines.count {
248 | var g = 0
249 | var F: UInt32 = 0
250 |
251 | switch j {
252 | case 0...15:
253 | F = (B & C) | ((~B) & D)
254 | g = j
255 | break
256 | case 16...31:
257 | F = (D & B) | (~D & C)
258 | g = (5 * j + 1) % 16
259 | break
260 | case 32...47:
261 | F = B ^ C ^ D
262 | g = (3 * j + 5) % 16
263 | break
264 | case 48...63:
265 | F = C ^ (B | (~D))
266 | g = (7 * j) % 16
267 | break
268 | default:
269 | break
270 | }
271 | dTemp = D
272 | D = C
273 | C = B
274 | B = B &+ rotateLeft((A &+ F &+ sines[j] &+ M[g]), bits: shifts[j])
275 | A = dTemp
276 | }
277 |
278 | hh[0] = hh[0] &+ A
279 | hh[1] = hh[1] &+ B
280 | hh[2] = hh[2] &+ C
281 | hh[3] = hh[3] &+ D
282 | }
283 |
284 | var result = [UInt8]()
285 | result.reserveCapacity(hh.count / 4)
286 |
287 | hh.forEach {
288 | let itemLE = $0.littleEndian
289 | let r1 = UInt8(itemLE & 0xff)
290 | let r2 = UInt8((itemLE >> 8) & 0xff)
291 | let r3 = UInt8((itemLE >> 16) & 0xff)
292 | let r4 = UInt8((itemLE >> 24) & 0xff)
293 | result += [r1, r2, r3, r4]
294 | }
295 | return result
296 | }
297 | }
298 |
--------------------------------------------------------------------------------
/Example/Pods/Kingfisher/Sources/ThreadHelper.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ThreadHelper.swift
3 | // Kingfisher
4 | //
5 | // Created by Wei Wang on 15/10/9.
6 | //
7 | // Copyright (c) 2018 Wei Wang
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 |
27 | import Foundation
28 |
29 | extension DispatchQueue {
30 | // This method will dispatch the `block` to self.
31 | // If `self` is the main queue, and current thread is main thread, the block
32 | // will be invoked immediately instead of being dispatched.
33 | func safeAsync(_ block: @escaping ()->()) {
34 | if self === DispatchQueue.main && Thread.isMainThread {
35 | block()
36 | } else {
37 | async { block() }
38 | }
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/Example/Pods/Kingfisher/Sources/UIButton+Kingfisher.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UIButton+Kingfisher.swift
3 | // Kingfisher
4 | //
5 | // Created by Wei Wang on 15/4/13.
6 | //
7 | // Copyright (c) 2018 Wei Wang
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 |
27 | import UIKit
28 |
29 | // MARK: - Set Images
30 | /**
31 | * Set image to use in button from web for a specified state.
32 | */
33 | extension Kingfisher where Base: UIButton {
34 | /**
35 | Set an image to use for a specified state with a resource, a placeholder image, options, progress handler and
36 | completion handler.
37 |
38 | - parameter resource: Resource object contains information such as `cacheKey` and `downloadURL`.
39 | - parameter state: The state that uses the specified image.
40 | - parameter placeholder: A placeholder image when retrieving the image at URL.
41 | - parameter options: A dictionary could control some behaviors. See `KingfisherOptionsInfo` for more.
42 | - parameter progressBlock: Called when the image downloading progress gets updated.
43 | - parameter completionHandler: Called when the image retrieved and set.
44 |
45 | - returns: A task represents the retrieving process.
46 |
47 | - note: Both the `progressBlock` and `completionHandler` will be invoked in main thread.
48 | The `CallbackDispatchQueue` specified in `optionsInfo` will not be used in callbacks of this method.
49 |
50 | If `resource` is `nil`, the `placeholder` image will be set and
51 | `completionHandler` will be called with both `error` and `image` being `nil`.
52 | */
53 | @discardableResult
54 | public func setImage(with resource: Resource?,
55 | for state: UIControlState,
56 | placeholder: UIImage? = nil,
57 | options: KingfisherOptionsInfo? = nil,
58 | progressBlock: DownloadProgressBlock? = nil,
59 | completionHandler: CompletionHandler? = nil) -> RetrieveImageTask
60 | {
61 | guard let resource = resource else {
62 | base.setImage(placeholder, for: state)
63 | setWebURL(nil, for: state)
64 | completionHandler?(nil, nil, .none, nil)
65 | return .empty
66 | }
67 |
68 | let options = KingfisherManager.shared.defaultOptions + (options ?? KingfisherEmptyOptionsInfo)
69 | if !options.keepCurrentImageWhileLoading {
70 | base.setImage(placeholder, for: state)
71 | }
72 |
73 | setWebURL(resource.downloadURL, for: state)
74 | let task = KingfisherManager.shared.retrieveImage(
75 | with: resource,
76 | options: options,
77 | progressBlock: { receivedSize, totalSize in
78 | guard resource.downloadURL == self.webURL(for: state) else {
79 | return
80 | }
81 | if let progressBlock = progressBlock {
82 | progressBlock(receivedSize, totalSize)
83 | }
84 | },
85 | completionHandler: {[weak base] image, error, cacheType, imageURL in
86 | DispatchQueue.main.safeAsync {
87 | guard let strongBase = base, imageURL == self.webURL(for: state) else {
88 | completionHandler?(image, error, cacheType, imageURL)
89 | return
90 | }
91 | self.setImageTask(nil)
92 | if image != nil {
93 | strongBase.setImage(image, for: state)
94 | }
95 |
96 | completionHandler?(image, error, cacheType, imageURL)
97 | }
98 | })
99 |
100 | setImageTask(task)
101 | return task
102 | }
103 |
104 | /**
105 | Cancel the image download task bounded to the image view if it is running.
106 | Nothing will happen if the downloading has already finished.
107 | */
108 | public func cancelImageDownloadTask() {
109 | imageTask?.cancel()
110 | }
111 |
112 | /**
113 | Set the background image to use for a specified state with a resource,
114 | a placeholder image, options progress handler and completion handler.
115 |
116 | - parameter resource: Resource object contains information such as `cacheKey` and `downloadURL`.
117 | - parameter state: The state that uses the specified image.
118 | - parameter placeholder: A placeholder image when retrieving the image at URL.
119 | - parameter options: A dictionary could control some behaviors. See `KingfisherOptionsInfo` for more.
120 | - parameter progressBlock: Called when the image downloading progress gets updated.
121 | - parameter completionHandler: Called when the image retrieved and set.
122 |
123 | - returns: A task represents the retrieving process.
124 |
125 | - note: Both the `progressBlock` and `completionHandler` will be invoked in main thread.
126 | The `CallbackDispatchQueue` specified in `optionsInfo` will not be used in callbacks of this method.
127 |
128 | If `resource` is `nil`, the `placeholder` image will be set and
129 | `completionHandler` will be called with both `error` and `image` being `nil`.
130 | */
131 | @discardableResult
132 | public func setBackgroundImage(with resource: Resource?,
133 | for state: UIControlState,
134 | placeholder: UIImage? = nil,
135 | options: KingfisherOptionsInfo? = nil,
136 | progressBlock: DownloadProgressBlock? = nil,
137 | completionHandler: CompletionHandler? = nil) -> RetrieveImageTask
138 | {
139 | guard let resource = resource else {
140 | base.setBackgroundImage(placeholder, for: state)
141 | setBackgroundWebURL(nil, for: state)
142 | completionHandler?(nil, nil, .none, nil)
143 | return .empty
144 | }
145 |
146 | let options = KingfisherManager.shared.defaultOptions + (options ?? KingfisherEmptyOptionsInfo)
147 | if !options.keepCurrentImageWhileLoading {
148 | base.setBackgroundImage(placeholder, for: state)
149 | }
150 |
151 | setBackgroundWebURL(resource.downloadURL, for: state)
152 | let task = KingfisherManager.shared.retrieveImage(
153 | with: resource,
154 | options: options,
155 | progressBlock: { receivedSize, totalSize in
156 | guard resource.downloadURL == self.backgroundWebURL(for: state) else {
157 | return
158 | }
159 | if let progressBlock = progressBlock {
160 | progressBlock(receivedSize, totalSize)
161 | }
162 | },
163 | completionHandler: { [weak base] image, error, cacheType, imageURL in
164 | DispatchQueue.main.safeAsync {
165 | guard let strongBase = base, imageURL == self.backgroundWebURL(for: state) else {
166 | completionHandler?(image, error, cacheType, imageURL)
167 | return
168 | }
169 | self.setBackgroundImageTask(nil)
170 | if image != nil {
171 | strongBase.setBackgroundImage(image, for: state)
172 | }
173 | completionHandler?(image, error, cacheType, imageURL)
174 | }
175 | })
176 |
177 | setBackgroundImageTask(task)
178 | return task
179 | }
180 |
181 | /**
182 | Cancel the background image download task bounded to the image view if it is running.
183 | Nothing will happen if the downloading has already finished.
184 | */
185 | public func cancelBackgroundImageDownloadTask() {
186 | backgroundImageTask?.cancel()
187 | }
188 |
189 | }
190 |
191 | // MARK: - Associated Object
192 | private var lastURLKey: Void?
193 | private var imageTaskKey: Void?
194 |
195 | extension Kingfisher where Base: UIButton {
196 | /**
197 | Get the image URL binded to this button for a specified state.
198 |
199 | - parameter state: The state that uses the specified image.
200 |
201 | - returns: Current URL for image.
202 | */
203 | public func webURL(for state: UIControlState) -> URL? {
204 | return webURLs[NSNumber(value:state.rawValue)] as? URL
205 | }
206 |
207 | fileprivate func setWebURL(_ url: URL?, for state: UIControlState) {
208 | webURLs[NSNumber(value:state.rawValue)] = url
209 | }
210 |
211 | fileprivate var webURLs: NSMutableDictionary {
212 | var dictionary = objc_getAssociatedObject(base, &lastURLKey) as? NSMutableDictionary
213 | if dictionary == nil {
214 | dictionary = NSMutableDictionary()
215 | setWebURLs(dictionary!)
216 | }
217 | return dictionary!
218 | }
219 |
220 | fileprivate func setWebURLs(_ URLs: NSMutableDictionary) {
221 | objc_setAssociatedObject(base, &lastURLKey, URLs, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
222 | }
223 |
224 | fileprivate var imageTask: RetrieveImageTask? {
225 | return objc_getAssociatedObject(base, &imageTaskKey) as? RetrieveImageTask
226 | }
227 |
228 | fileprivate func setImageTask(_ task: RetrieveImageTask?) {
229 | objc_setAssociatedObject(base, &imageTaskKey, task, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
230 | }
231 | }
232 |
233 |
234 | private var lastBackgroundURLKey: Void?
235 | private var backgroundImageTaskKey: Void?
236 |
237 |
238 | extension Kingfisher where Base: UIButton {
239 | /**
240 | Get the background image URL binded to this button for a specified state.
241 |
242 | - parameter state: The state that uses the specified background image.
243 |
244 | - returns: Current URL for background image.
245 | */
246 | public func backgroundWebURL(for state: UIControlState) -> URL? {
247 | return backgroundWebURLs[NSNumber(value:state.rawValue)] as? URL
248 | }
249 |
250 | fileprivate func setBackgroundWebURL(_ url: URL?, for state: UIControlState) {
251 | backgroundWebURLs[NSNumber(value:state.rawValue)] = url
252 | }
253 |
254 | fileprivate var backgroundWebURLs: NSMutableDictionary {
255 | var dictionary = objc_getAssociatedObject(base, &lastBackgroundURLKey) as? NSMutableDictionary
256 | if dictionary == nil {
257 | dictionary = NSMutableDictionary()
258 | setBackgroundWebURLs(dictionary!)
259 | }
260 | return dictionary!
261 | }
262 |
263 | fileprivate func setBackgroundWebURLs(_ URLs: NSMutableDictionary) {
264 | objc_setAssociatedObject(base, &lastBackgroundURLKey, URLs, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
265 | }
266 |
267 | fileprivate var backgroundImageTask: RetrieveImageTask? {
268 | return objc_getAssociatedObject(base, &backgroundImageTaskKey) as? RetrieveImageTask
269 | }
270 |
271 | fileprivate func setBackgroundImageTask(_ task: RetrieveImageTask?) {
272 | objc_setAssociatedObject(base, &backgroundImageTaskKey, task, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
273 | }
274 | }
275 |
--------------------------------------------------------------------------------
/Example/Pods/Local Podspecs/LTAutoScrollView.podspec.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "LTAutoScrollView",
3 | "version": "0.1.0",
4 | "summary": "LTAutoScrollView",
5 | "description": "自定义轮播图,不依赖任何图片加载框架,可实现自定义pageControl,自定义轮播View,自定义滚动样式等等",
6 | "homepage": "https://github.com/gltwy/LTAutoScrollView",
7 | "license": {
8 | "type": "MIT",
9 | "file": "LICENSE"
10 | },
11 | "authors": {
12 | "1282990794@qq.com": "1282990794@qq.com"
13 | },
14 | "source": {
15 | "git": "https://github.com/gltwy/LTAutoScrollView.git",
16 | "tag": "0.1.0"
17 | },
18 | "platforms": {
19 | "ios": "8.0"
20 | },
21 | "source_files": "LTAutoScrollView/Classes/**/*"
22 | }
23 |
--------------------------------------------------------------------------------
/Example/Pods/Manifest.lock:
--------------------------------------------------------------------------------
1 | PODS:
2 | - Kingfisher (4.7.0)
3 | - LTAutoScrollView (0.1.0)
4 |
5 | DEPENDENCIES:
6 | - Kingfisher (~> 4.7.0)
7 | - LTAutoScrollView (from `../`)
8 |
9 | EXTERNAL SOURCES:
10 | LTAutoScrollView:
11 | :path: "../"
12 |
13 | SPEC CHECKSUMS:
14 | Kingfisher: da6b005aa96d37698e3e4f1ccfe96a5b9bbf27d6
15 | LTAutoScrollView: 457e1fb733fd4ba33d36d4dd6e21a07891022c1d
16 |
17 | PODFILE CHECKSUM: 084a963cfe4e84dec4a03d4df38492d4e14e1424
18 |
19 | COCOAPODS: 1.2.0
20 |
--------------------------------------------------------------------------------
/Example/Pods/Target Support Files/Kingfisher/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | ${EXECUTABLE_NAME}
9 | CFBundleIdentifier
10 | ${PRODUCT_BUNDLE_IDENTIFIER}
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | ${PRODUCT_NAME}
15 | CFBundlePackageType
16 | FMWK
17 | CFBundleShortVersionString
18 | 4.7.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | ${CURRENT_PROJECT_VERSION}
23 | NSPrincipalClass
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/Example/Pods/Target Support Files/Kingfisher/Kingfisher-dummy.m:
--------------------------------------------------------------------------------
1 | #import
2 | @interface PodsDummy_Kingfisher : NSObject
3 | @end
4 | @implementation PodsDummy_Kingfisher
5 | @end
6 |
--------------------------------------------------------------------------------
/Example/Pods/Target Support Files/Kingfisher/Kingfisher-prefix.pch:
--------------------------------------------------------------------------------
1 | #ifdef __OBJC__
2 | #import
3 | #else
4 | #ifndef FOUNDATION_EXPORT
5 | #if defined(__cplusplus)
6 | #define FOUNDATION_EXPORT extern "C"
7 | #else
8 | #define FOUNDATION_EXPORT extern
9 | #endif
10 | #endif
11 | #endif
12 |
13 |
--------------------------------------------------------------------------------
/Example/Pods/Target Support Files/Kingfisher/Kingfisher-umbrella.h:
--------------------------------------------------------------------------------
1 | #ifdef __OBJC__
2 | #import
3 | #else
4 | #ifndef FOUNDATION_EXPORT
5 | #if defined(__cplusplus)
6 | #define FOUNDATION_EXPORT extern "C"
7 | #else
8 | #define FOUNDATION_EXPORT extern
9 | #endif
10 | #endif
11 | #endif
12 |
13 | #import "Kingfisher.h"
14 |
15 | FOUNDATION_EXPORT double KingfisherVersionNumber;
16 | FOUNDATION_EXPORT const unsigned char KingfisherVersionString[];
17 |
18 |
--------------------------------------------------------------------------------
/Example/Pods/Target Support Files/Kingfisher/Kingfisher.modulemap:
--------------------------------------------------------------------------------
1 | framework module Kingfisher {
2 | umbrella header "Kingfisher-umbrella.h"
3 |
4 | export *
5 | module * { export * }
6 | }
7 |
--------------------------------------------------------------------------------
/Example/Pods/Target Support Files/Kingfisher/Kingfisher.xcconfig:
--------------------------------------------------------------------------------
1 | CONFIGURATION_BUILD_DIR = $PODS_CONFIGURATION_BUILD_DIR/Kingfisher
2 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
3 | HEADER_SEARCH_PATHS = "${PODS_ROOT}/Headers/Private" "${PODS_ROOT}/Headers/Public"
4 | OTHER_LDFLAGS = -framework "CFNetwork"
5 | OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS"
6 | PODS_BUILD_DIR = $BUILD_DIR
7 | PODS_CONFIGURATION_BUILD_DIR = $PODS_BUILD_DIR/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
8 | PODS_ROOT = ${SRCROOT}
9 | PODS_TARGET_SRCROOT = ${PODS_ROOT}/Kingfisher
10 | PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier}
11 | SKIP_INSTALL = YES
12 |
--------------------------------------------------------------------------------
/Example/Pods/Target Support Files/LTAutoScrollView/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | ${EXECUTABLE_NAME}
9 | CFBundleIdentifier
10 | ${PRODUCT_BUNDLE_IDENTIFIER}
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | ${PRODUCT_NAME}
15 | CFBundlePackageType
16 | FMWK
17 | CFBundleShortVersionString
18 | 0.1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | ${CURRENT_PROJECT_VERSION}
23 | NSPrincipalClass
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/Example/Pods/Target Support Files/LTAutoScrollView/LTAutoScrollView-dummy.m:
--------------------------------------------------------------------------------
1 | #import
2 | @interface PodsDummy_LTAutoScrollView : NSObject
3 | @end
4 | @implementation PodsDummy_LTAutoScrollView
5 | @end
6 |
--------------------------------------------------------------------------------
/Example/Pods/Target Support Files/LTAutoScrollView/LTAutoScrollView-prefix.pch:
--------------------------------------------------------------------------------
1 | #ifdef __OBJC__
2 | #import
3 | #else
4 | #ifndef FOUNDATION_EXPORT
5 | #if defined(__cplusplus)
6 | #define FOUNDATION_EXPORT extern "C"
7 | #else
8 | #define FOUNDATION_EXPORT extern
9 | #endif
10 | #endif
11 | #endif
12 |
13 |
--------------------------------------------------------------------------------
/Example/Pods/Target Support Files/LTAutoScrollView/LTAutoScrollView-umbrella.h:
--------------------------------------------------------------------------------
1 | #ifdef __OBJC__
2 | #import
3 | #else
4 | #ifndef FOUNDATION_EXPORT
5 | #if defined(__cplusplus)
6 | #define FOUNDATION_EXPORT extern "C"
7 | #else
8 | #define FOUNDATION_EXPORT extern
9 | #endif
10 | #endif
11 | #endif
12 |
13 |
14 | FOUNDATION_EXPORT double LTAutoScrollViewVersionNumber;
15 | FOUNDATION_EXPORT const unsigned char LTAutoScrollViewVersionString[];
16 |
17 |
--------------------------------------------------------------------------------
/Example/Pods/Target Support Files/LTAutoScrollView/LTAutoScrollView.modulemap:
--------------------------------------------------------------------------------
1 | framework module LTAutoScrollView {
2 | umbrella header "LTAutoScrollView-umbrella.h"
3 |
4 | export *
5 | module * { export * }
6 | }
7 |
--------------------------------------------------------------------------------
/Example/Pods/Target Support Files/LTAutoScrollView/LTAutoScrollView.xcconfig:
--------------------------------------------------------------------------------
1 | CONFIGURATION_BUILD_DIR = $PODS_CONFIGURATION_BUILD_DIR/LTAutoScrollView
2 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
3 | HEADER_SEARCH_PATHS = "${PODS_ROOT}/Headers/Private" "${PODS_ROOT}/Headers/Public"
4 | OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS"
5 | PODS_BUILD_DIR = $BUILD_DIR
6 | PODS_CONFIGURATION_BUILD_DIR = $PODS_BUILD_DIR/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
7 | PODS_ROOT = ${SRCROOT}
8 | PODS_TARGET_SRCROOT = ${PODS_ROOT}/../..
9 | PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier}
10 | SKIP_INSTALL = YES
11 |
--------------------------------------------------------------------------------
/Example/Pods/Target Support Files/Pods-LTAutoScrollView_Example/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | ${EXECUTABLE_NAME}
9 | CFBundleIdentifier
10 | ${PRODUCT_BUNDLE_IDENTIFIER}
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | ${PRODUCT_NAME}
15 | CFBundlePackageType
16 | FMWK
17 | CFBundleShortVersionString
18 | 1.0.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | ${CURRENT_PROJECT_VERSION}
23 | NSPrincipalClass
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/Example/Pods/Target Support Files/Pods-LTAutoScrollView_Example/Pods-LTAutoScrollView_Example-acknowledgements.markdown:
--------------------------------------------------------------------------------
1 | # Acknowledgements
2 | This application makes use of the following third party libraries:
3 |
4 | ## Kingfisher
5 |
6 | The MIT License (MIT)
7 |
8 | Copyright (c) 2018 Wei Wang
9 |
10 | Permission is hereby granted, free of charge, to any person obtaining a copy
11 | of this software and associated documentation files (the "Software"), to deal
12 | in the Software without restriction, including without limitation the rights
13 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14 | copies of the Software, and to permit persons to whom the Software is
15 | furnished to do so, subject to the following conditions:
16 |
17 | The above copyright notice and this permission notice shall be included in all
18 | copies or substantial portions of the Software.
19 |
20 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26 | SOFTWARE.
27 |
28 |
29 |
30 | ## LTAutoScrollView
31 |
32 | Copyright (c) 2018 1282990794@qq.com <1282990794@qq.com>
33 |
34 | Permission is hereby granted, free of charge, to any person obtaining a copy
35 | of this software and associated documentation files (the "Software"), to deal
36 | in the Software without restriction, including without limitation the rights
37 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
38 | copies of the Software, and to permit persons to whom the Software is
39 | furnished to do so, subject to the following conditions:
40 |
41 | The above copyright notice and this permission notice shall be included in
42 | all copies or substantial portions of the Software.
43 |
44 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
45 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
46 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
47 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
48 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
49 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
50 | THE SOFTWARE.
51 |
52 | Generated by CocoaPods - https://cocoapods.org
53 |
--------------------------------------------------------------------------------
/Example/Pods/Target Support Files/Pods-LTAutoScrollView_Example/Pods-LTAutoScrollView_Example-acknowledgements.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | PreferenceSpecifiers
6 |
7 |
8 | FooterText
9 | This application makes use of the following third party libraries:
10 | Title
11 | Acknowledgements
12 | Type
13 | PSGroupSpecifier
14 |
15 |
16 | FooterText
17 | The MIT License (MIT)
18 |
19 | Copyright (c) 2018 Wei Wang
20 |
21 | Permission is hereby granted, free of charge, to any person obtaining a copy
22 | of this software and associated documentation files (the "Software"), to deal
23 | in the Software without restriction, including without limitation the rights
24 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
25 | copies of the Software, and to permit persons to whom the Software is
26 | furnished to do so, subject to the following conditions:
27 |
28 | The above copyright notice and this permission notice shall be included in all
29 | copies or substantial portions of the Software.
30 |
31 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
32 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
33 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
34 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
35 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
36 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
37 | SOFTWARE.
38 |
39 |
40 | License
41 | MIT
42 | Title
43 | Kingfisher
44 | Type
45 | PSGroupSpecifier
46 |
47 |
48 | FooterText
49 | Copyright (c) 2018 1282990794@qq.com <1282990794@qq.com>
50 |
51 | Permission is hereby granted, free of charge, to any person obtaining a copy
52 | of this software and associated documentation files (the "Software"), to deal
53 | in the Software without restriction, including without limitation the rights
54 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
55 | copies of the Software, and to permit persons to whom the Software is
56 | furnished to do so, subject to the following conditions:
57 |
58 | The above copyright notice and this permission notice shall be included in
59 | all copies or substantial portions of the Software.
60 |
61 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
62 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
63 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
64 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
65 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
66 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
67 | THE SOFTWARE.
68 |
69 | License
70 | MIT
71 | Title
72 | LTAutoScrollView
73 | Type
74 | PSGroupSpecifier
75 |
76 |
77 | FooterText
78 | Generated by CocoaPods - https://cocoapods.org
79 | Title
80 |
81 | Type
82 | PSGroupSpecifier
83 |
84 |
85 | StringsTable
86 | Acknowledgements
87 | Title
88 | Acknowledgements
89 |
90 |
91 |
--------------------------------------------------------------------------------
/Example/Pods/Target Support Files/Pods-LTAutoScrollView_Example/Pods-LTAutoScrollView_Example-dummy.m:
--------------------------------------------------------------------------------
1 | #import
2 | @interface PodsDummy_Pods_LTAutoScrollView_Example : NSObject
3 | @end
4 | @implementation PodsDummy_Pods_LTAutoScrollView_Example
5 | @end
6 |
--------------------------------------------------------------------------------
/Example/Pods/Target Support Files/Pods-LTAutoScrollView_Example/Pods-LTAutoScrollView_Example-frameworks.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | set -e
3 |
4 | echo "mkdir -p ${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"
5 | mkdir -p "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"
6 |
7 | SWIFT_STDLIB_PATH="${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}"
8 |
9 | install_framework()
10 | {
11 | if [ -r "${BUILT_PRODUCTS_DIR}/$1" ]; then
12 | local source="${BUILT_PRODUCTS_DIR}/$1"
13 | elif [ -r "${BUILT_PRODUCTS_DIR}/$(basename "$1")" ]; then
14 | local source="${BUILT_PRODUCTS_DIR}/$(basename "$1")"
15 | elif [ -r "$1" ]; then
16 | local source="$1"
17 | fi
18 |
19 | local destination="${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"
20 |
21 | if [ -L "${source}" ]; then
22 | echo "Symlinked..."
23 | source="$(readlink "${source}")"
24 | fi
25 |
26 | # use filter instead of exclude so missing patterns dont' throw errors
27 | echo "rsync -av --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${destination}\""
28 | rsync -av --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${destination}"
29 |
30 | local basename
31 | basename="$(basename -s .framework "$1")"
32 | binary="${destination}/${basename}.framework/${basename}"
33 | if ! [ -r "$binary" ]; then
34 | binary="${destination}/${basename}"
35 | fi
36 |
37 | # Strip invalid architectures so "fat" simulator / device frameworks work on device
38 | if [[ "$(file "$binary")" == *"dynamically linked shared library"* ]]; then
39 | strip_invalid_archs "$binary"
40 | fi
41 |
42 | # Resign the code if required by the build settings to avoid unstable apps
43 | code_sign_if_enabled "${destination}/$(basename "$1")"
44 |
45 | # Embed linked Swift runtime libraries. No longer necessary as of Xcode 7.
46 | if [ "${XCODE_VERSION_MAJOR}" -lt 7 ]; then
47 | local swift_runtime_libs
48 | swift_runtime_libs=$(xcrun otool -LX "$binary" | grep --color=never @rpath/libswift | sed -E s/@rpath\\/\(.+dylib\).*/\\1/g | uniq -u && exit ${PIPESTATUS[0]})
49 | for lib in $swift_runtime_libs; do
50 | echo "rsync -auv \"${SWIFT_STDLIB_PATH}/${lib}\" \"${destination}\""
51 | rsync -auv "${SWIFT_STDLIB_PATH}/${lib}" "${destination}"
52 | code_sign_if_enabled "${destination}/${lib}"
53 | done
54 | fi
55 | }
56 |
57 | # Signs a framework with the provided identity
58 | code_sign_if_enabled() {
59 | if [ -n "${EXPANDED_CODE_SIGN_IDENTITY}" -a "${CODE_SIGNING_REQUIRED}" != "NO" -a "${CODE_SIGNING_ALLOWED}" != "NO" ]; then
60 | # Use the current code_sign_identitiy
61 | echo "Code Signing $1 with Identity ${EXPANDED_CODE_SIGN_IDENTITY_NAME}"
62 | local code_sign_cmd="/usr/bin/codesign --force --sign ${EXPANDED_CODE_SIGN_IDENTITY} ${OTHER_CODE_SIGN_FLAGS} --preserve-metadata=identifier,entitlements '$1'"
63 |
64 | if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then
65 | code_sign_cmd="$code_sign_cmd &"
66 | fi
67 | echo "$code_sign_cmd"
68 | eval "$code_sign_cmd"
69 | fi
70 | }
71 |
72 | # Strip invalid architectures
73 | strip_invalid_archs() {
74 | binary="$1"
75 | # Get architectures for current file
76 | archs="$(lipo -info "$binary" | rev | cut -d ':' -f1 | rev)"
77 | stripped=""
78 | for arch in $archs; do
79 | if ! [[ "${VALID_ARCHS}" == *"$arch"* ]]; then
80 | # Strip non-valid architectures in-place
81 | lipo -remove "$arch" -output "$binary" "$binary" || exit 1
82 | stripped="$stripped $arch"
83 | fi
84 | done
85 | if [[ "$stripped" ]]; then
86 | echo "Stripped $binary of architectures:$stripped"
87 | fi
88 | }
89 |
90 |
91 | if [[ "$CONFIGURATION" == "Debug" ]]; then
92 | install_framework "$BUILT_PRODUCTS_DIR/Kingfisher/Kingfisher.framework"
93 | install_framework "$BUILT_PRODUCTS_DIR/LTAutoScrollView/LTAutoScrollView.framework"
94 | fi
95 | if [[ "$CONFIGURATION" == "Release" ]]; then
96 | install_framework "$BUILT_PRODUCTS_DIR/Kingfisher/Kingfisher.framework"
97 | install_framework "$BUILT_PRODUCTS_DIR/LTAutoScrollView/LTAutoScrollView.framework"
98 | fi
99 | if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then
100 | wait
101 | fi
102 |
--------------------------------------------------------------------------------
/Example/Pods/Target Support Files/Pods-LTAutoScrollView_Example/Pods-LTAutoScrollView_Example-resources.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | set -e
3 |
4 | mkdir -p "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}"
5 |
6 | RESOURCES_TO_COPY=${PODS_ROOT}/resources-to-copy-${TARGETNAME}.txt
7 | > "$RESOURCES_TO_COPY"
8 |
9 | XCASSET_FILES=()
10 |
11 | case "${TARGETED_DEVICE_FAMILY}" in
12 | 1,2)
13 | TARGET_DEVICE_ARGS="--target-device ipad --target-device iphone"
14 | ;;
15 | 1)
16 | TARGET_DEVICE_ARGS="--target-device iphone"
17 | ;;
18 | 2)
19 | TARGET_DEVICE_ARGS="--target-device ipad"
20 | ;;
21 | 3)
22 | TARGET_DEVICE_ARGS="--target-device tv"
23 | ;;
24 | *)
25 | TARGET_DEVICE_ARGS="--target-device mac"
26 | ;;
27 | esac
28 |
29 | install_resource()
30 | {
31 | if [[ "$1" = /* ]] ; then
32 | RESOURCE_PATH="$1"
33 | else
34 | RESOURCE_PATH="${PODS_ROOT}/$1"
35 | fi
36 | if [[ ! -e "$RESOURCE_PATH" ]] ; then
37 | cat << EOM
38 | error: Resource "$RESOURCE_PATH" not found. Run 'pod install' to update the copy resources script.
39 | EOM
40 | exit 1
41 | fi
42 | case $RESOURCE_PATH in
43 | *.storyboard)
44 | echo "ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile ${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .storyboard`.storyboardc $RESOURCE_PATH --sdk ${SDKROOT} ${TARGET_DEVICE_ARGS}"
45 | ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .storyboard`.storyboardc" "$RESOURCE_PATH" --sdk "${SDKROOT}" ${TARGET_DEVICE_ARGS}
46 | ;;
47 | *.xib)
48 | echo "ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile ${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .xib`.nib $RESOURCE_PATH --sdk ${SDKROOT} ${TARGET_DEVICE_ARGS}"
49 | ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .xib`.nib" "$RESOURCE_PATH" --sdk "${SDKROOT}" ${TARGET_DEVICE_ARGS}
50 | ;;
51 | *.framework)
52 | echo "mkdir -p ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"
53 | mkdir -p "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"
54 | echo "rsync -av $RESOURCE_PATH ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"
55 | rsync -av "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"
56 | ;;
57 | *.xcdatamodel)
58 | echo "xcrun momc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH"`.mom\""
59 | xcrun momc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodel`.mom"
60 | ;;
61 | *.xcdatamodeld)
62 | echo "xcrun momc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodeld`.momd\""
63 | xcrun momc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodeld`.momd"
64 | ;;
65 | *.xcmappingmodel)
66 | echo "xcrun mapc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcmappingmodel`.cdm\""
67 | xcrun mapc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcmappingmodel`.cdm"
68 | ;;
69 | *.xcassets)
70 | ABSOLUTE_XCASSET_FILE="$RESOURCE_PATH"
71 | XCASSET_FILES+=("$ABSOLUTE_XCASSET_FILE")
72 | ;;
73 | *)
74 | echo "$RESOURCE_PATH"
75 | echo "$RESOURCE_PATH" >> "$RESOURCES_TO_COPY"
76 | ;;
77 | esac
78 | }
79 |
80 | mkdir -p "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}"
81 | rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}"
82 | if [[ "${ACTION}" == "install" ]] && [[ "${SKIP_INSTALL}" == "NO" ]]; then
83 | mkdir -p "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}"
84 | rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}"
85 | fi
86 | rm -f "$RESOURCES_TO_COPY"
87 |
88 | if [[ -n "${WRAPPER_EXTENSION}" ]] && [ "`xcrun --find actool`" ] && [ -n "$XCASSET_FILES" ]
89 | then
90 | # Find all other xcassets (this unfortunately includes those of path pods and other targets).
91 | OTHER_XCASSETS=$(find "$PWD" -iname "*.xcassets" -type d)
92 | while read line; do
93 | if [[ $line != "${PODS_ROOT}*" ]]; then
94 | XCASSET_FILES+=("$line")
95 | fi
96 | done <<<"$OTHER_XCASSETS"
97 |
98 | printf "%s\0" "${XCASSET_FILES[@]}" | xargs -0 xcrun actool --output-format human-readable-text --notices --warnings --platform "${PLATFORM_NAME}" --minimum-deployment-target "${!DEPLOYMENT_TARGET_SETTING_NAME}" ${TARGET_DEVICE_ARGS} --compress-pngs --compile "${BUILT_PRODUCTS_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}"
99 | fi
100 |
--------------------------------------------------------------------------------
/Example/Pods/Target Support Files/Pods-LTAutoScrollView_Example/Pods-LTAutoScrollView_Example-umbrella.h:
--------------------------------------------------------------------------------
1 | #ifdef __OBJC__
2 | #import
3 | #else
4 | #ifndef FOUNDATION_EXPORT
5 | #if defined(__cplusplus)
6 | #define FOUNDATION_EXPORT extern "C"
7 | #else
8 | #define FOUNDATION_EXPORT extern
9 | #endif
10 | #endif
11 | #endif
12 |
13 |
14 | FOUNDATION_EXPORT double Pods_LTAutoScrollView_ExampleVersionNumber;
15 | FOUNDATION_EXPORT const unsigned char Pods_LTAutoScrollView_ExampleVersionString[];
16 |
17 |
--------------------------------------------------------------------------------
/Example/Pods/Target Support Files/Pods-LTAutoScrollView_Example/Pods-LTAutoScrollView_Example.debug.xcconfig:
--------------------------------------------------------------------------------
1 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES
2 | FRAMEWORK_SEARCH_PATHS = $(inherited) "$PODS_CONFIGURATION_BUILD_DIR/Kingfisher" "$PODS_CONFIGURATION_BUILD_DIR/LTAutoScrollView"
3 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
4 | LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks'
5 | OTHER_CFLAGS = $(inherited) -iquote "$PODS_CONFIGURATION_BUILD_DIR/Kingfisher/Kingfisher.framework/Headers" -iquote "$PODS_CONFIGURATION_BUILD_DIR/LTAutoScrollView/LTAutoScrollView.framework/Headers"
6 | OTHER_LDFLAGS = $(inherited) -framework "Kingfisher" -framework "LTAutoScrollView"
7 | OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS"
8 | PODS_BUILD_DIR = $BUILD_DIR
9 | PODS_CONFIGURATION_BUILD_DIR = $PODS_BUILD_DIR/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
10 | PODS_ROOT = ${SRCROOT}/Pods
11 |
--------------------------------------------------------------------------------
/Example/Pods/Target Support Files/Pods-LTAutoScrollView_Example/Pods-LTAutoScrollView_Example.modulemap:
--------------------------------------------------------------------------------
1 | framework module Pods_LTAutoScrollView_Example {
2 | umbrella header "Pods-LTAutoScrollView_Example-umbrella.h"
3 |
4 | export *
5 | module * { export * }
6 | }
7 |
--------------------------------------------------------------------------------
/Example/Pods/Target Support Files/Pods-LTAutoScrollView_Example/Pods-LTAutoScrollView_Example.release.xcconfig:
--------------------------------------------------------------------------------
1 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES
2 | FRAMEWORK_SEARCH_PATHS = $(inherited) "$PODS_CONFIGURATION_BUILD_DIR/Kingfisher" "$PODS_CONFIGURATION_BUILD_DIR/LTAutoScrollView"
3 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
4 | LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks'
5 | OTHER_CFLAGS = $(inherited) -iquote "$PODS_CONFIGURATION_BUILD_DIR/Kingfisher/Kingfisher.framework/Headers" -iquote "$PODS_CONFIGURATION_BUILD_DIR/LTAutoScrollView/LTAutoScrollView.framework/Headers"
6 | OTHER_LDFLAGS = $(inherited) -framework "Kingfisher" -framework "LTAutoScrollView"
7 | OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS"
8 | PODS_BUILD_DIR = $BUILD_DIR
9 | PODS_CONFIGURATION_BUILD_DIR = $PODS_BUILD_DIR/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
10 | PODS_ROOT = ${SRCROOT}/Pods
11 |
--------------------------------------------------------------------------------
/Example/Tests/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | BNDL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1
23 |
24 |
25 |
--------------------------------------------------------------------------------
/Example/Tests/Tests.swift:
--------------------------------------------------------------------------------
1 | import XCTest
2 |
3 | class Tests: XCTestCase {
4 |
5 | override func setUp() {
6 | super.setUp()
7 | // Put setup code here. This method is called before the invocation of each test method in the class.
8 | }
9 |
10 | override func tearDown() {
11 | // Put teardown code here. This method is called after the invocation of each test method in the class.
12 | super.tearDown()
13 | }
14 |
15 | func testExample() {
16 | // This is an example of a functional test case.
17 | XCTAssert(true, "Pass")
18 | }
19 |
20 | func testPerformanceExample() {
21 | // This is an example of a performance test case.
22 | self.measure() {
23 | // Put the code you want to measure the time of here.
24 | }
25 | }
26 |
27 | }
28 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2018 1282990794@qq.com <1282990794@qq.com>
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy
4 | of this software and associated documentation files (the "Software"), to deal
5 | in the Software without restriction, including without limitation the rights
6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | copies of the Software, and to permit persons to whom the Software is
8 | furnished to do so, subject to the following conditions:
9 |
10 | The above copyright notice and this permission notice shall be included in
11 | all copies or substantial portions of the Software.
12 |
13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | THE SOFTWARE.
20 |
--------------------------------------------------------------------------------
/LTAutoScrollView.podspec:
--------------------------------------------------------------------------------
1 | #
2 | # Be sure to run `pod lib lint LTAutoScrollView.podspec' to ensure this is a
3 | # valid spec before submitting.
4 | #
5 | # Any lines starting with a # are optional, but their use is encouraged
6 | # To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html
7 | #
8 |
9 | Pod::Spec.new do |s|
10 | s.name = 'LTAutoScrollView'
11 | s.version = '0.2.0'
12 | s.summary = 'LTAutoScrollView'
13 |
14 | # This description is used to generate tags and improve search results.
15 | # * Think: What does it do? Why did you write it? What is the focus?
16 | # * Try to keep it short, snappy and to the point.
17 | # * Write the description between the DESC delimiters below.
18 | # * Finally, don't worry about the indent, CocoaPods strips it!
19 |
20 | s.description = <<-DESC
21 | 自定义轮播图,不依赖任何图片加载框架,可实现自定义pageControl,自定义轮播View,自定义滚动样式等等
22 | DESC
23 |
24 | s.homepage = 'https://github.com/gltwy/LTAutoScrollView'
25 | # s.screenshots = 'www.example.com/screenshots_1', 'www.example.com/screenshots_2'
26 | s.license = { :type => 'MIT', :file => 'LICENSE' }
27 | s.author = { '1282990794@qq.com' => '1282990794@qq.com' }
28 | s.source = { :git => 'https://github.com/gltwy/LTAutoScrollView.git', :tag => s.version.to_s }
29 | # s.social_media_url = 'https://twitter.com/'
30 |
31 | s.ios.deployment_target = '8.0'
32 |
33 | s.source_files = 'LTAutoScrollView/Classes/**/*'
34 |
35 | # s.resource_bundles = {
36 | # 'LTAutoScrollView' => ['LTAutoScrollView/Assets/*.png']
37 | # }
38 |
39 | # s.public_header_files = 'Pod/Classes/**/*.h'
40 | # s.frameworks = 'UIKit', 'MapKit'
41 | # s.dependency 'AFNetworking', '~> 2.3'
42 | end
43 |
--------------------------------------------------------------------------------
/LTAutoScrollView/Assets/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/glthello/LTAutoScrollView/5bdf6b3b3c3c99df5313326406d1a02e51cbdd4d/LTAutoScrollView/Assets/.gitkeep
--------------------------------------------------------------------------------
/LTAutoScrollView/Classes/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/glthello/LTAutoScrollView/5bdf6b3b3c3c99df5313326406d1a02e51cbdd4d/LTAutoScrollView/Classes/.gitkeep
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # LTAutoScrollView
2 |
3 | [](https://travis-ci.org/1282990794@qq.com/LTAutoScrollView)
4 | [](http://cocoapods.org/pods/LTAutoScrollView)
5 | [](http://cocoapods.org/pods/LTAutoScrollView)
6 | [](http://cocoapods.org/pods/LTAutoScrollView)
7 |
8 | 
9 |
10 | ## Example
11 |
12 | To run the example project, clone the repo, and run `pod install` from the Example directory first.
13 |
14 | ## Installation with CocoaPods
15 |
16 | [CocoaPods](http://cocoapods.org) is a dependency manager for Swift, which automates and simplifies the process of using 3rd-party libraries like LTAutoScrollView in your projects. You can install it with the following command:
17 |
18 | ```bash
19 | $ gem install cocoapods
20 | ```
21 |
22 | #### Podfile
23 |
24 | To integrate LTAutoScrollView into your Xcode project using CocoaPods, specify it in your `Podfile`:
25 |
26 | ```ruby
27 | source 'https://github.com/CocoaPods/Specs.git'
28 | platform :ios, '8.0'
29 |
30 | target 'TargetName' do
31 | pod 'LTAutoScrollView'
32 | end
33 | ```
34 |
35 | Then, run the following command:
36 |
37 | ```bash
38 | $ pod install
39 | ```
40 |
41 | ## Usage
42 |
43 | #### 创建LTAutoScrollView
44 |
45 | ```swift
46 | let autoScrollView = LTAutoScrollView(frame: CGRect(x: 0, y: 0, width: view.bounds.width, height: 150))
47 |
48 | //设置滚动时间间隔 默认2.0s
49 | autoScrollView.glt_timeInterval = 1.5
50 |
51 | //设置轮播图的方向 默认水平
52 | autoScrollView.scrollDirection = .vertical
53 |
54 | //加载网络图片传入图片url数组, 加载本地图片传入图片名称数组
55 | autoScrollView.images = images
56 |
57 | //加载图片,内部不依赖任何图片加载框架
58 | autoScrollView.imageHandle = {(imageView, imageName) in
59 | //加载本地图片(根据传入的images数组来决定加载方式)
60 | imageView.image = UIImage(named: imageName)
61 | //加载网络图片(根据传入的images数组来决定加载方式)
62 | //imageView.kf.setImage(with: URL(string: imageName))
63 | }
64 |
65 | // 滚动手势禁用(文字轮播较实用) 默认为false
66 | autoScrollView.isDisableScrollGesture = false
67 |
68 | //设置pageControl View的高度 默认为20
69 | autoScrollView.gltPageControlHeight = 20;
70 |
71 | // 是否自动轮播 默认true
72 | autoScrollView.isAutoScroll = true
73 |
74 | //dot在轮播图的位置 中心 左侧 右侧 默认居中
75 | autoScrollView.dotDirection = .default
76 |
77 | //点击事件
78 | autoScrollView.didSelectItemHandle = {
79 | print("autoScrollView1 点击了第 \($0) 个索引")
80 | }
81 |
82 | //自动滚动到当前索引事件
83 | autoScrollView.autoDidSelectItemHandle = { index in
84 | print("autoScrollView1 自动滚动到了第 \(index) 个索引")
85 | }
86 |
87 | //PageControl点击事件
88 | autoScrollView.pageControlDidSelectIndexHandle = { index in
89 | print("autoScrollView1 pageControl点击了第 \(index) 个索引")
90 | }
91 |
92 | //设置pageControl的位置
93 | autoScrollView.dotDirection = .right
94 | //dot在轮播图的位置 左侧 或 右侧时,距离最屏幕最左边或最最右边的距离,默认0
95 | autoScrollView.adjustValue = 15.0
96 | //pageControl高度调整从而改变pageControl位置 默认20
97 | autoScrollView.gltPageControlHeight = 25
98 |
99 | //设置LTDotLayout,更多dot使用见LTDotLayout属性说明
100 | let layout = LTDotLayout(dotImage: dotImage, dotSelectImage: dotSelectImage)
101 | layout.dotMargin = 10.0
102 | autoScrollView.dotLayout = layout
103 | ```
104 |
105 | #### LTDotLayout属性说明
106 |
107 | ```swift
108 | /* dot单独的一个的宽度 */
109 | public var dotWidth: CGFloat = isPostDotSize
110 | /* dot单独的一个的高度 */
111 | public var dotHeight: CGFloat = isPostDotSize
112 | /* dot之间的间距 */
113 | public var dotMargin: CGFloat = 15.0
114 | /* dot未选中的图片 */
115 | public var dotImage: UIImage?
116 | /* dot选中后的图片 */
117 | public var dotSelectImage: UIImage?
118 | /* dot未选中的颜色 */
119 | public var dotColor: UIColor = UIColor.clear
120 | /* dot选中的后颜色 */
121 | public var dotSelectColor: UIColor = UIColor.clear
122 | /* custom为默认是自定义 , 想使用类似系统样式传入default */
123 | public var dotType: LTAutoScrollViewType = .custom
124 | /* 滚动过程是否放大当前dot */
125 | public var isScale: Bool = true
126 | /* 滚动过程dot放大倍率 */
127 | public var scaleXY: CGFloat = 1.4
128 | ```
129 |
130 | ## Author
131 |
132 | 1282990794@qq.com
133 |
134 | ## License
135 |
136 | LTAutoScrollView is available under the MIT license. See the LICENSE file for more info.
137 |
--------------------------------------------------------------------------------
/_Pods.xcodeproj:
--------------------------------------------------------------------------------
1 | Example/Pods/Pods.xcodeproj
--------------------------------------------------------------------------------
/gltauto.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/glthello/LTAutoScrollView/5bdf6b3b3c3c99df5313326406d1a02e51cbdd4d/gltauto.gif
--------------------------------------------------------------------------------