├── .gitignore ├── .travis.yml ├── Core ├── ActivityIndicator.swift ├── FullScreenSlideshowViewController.swift ├── ImageSlideshowItem.swift ├── InputSource.swift ├── KFImageViewer.swift ├── PageIndicator.swift ├── PageIndicatorPosition.swift ├── Resources │ ├── ic_cross_white@2x.png │ └── ic_cross_white@3x.png ├── UIImage+AspectFit.swift ├── UIImageView+Tools.swift └── ZoomAnimatedTransitioning.swift ├── Example ├── KFImageViewer.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ └── contents.xcworkspacedata │ └── xcshareddata │ │ └── xcschemes │ │ └── KFImageViewer-Example.xcscheme ├── KFImageViewer.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist ├── KFImageViewer │ ├── AppDelegate.swift │ ├── Base.lproj │ │ ├── LaunchScreen.xib │ │ └── Main.storyboard │ ├── Images.xcassets │ │ └── AppIcon.appiconset │ │ │ └── Contents.json │ ├── Info.plist │ ├── KFImageViewer.gif │ ├── KFImageViewer_02.gif │ └── ViewController.swift ├── Podfile ├── Podfile.lock ├── Pods │ ├── Kingfisher │ │ ├── LICENSE │ │ ├── README.md │ │ └── Sources │ │ │ ├── AnimatedImageView.swift │ │ │ ├── Box.swift │ │ │ ├── CacheSerializer.swift │ │ │ ├── Filter.swift │ │ │ ├── FormatIndicatedCacheSerializer.swift │ │ │ ├── Image.swift │ │ │ ├── ImageCache.swift │ │ │ ├── ImageDownloader.swift │ │ │ ├── ImageModifier.swift │ │ │ ├── ImagePrefetcher.swift │ │ │ ├── ImageProcessor.swift │ │ │ ├── ImageTransition.swift │ │ │ ├── ImageView+Kingfisher.swift │ │ │ ├── Indicator.swift │ │ │ ├── Kingfisher.h │ │ │ ├── Kingfisher.swift │ │ │ ├── KingfisherManager.swift │ │ │ ├── KingfisherOptionsInfo.swift │ │ │ ├── Placeholder.swift │ │ │ ├── RequestModifier.swift │ │ │ ├── Resource.swift │ │ │ ├── String+MD5.swift │ │ │ ├── ThreadHelper.swift │ │ │ └── UIButton+Kingfisher.swift │ ├── Local Podspecs │ │ └── KFImageViewer.podspec.json │ ├── Manifest.lock │ ├── Pods.xcodeproj │ │ ├── project.pbxproj │ │ └── project.xcworkspace │ │ │ ├── contents.xcworkspacedata │ │ │ └── xcshareddata │ │ │ └── IDEWorkspaceChecks.plist │ └── Target Support Files │ │ ├── KFImageViewer │ │ ├── Info.plist │ │ ├── KFImageViewer-dummy.m │ │ ├── KFImageViewer-prefix.pch │ │ ├── KFImageViewer-umbrella.h │ │ ├── KFImageViewer.modulemap │ │ └── KFImageViewer.xcconfig │ │ ├── Kingfisher │ │ ├── Info.plist │ │ ├── Kingfisher-dummy.m │ │ ├── Kingfisher-prefix.pch │ │ ├── Kingfisher-umbrella.h │ │ ├── Kingfisher.modulemap │ │ └── Kingfisher.xcconfig │ │ ├── Pods-KFImageViewer_Example │ │ ├── Info.plist │ │ ├── Pods-KFImageViewer_Example-acknowledgements.markdown │ │ ├── Pods-KFImageViewer_Example-acknowledgements.plist │ │ ├── Pods-KFImageViewer_Example-dummy.m │ │ ├── Pods-KFImageViewer_Example-frameworks.sh │ │ ├── Pods-KFImageViewer_Example-resources.sh │ │ ├── Pods-KFImageViewer_Example-umbrella.h │ │ ├── Pods-KFImageViewer_Example.debug.xcconfig │ │ ├── Pods-KFImageViewer_Example.modulemap │ │ └── Pods-KFImageViewer_Example.release.xcconfig │ │ └── Pods-KFImageViewer_Tests │ │ ├── Info.plist │ │ ├── Pods-KFImageViewer_Tests-acknowledgements.markdown │ │ ├── Pods-KFImageViewer_Tests-acknowledgements.plist │ │ ├── Pods-KFImageViewer_Tests-dummy.m │ │ ├── Pods-KFImageViewer_Tests-frameworks.sh │ │ ├── Pods-KFImageViewer_Tests-resources.sh │ │ ├── Pods-KFImageViewer_Tests-umbrella.h │ │ ├── Pods-KFImageViewer_Tests.debug.xcconfig │ │ ├── Pods-KFImageViewer_Tests.modulemap │ │ └── Pods-KFImageViewer_Tests.release.xcconfig └── Tests │ ├── Info.plist │ └── Tests.swift ├── KFImageViewer.podspec ├── Kingfisher └── KingfisherSource.swift ├── LICENSE ├── Podfile ├── README.md └── _Pods.xcodeproj /.gitignore: -------------------------------------------------------------------------------- 1 | # OS X 2 | .DS_Store 3 | 4 | # Xcode 5 | build/ 6 | *.pbxuser 7 | !default.pbxuser 8 | *.mode1v3 9 | !default.mode1v3 10 | *.mode2v3 11 | !default.mode2v3 12 | *.perspectivev3 13 | !default.perspectivev3 14 | xcuserdata/ 15 | *.xccheckout 16 | profile 17 | *.moved-aside 18 | DerivedData 19 | *.hmap 20 | *.ipa 21 | 22 | # Bundler 23 | .bundle 24 | 25 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 26 | # Carthage/Checkouts 27 | 28 | Carthage/Build 29 | 30 | # We recommend against adding the Pods directory to your .gitignore. However 31 | # you should judge for yourself, the pros and cons are mentioned at: 32 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-ignore-the-pods-directory-in-source-control 33 | # 34 | # Note: if you ignore the Pods directory, make sure to uncomment 35 | # `pod install` in .travis.yml 36 | # 37 | # Pods/ 38 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | # references: 2 | # * https://www.objc.io/issues/6-build-tools/travis-ci/ 3 | # * https://github.com/supermarin/xcpretty#usage 4 | 5 | osx_image: xcode7.3 6 | language: objective-c 7 | # cache: cocoapods 8 | # podfile: Example/Podfile 9 | # before_install: 10 | # - gem install cocoapods # Since Travis is not always on latest version 11 | # - pod install --project-directory=Example 12 | script: 13 | - set -o pipefail && xcodebuild test -enableCodeCoverage YES -workspace Example/KFImageViewer.xcworkspace -scheme KFImageViewer-Example -sdk iphonesimulator9.3 ONLY_ACTIVE_ARCH=NO | xcpretty 14 | - pod lib lint 15 | -------------------------------------------------------------------------------- /Core/ActivityIndicator.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ActivityIndicator.swift 3 | // KFImageViewer 4 | // 5 | // Created by Petr Zvoníček on 01.05.17. 6 | // 7 | 8 | import UIKit 9 | 10 | /// Cusotm Activity Indicator can be used by implementing this protocol 11 | public protocol ActivityIndicatorView { 12 | /// View of the activity indicator 13 | var view: UIView { get } 14 | 15 | /// Show activity indicator 16 | func show() 17 | 18 | /// Hide activity indicator 19 | func hide() 20 | } 21 | 22 | /// Factory protocol to create new ActivityIndicatorViews. Meant to be implemented when creating custom activity indicator. 23 | public protocol ActivityIndicatorFactory { 24 | func create() -> ActivityIndicatorView 25 | } 26 | 27 | /// Default ActivityIndicatorView implementation for UIActivityIndicatorView 28 | extension UIActivityIndicatorView: ActivityIndicatorView { 29 | public var view: UIView { 30 | return self 31 | } 32 | 33 | public func show() { 34 | startAnimating() 35 | } 36 | 37 | public func hide() { 38 | stopAnimating() 39 | } 40 | } 41 | 42 | /// Default activity indicator factory creating UIActivityIndicatorView instances 43 | @objcMembers 44 | open class DefaultActivityIndicator: ActivityIndicatorFactory { 45 | /// activity indicator style 46 | open var style: UIActivityIndicatorView.Style 47 | /// activity indicator color 48 | open var color: UIColor? 49 | 50 | /// Create a new ActivityIndicator for UIActivityIndicatorView 51 | /// 52 | /// - style: activity indicator style 53 | /// - color: activity indicator color 54 | public init(style: UIActivityIndicatorView.Style = .gray, color: UIColor? = nil) { 55 | self.style = style 56 | self.color = color 57 | } 58 | 59 | /// create ActivityIndicatorView instance 60 | open func create() -> ActivityIndicatorView { 61 | let activityIndicator = UIActivityIndicatorView(style: style) 62 | activityIndicator.color = color 63 | activityIndicator.hidesWhenStopped = true 64 | 65 | return activityIndicator 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /Core/FullScreenSlideshowViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FullScreenSlideshowViewController.swift 3 | // KFImageViewer 4 | // 5 | // Created by Petr Zvoníček on 31.08.15. 6 | // 7 | 8 | import UIKit 9 | 10 | @objcMembers 11 | open class FullScreenSlideshowViewController: UIViewController { 12 | 13 | open var slideshow: KFImageViewer = { 14 | let slideshow = KFImageViewer() 15 | slideshow.zoomEnabled = true 16 | slideshow.contentScaleMode = UIView.ContentMode.scaleAspectFit 17 | slideshow.pageIndicatorPosition = PageIndicatorPosition(horizontal: .center, vertical: .bottom) 18 | // turns off the timer 19 | slideshow.slideshowInterval = 0 20 | slideshow.autoresizingMask = [UIView.AutoresizingMask.flexibleWidth, UIView.AutoresizingMask.flexibleHeight] 21 | 22 | return slideshow 23 | }() 24 | 25 | /// Close button 26 | open var closeButton = UIButton() 27 | 28 | /// Close button frame 29 | open var closeButtonFrame: CGRect? 30 | 31 | /// Closure called on page selection 32 | open var pageSelected: ((_ page: Int) -> Void)? 33 | 34 | /// Index of initial image 35 | open var initialPage: Int = 0 36 | 37 | /// Input sources to 38 | open var inputs: [InputSource]? 39 | 40 | /// Background color 41 | open var backgroundColor = UIColor.black 42 | 43 | /// Enables/disable zoom 44 | open var zoomEnabled = true { 45 | didSet { 46 | slideshow.zoomEnabled = zoomEnabled 47 | } 48 | } 49 | 50 | fileprivate var isInit = true 51 | 52 | override open func viewDidLoad() { 53 | super.viewDidLoad() 54 | 55 | view.backgroundColor = backgroundColor 56 | slideshow.backgroundColor = backgroundColor 57 | 58 | if let inputs = inputs { 59 | slideshow.setImageInputs(inputs) 60 | } 61 | 62 | view.addSubview(slideshow) 63 | 64 | // close button configuration 65 | closeButton.setImage(UIImage(named: "ic_cross_white", in: Bundle(for: type(of: self)), compatibleWith: nil), for: UIControl.State()) 66 | closeButton.addTarget(self, action: #selector(FullScreenSlideshowViewController.close), for: UIControl.Event.touchUpInside) 67 | view.addSubview(closeButton) 68 | } 69 | 70 | override open var prefersStatusBarHidden: Bool { 71 | return true 72 | } 73 | 74 | override open func viewWillAppear(_ animated: Bool) { 75 | super.viewWillAppear(animated) 76 | 77 | if isInit { 78 | isInit = false 79 | slideshow.setCurrentPage(initialPage, animated: false) 80 | } 81 | } 82 | 83 | override open func viewWillDisappear(_ animated: Bool) { 84 | super.viewWillDisappear(animated) 85 | 86 | slideshow.slideshowItems.forEach { $0.cancelPendingLoad() } 87 | } 88 | 89 | open override func viewDidLayoutSubviews() { 90 | if !isBeingDismissed { 91 | let safeAreaInsets: UIEdgeInsets 92 | if #available(iOS 11.0, *) { 93 | safeAreaInsets = view.safeAreaInsets 94 | } else { 95 | safeAreaInsets = UIEdgeInsets.zero 96 | } 97 | 98 | closeButton.frame = closeButtonFrame ?? CGRect(x: max(10, safeAreaInsets.left), y: max(10, safeAreaInsets.top), width: 40, height: 40) 99 | } 100 | 101 | slideshow.frame = view.frame 102 | } 103 | 104 | @objc func close() { 105 | // if pageSelected closure set, send call it with current page 106 | if let pageSelected = pageSelected { 107 | pageSelected(slideshow.currentPage) 108 | } 109 | 110 | dismiss(animated: true, completion: nil) 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /Core/ImageSlideshowItem.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ZoomablePhotoView.swift 3 | // KFImageViewer 4 | // 5 | // Created by Petr Zvoníček on 30.07.15. 6 | // 7 | 8 | import UIKit 9 | 10 | /// Used to wrap a single slideshow item and allow zooming on it 11 | @objcMembers 12 | open class ImageSlideshowItem: UIScrollView, UIScrollViewDelegate { 13 | 14 | /// Image view to hold the image 15 | public let imageView = UIImageView() 16 | 17 | /// Activity indicator shown during image loading, when nil there won't be shown any 18 | public let activityIndicator: ActivityIndicatorView? 19 | 20 | open var progressView = UIProgressView() 21 | 22 | /// Input Source for the item 23 | public let image: InputSource 24 | 25 | /// Guesture recognizer to detect double tap to zoom 26 | open var gestureRecognizer: UITapGestureRecognizer? 27 | 28 | /// Holds if the zoom feature is enabled 29 | public let zoomEnabled: Bool 30 | 31 | /// If set to true image is initially zoomed in 32 | open var zoomInInitially = false 33 | 34 | /// Maximum zoom scale 35 | open var maximumScale: CGFloat = 2.0 36 | 37 | fileprivate var lastFrame = CGRect.zero 38 | fileprivate var imageReleased = false 39 | fileprivate var isLoading = false 40 | fileprivate var singleTapGestureRecognizer: UITapGestureRecognizer? 41 | fileprivate var loadFailed = false { 42 | didSet { 43 | singleTapGestureRecognizer?.isEnabled = loadFailed 44 | gestureRecognizer?.isEnabled = !loadFailed 45 | } 46 | } 47 | 48 | // MARK: - Life cycle 49 | 50 | /** 51 | Initializes a new ImageSlideshowItem 52 | - parameter image: Input Source to load the image 53 | - parameter zoomEnabled: holds if it should be possible to zoom-in the image 54 | */ 55 | init(image: InputSource, zoomEnabled: Bool, activityIndicator: ActivityIndicatorView? = nil, maximumScale: CGFloat = 2.0) { 56 | self.zoomEnabled = zoomEnabled 57 | self.image = image 58 | self.activityIndicator = activityIndicator 59 | self.maximumScale = maximumScale 60 | 61 | super.init(frame: CGRect.null) 62 | 63 | imageView.clipsToBounds = true 64 | imageView.isUserInteractionEnabled = true 65 | 66 | setPictoCenter() 67 | 68 | // scroll view configuration 69 | delegate = self 70 | showsVerticalScrollIndicator = false 71 | showsHorizontalScrollIndicator = false 72 | addSubview(imageView) 73 | minimumZoomScale = 1.0 74 | maximumZoomScale = calculateMaximumScale() 75 | 76 | if let activityIndicator = activityIndicator { 77 | addSubview(activityIndicator.view) 78 | } 79 | 80 | progressView = UIProgressView(frame: CGRect(x: 0, y: 0, width: UIScreen.main.bounds.width, height: 30)) 81 | progressView.trackTintColor = UIColor.clear 82 | addSubview(progressView) 83 | 84 | // tap gesture recognizer 85 | let tapRecognizer = UITapGestureRecognizer(target: self, action: #selector(ImageSlideshowItem.tapZoom)) 86 | tapRecognizer.numberOfTapsRequired = 2 87 | imageView.addGestureRecognizer(tapRecognizer) 88 | gestureRecognizer = tapRecognizer 89 | 90 | singleTapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(retryLoadImage)) 91 | singleTapGestureRecognizer!.numberOfTapsRequired = 1 92 | singleTapGestureRecognizer!.isEnabled = false 93 | imageView.addGestureRecognizer(singleTapGestureRecognizer!) 94 | } 95 | 96 | required public init?(coder aDecoder: NSCoder) { 97 | fatalError("init(coder:) has not been implemented") 98 | } 99 | 100 | override open func layoutSubviews() { 101 | super.layoutSubviews() 102 | 103 | if !zoomEnabled { 104 | imageView.frame.size = frame.size 105 | } else if !isZoomed() { 106 | imageView.frame.size = calculatePictureSize() 107 | } 108 | 109 | if isFullScreen() { 110 | clearContentInsets() 111 | } else { 112 | setPictoCenter() 113 | } 114 | 115 | self.activityIndicator?.view.center = imageView.center 116 | 117 | // if self.frame was changed and zoomInInitially enabled, zoom in 118 | if lastFrame != frame && zoomInInitially { 119 | setZoomScale(maximumZoomScale, animated: false) 120 | } 121 | 122 | lastFrame = self.frame 123 | 124 | contentSize = imageView.frame.size 125 | maximumZoomScale = calculateMaximumScale() 126 | } 127 | 128 | /// Request to load Image Source to Image View 129 | public func loadImage() { 130 | if self.imageView.image == nil && !isLoading { 131 | isLoading = true 132 | imageReleased = false 133 | activityIndicator?.show() 134 | image.load(to: self.imageView) {[weak self] image in 135 | // set image to nil if there was a release request during the image load 136 | if let imageRelease = self?.imageReleased, imageRelease { 137 | self?.imageView.image = nil 138 | }else{ 139 | self?.imageView.image = image 140 | } 141 | self?.activityIndicator?.hide() 142 | self?.loadFailed = image == nil 143 | self?.isLoading = false 144 | } 145 | } 146 | } 147 | 148 | public func loadImageWithProgress() 149 | { 150 | if self.imageView.image == nil && !isLoading { 151 | isLoading = true 152 | imageReleased = false 153 | activityIndicator?.show() 154 | self.progressView.isHidden = false 155 | image.loadWithProgress!(to: self.imageView, with: {[weak self] image in 156 | // set image to nil if there was a release request during the image load 157 | if let imageRelease = self?.imageReleased, imageRelease { 158 | self?.imageView.image = nil 159 | }else{ 160 | self?.imageView.image = image 161 | } 162 | self?.progressView.isHidden = true 163 | self?.activityIndicator?.hide() 164 | self?.loadFailed = image == nil 165 | self?.isLoading = false 166 | }) { (progress) in 167 | self.progressView.progress = Float(progress) 168 | } 169 | } 170 | } 171 | 172 | func releaseImage() { 173 | imageReleased = true 174 | cancelPendingLoad() 175 | self.imageView.image = nil 176 | } 177 | 178 | public func cancelPendingLoad() { 179 | image.cancelLoad?(on: imageView) 180 | } 181 | 182 | @objc func retryLoadImage() { 183 | //self.loadImage() 184 | self.loadImageWithProgress() 185 | } 186 | 187 | // MARK: - Image zoom & size 188 | 189 | func isZoomed() -> Bool { 190 | return self.zoomScale != self.minimumZoomScale 191 | } 192 | 193 | func zoomOut() { 194 | self.setZoomScale(minimumZoomScale, animated: false) 195 | } 196 | 197 | @objc func tapZoom() { 198 | if isZoomed() { 199 | self.setZoomScale(minimumZoomScale, animated: true) 200 | } else { 201 | self.setZoomScale(maximumZoomScale, animated: true) 202 | } 203 | } 204 | 205 | fileprivate func screenSize() -> CGSize { 206 | return CGSize(width: frame.width, height: frame.height) 207 | } 208 | 209 | fileprivate func calculatePictureFrame() { 210 | let boundsSize: CGSize = bounds.size 211 | var frameToCenter: CGRect = imageView.frame 212 | 213 | if frameToCenter.size.width < boundsSize.width { 214 | frameToCenter.origin.x = (boundsSize.width - frameToCenter.size.width) / 2 215 | } else { 216 | frameToCenter.origin.x = 0 217 | } 218 | 219 | if frameToCenter.size.height < boundsSize.height { 220 | frameToCenter.origin.y = (boundsSize.height - frameToCenter.size.height) / 2 221 | } else { 222 | frameToCenter.origin.y = 0 223 | } 224 | 225 | imageView.frame = frameToCenter 226 | } 227 | 228 | fileprivate func calculatePictureSize() -> CGSize { 229 | if let image = imageView.image, imageView.contentMode == .scaleAspectFit { 230 | let picSize = image.size 231 | let picRatio = picSize.width / picSize.height 232 | let screenRatio = screenSize().width / screenSize().height 233 | 234 | if picRatio > screenRatio { 235 | return CGSize(width: screenSize().width, height: screenSize().width / picSize.width * picSize.height) 236 | } else { 237 | return CGSize(width: screenSize().height / picSize.height * picSize.width, height: screenSize().height) 238 | } 239 | } else { 240 | return CGSize(width: screenSize().width, height: screenSize().height) 241 | } 242 | } 243 | 244 | fileprivate func calculateMaximumScale() -> CGFloat { 245 | return maximumScale 246 | } 247 | 248 | fileprivate func setPictoCenter() { 249 | var intendHorizon = (screenSize().width - imageView.frame.width ) / 2 250 | var intendVertical = (screenSize().height - imageView.frame.height ) / 2 251 | intendHorizon = intendHorizon > 0 ? intendHorizon : 0 252 | intendVertical = intendVertical > 0 ? intendVertical : 0 253 | contentInset = UIEdgeInsets(top: intendVertical, left: intendHorizon, bottom: intendVertical, right: intendHorizon) 254 | } 255 | 256 | private func isFullScreen() -> Bool { 257 | return imageView.frame.width >= screenSize().width && imageView.frame.height >= screenSize().height 258 | } 259 | 260 | func clearContentInsets() { 261 | contentInset = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0) 262 | } 263 | 264 | // MARK: UIScrollViewDelegate 265 | 266 | open func scrollViewDidZoom(_ scrollView: UIScrollView) { 267 | setPictoCenter() 268 | } 269 | 270 | open func viewForZooming(in scrollView: UIScrollView) -> UIView? { 271 | return zoomEnabled ? imageView : nil 272 | } 273 | 274 | } 275 | -------------------------------------------------------------------------------- /Core/InputSource.swift: -------------------------------------------------------------------------------- 1 | // 2 | // InputSource.swift 3 | // KFImageViewer 4 | // 5 | // Created by Petr Zvoníček on 14.01.16. 6 | // 7 | // 8 | 9 | import UIKit 10 | 11 | /// A protocol that can be adapted by different Input Source providers 12 | @objc public protocol InputSource { 13 | /** 14 | Load image from the source to image view. 15 | - parameter imageView: Image view to load the image into. 16 | - parameter callback: Callback called after image was set to the image view. 17 | - parameter image: Image that was set to the image view. 18 | */ 19 | func load(to imageView: UIImageView, with callback: @escaping (_ image: UIImage?) -> Void) 20 | 21 | @objc optional func loadWithProgress(to imageView: UIImageView, with callback: @escaping (_ image: UIImage?) -> Void, progress: @escaping(_ progress: CGFloat) ->Void) 22 | /** 23 | Cancel image load on the image view 24 | - parameter imageView: Image view that is loading the image 25 | */ 26 | @objc optional func cancelLoad(on imageView: UIImageView) 27 | } 28 | 29 | /// Input Source to load plain UIImage 30 | @objcMembers 31 | open class ImageSource: NSObject, InputSource { 32 | var image: UIImage! 33 | 34 | /// Initializes a new Image Source with UIImage 35 | /// - parameter image: Image to be loaded 36 | public init(image: UIImage) { 37 | self.image = image 38 | } 39 | 40 | /// Initializes a new Image Source with an image name from the main bundle 41 | /// - parameter imageString: name of the file in the application's main bundle 42 | public init?(imageString: String) { 43 | if let image = UIImage(named: imageString) { 44 | self.image = image 45 | super.init() 46 | } else { 47 | return nil 48 | } 49 | } 50 | 51 | public func load(to imageView: UIImageView, with callback: @escaping (UIImage?) -> Void) { 52 | imageView.image = image 53 | callback(image) 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /Core/PageIndicator.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PageIndicator.swift 3 | // KFImageViewer 4 | // 5 | // Created by Petr Zvoníček on 27.05.18. 6 | // 7 | 8 | import UIKit 9 | 10 | /// Cusotm Page Indicator can be used by implementing this protocol 11 | public protocol PageIndicatorView: class { 12 | /// View of the page indicator 13 | var view: UIView { get } 14 | 15 | /// Current page of the page indicator 16 | var page: Int { get set } 17 | 18 | /// Total number of pages of the page indicator 19 | var numberOfPages: Int { get set} 20 | } 21 | 22 | extension UIPageControl: PageIndicatorView { 23 | public var view: UIView { 24 | return self 25 | } 26 | 27 | public var page: Int { 28 | get { 29 | return currentPage 30 | } 31 | set { 32 | currentPage = newValue 33 | } 34 | } 35 | 36 | open override func sizeToFit() { 37 | var frame = self.frame 38 | frame.size = size(forNumberOfPages: numberOfPages) 39 | frame.size.height = 30 40 | self.frame = frame 41 | } 42 | } 43 | 44 | /// Page indicator that shows page in numeric style, eg. "5/21" 45 | public class LabelPageIndicator: UILabel, PageIndicatorView { 46 | public var view: UIView { 47 | return self 48 | } 49 | 50 | public var numberOfPages: Int = 0 { 51 | didSet { 52 | updateLabel() 53 | } 54 | } 55 | 56 | public var page: Int = 0 { 57 | didSet { 58 | updateLabel() 59 | } 60 | } 61 | 62 | public override init(frame: CGRect) { 63 | super.init(frame: frame) 64 | initialize() 65 | } 66 | 67 | required public init?(coder aDecoder: NSCoder) { 68 | super.init(coder: aDecoder) 69 | initialize() 70 | } 71 | 72 | private func initialize() { 73 | self.textAlignment = .center 74 | } 75 | 76 | private func updateLabel() { 77 | text = "\(page+1)/\(numberOfPages)" 78 | } 79 | 80 | public override func sizeToFit() { 81 | let maximumString = String(repeating: "8", count: numberOfPages) as NSString 82 | self.frame.size = maximumString.size(withAttributes: [.font: font]) 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /Core/PageIndicatorPosition.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PageIndicator.swift 3 | // KFImageViewer 4 | // 5 | // Created by Petr Zvoníček on 04.02.18. 6 | // 7 | 8 | import UIKit 9 | 10 | /// Describes the configuration of the page indicator position 11 | public struct PageIndicatorPosition { 12 | public enum Horizontal { 13 | case left(padding: CGFloat), center, right(padding: CGFloat) 14 | } 15 | 16 | public enum Vertical { 17 | case top, bottom, under, customTop(padding: CGFloat), customBottom(padding: CGFloat), customUnder(padding: CGFloat) 18 | } 19 | 20 | /// Horizontal position of the page indicator 21 | var horizontal: Horizontal 22 | 23 | /// Vertical position of the page indicator 24 | var vertical: Vertical 25 | 26 | /// Creates a new PageIndicatorPosition struct 27 | /// 28 | /// - Parameters: 29 | /// - horizontal: horizontal position of the page indicator 30 | /// - vertical: vertical position of the page indicator 31 | public init(horizontal: Horizontal = .center, vertical: Vertical = .bottom) { 32 | self.horizontal = horizontal 33 | self.vertical = vertical 34 | } 35 | 36 | /// Computes the additional padding needed for the page indicator under the KFImageViewer 37 | /// 38 | /// - Parameter indicatorSize: size of the page indicator 39 | /// - Returns: padding needed under the KFImageViewer 40 | func underPadding(for indicatorSize: CGSize) -> CGFloat { 41 | switch vertical { 42 | case .under: 43 | return indicatorSize.height 44 | case .customUnder(let padding): 45 | return indicatorSize.height + padding 46 | default: 47 | return 0 48 | } 49 | } 50 | 51 | /// Computes the page indicator frame 52 | /// 53 | /// - Parameters: 54 | /// - parentFrame: frame of the parent view – KFImageViewer 55 | /// - indicatorSize: size of the page indicator 56 | /// - edgeInsets: edge insets of the parent view – KFImageViewer (used for SafeAreaInsets adjustment) 57 | /// - Returns: frame of the indicator by computing the origin and using `indicatorSize` as size 58 | func indicatorFrame(for parentFrame: CGRect, indicatorSize: CGSize, edgeInsets: UIEdgeInsets) -> CGRect { 59 | var xSize: CGFloat = 0 60 | var ySize: CGFloat = 0 61 | 62 | switch horizontal { 63 | case .center: 64 | xSize = parentFrame.size.width / 2 - indicatorSize.width / 2 65 | case .left(let padding): 66 | xSize = padding + edgeInsets.left 67 | case .right(let padding): 68 | xSize = parentFrame.size.width - indicatorSize.width - padding - edgeInsets.right 69 | } 70 | 71 | switch vertical { 72 | case .bottom, .under, .customUnder: 73 | ySize = parentFrame.size.height - indicatorSize.height - edgeInsets.bottom 74 | case .customBottom(let padding): 75 | ySize = parentFrame.size.height - indicatorSize.height - padding - edgeInsets.bottom 76 | case .top: 77 | ySize = edgeInsets.top 78 | case .customTop(let padding): 79 | ySize = padding + edgeInsets.top 80 | } 81 | 82 | return CGRect(x: xSize, y: ySize, width: indicatorSize.width, height: indicatorSize.height) 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /Core/Resources/ic_cross_white@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/faisalazeez/KFImageViewer/1564b20f5b21933e3f47b093a614d5480211a49d/Core/Resources/ic_cross_white@2x.png -------------------------------------------------------------------------------- /Core/Resources/ic_cross_white@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/faisalazeez/KFImageViewer/1564b20f5b21933e3f47b093a614d5480211a49d/Core/Resources/ic_cross_white@3x.png -------------------------------------------------------------------------------- /Core/UIImage+AspectFit.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIImage+AspectFit.swift 3 | // KFImageViewer 4 | // 5 | // Created by Petr Zvoníček on 31.08.15. 6 | // 7 | // 8 | 9 | import UIKit 10 | 11 | extension UIImage { 12 | 13 | func tgr_aspectFitRectForSize(_ size: CGSize) -> CGRect { 14 | let targetAspect: CGFloat = size.width / size.height 15 | let sourceAspect: CGFloat = self.size.width / self.size.height 16 | var rect: CGRect = CGRect.zero 17 | 18 | if targetAspect > sourceAspect { 19 | rect.size.height = size.height 20 | rect.size.width = ceil(rect.size.height * sourceAspect) 21 | rect.origin.x = ceil((size.width - rect.size.width) * 0.5) 22 | } else { 23 | rect.size.width = size.width 24 | rect.size.height = ceil(rect.size.width / sourceAspect) 25 | rect.origin.y = ceil((size.height - rect.size.height) * 0.5) 26 | } 27 | 28 | return rect 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Core/UIImageView+Tools.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIImageView+Tools.swift 3 | // Pods 4 | // 5 | // Created by Aleš Kocur on 20/04/16. 6 | // 7 | // 8 | 9 | import UIKit 10 | 11 | extension UIImageView { 12 | 13 | func aspectToFitFrame() -> CGRect { 14 | 15 | guard let image = image else { 16 | assertionFailure("No image found!") 17 | return CGRect.zero 18 | } 19 | 20 | let imageRatio: CGFloat = image.size.width / image.size.height 21 | let viewRatio: CGFloat = frame.size.width / frame.size.height 22 | 23 | if imageRatio < viewRatio { 24 | let scale: CGFloat = frame.size.height / image.size.height 25 | let width: CGFloat = scale * image.size.width 26 | let topLeftX: CGFloat = (frame.size.width - width) * 0.5 27 | return CGRect(x: topLeftX, y: 0, width: width, height: frame.size.height) 28 | } else { 29 | let scale: CGFloat = frame.size.width / image.size.width 30 | let height: CGFloat = scale * image.size.height 31 | let topLeftY: CGFloat = (frame.size.height - height) * 0.5 32 | return CGRect(x: 0, y: topLeftY, width: frame.size.width, height: height) 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /Example/KFImageViewer.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Example/KFImageViewer.xcodeproj/xcshareddata/xcschemes/KFImageViewer-Example.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 29 | 35 | 36 | 37 | 38 | 39 | 45 | 46 | 48 | 54 | 55 | 56 | 57 | 58 | 64 | 65 | 66 | 67 | 68 | 69 | 80 | 82 | 88 | 89 | 90 | 91 | 92 | 93 | 99 | 101 | 107 | 108 | 109 | 110 | 112 | 113 | 116 | 117 | 118 | -------------------------------------------------------------------------------- /Example/KFImageViewer.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Example/KFImageViewer.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Example/KFImageViewer/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // KFImageViewer 4 | // 5 | // Created by faisalazeez on 08/09/2018. 6 | // Copyright (c) 2018 faisalazeez. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @UIApplicationMain 12 | class AppDelegate: UIResponder, UIApplicationDelegate { 13 | 14 | var window: UIWindow? 15 | 16 | 17 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { 18 | // Override point for customization after application launch. 19 | return true 20 | } 21 | 22 | func applicationWillResignActive(_ application: UIApplication) { 23 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 24 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. 25 | } 26 | 27 | func applicationDidEnterBackground(_ application: UIApplication) { 28 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 29 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 30 | } 31 | 32 | func applicationWillEnterForeground(_ application: UIApplication) { 33 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. 34 | } 35 | 36 | func applicationDidBecomeActive(_ application: UIApplication) { 37 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 38 | } 39 | 40 | func applicationWillTerminate(_ application: UIApplication) { 41 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 42 | } 43 | 44 | 45 | } 46 | 47 | -------------------------------------------------------------------------------- /Example/KFImageViewer/Base.lproj/LaunchScreen.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 25 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /Example/KFImageViewer/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /Example/KFImageViewer/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "29x29", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "29x29", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "40x40", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "40x40", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "size" : "60x60", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "size" : "60x60", 41 | "scale" : "3x" 42 | }, 43 | { 44 | "idiom" : "ios-marketing", 45 | "size" : "1024x1024", 46 | "scale" : "1x" 47 | } 48 | ], 49 | "info" : { 50 | "version" : 1, 51 | "author" : "xcode" 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /Example/KFImageViewer/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 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UIRequiredDeviceCapabilities 30 | 31 | armv7 32 | 33 | UISupportedInterfaceOrientations 34 | 35 | UIInterfaceOrientationPortrait 36 | UIInterfaceOrientationLandscapeLeft 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /Example/KFImageViewer/KFImageViewer.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/faisalazeez/KFImageViewer/1564b20f5b21933e3f47b093a614d5480211a49d/Example/KFImageViewer/KFImageViewer.gif -------------------------------------------------------------------------------- /Example/KFImageViewer/KFImageViewer_02.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/faisalazeez/KFImageViewer/1564b20f5b21933e3f47b093a614d5480211a49d/Example/KFImageViewer/KFImageViewer_02.gif -------------------------------------------------------------------------------- /Example/KFImageViewer/ViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // KFImageViewer 4 | // 5 | // Created by faisalazeez on 08/09/2018. 6 | // Copyright (c) 2018 faisalazeez. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import KFImageViewer 11 | 12 | class ViewController: UIViewController { 13 | 14 | /* 15 | * sample url as kingfisher Input source 16 | */ 17 | let photo:[InputSource] = [KingfisherSource(urlString: "https://eoimages.gsfc.nasa.gov/images/imagerecords/84000/84214/bluemarble_2014090_xlrg.jpg")!,KingfisherSource(urlString: "https://picsum.photos/1024/1000")!] 18 | 19 | override func viewDidLoad() { 20 | super.viewDidLoad() 21 | // Do any additional setup after loading the view, typically from a nib. 22 | } 23 | 24 | override func didReceiveMemoryWarning() { 25 | super.didReceiveMemoryWarning() 26 | // Dispose of any resources that can be recreated. 27 | } 28 | 29 | func callLIb() 30 | { 31 | let fullScreenController = FullScreenSlideshowViewController() 32 | fullScreenController.inputs = photo 33 | fullScreenController.slideshow.activityIndicator = DefaultActivityIndicator(style: .white, color: nil) 34 | 35 | /* 36 | if let cell = tableView.cellForRow(at: indexPath), let imageView = cell.imageView { 37 | slideshowTransitioningDelegate = ZoomAnimatedTransitioningDelegate(imageView: imageView, slideshowController: fullScreenController) 38 | fullScreenController.transitioningDelegate = slideshowTransitioningDelegate 39 | } 40 | 41 | fullScreenController.slideshow.currentPageChanged = { [weak self] page in 42 | if let cell = tableView.cellForRow(at: IndexPath(row: page, section: 0)), let imageView = cell.imageView { 43 | self?.slideshowTransitioningDelegate?.referenceImageView = imageView 44 | } 45 | } 46 | */ 47 | present(fullScreenController, animated: true, completion: nil) 48 | } 49 | @IBAction func action(_ sender: Any) 50 | { 51 | callLIb() 52 | } 53 | } 54 | 55 | -------------------------------------------------------------------------------- /Example/Podfile: -------------------------------------------------------------------------------- 1 | use_frameworks! 2 | 3 | target 'KFImageViewer_Example' do 4 | pod 'KFImageViewer', :path => '../' 5 | pod "KFImageViewer/Kingfisher", :path => "../" 6 | 7 | target 'KFImageViewer_Tests' do 8 | inherit! :search_paths 9 | end 10 | 11 | end 12 | -------------------------------------------------------------------------------- /Example/Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - KFImageViewer (1.0.0): 3 | - KFImageViewer/Core (= 1.0.0) 4 | - KFImageViewer/Core (1.0.0) 5 | - KFImageViewer/Kingfisher (1.0.0): 6 | - KFImageViewer/Core 7 | - Kingfisher (> 4.0) 8 | - Kingfisher (4.8.0) 9 | 10 | DEPENDENCIES: 11 | - KFImageViewer (from `../`) 12 | - KFImageViewer/Kingfisher (from `../`) 13 | 14 | SPEC REPOS: 15 | https://github.com/cocoapods/specs.git: 16 | - Kingfisher 17 | 18 | EXTERNAL SOURCES: 19 | KFImageViewer: 20 | :path: "../" 21 | 22 | SPEC CHECKSUMS: 23 | KFImageViewer: 98e9bbf8eb8547cbe068501675b95d8e14c5658a 24 | Kingfisher: 976d828df2b24834c6a3f2fc4d82cdbd26552be1 25 | 26 | PODFILE CHECKSUM: ec48d017d47cd4da82807056131dfeea12515cee 27 | 28 | COCOAPODS: 1.5.3 29 | -------------------------------------------------------------------------------- /Example/Pods/Kingfisher/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2018 Wei Wang 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /Example/Pods/Kingfisher/README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 | Kingfisher 4 | 5 |

6 | 7 |

8 | 9 | 10 | 11 | 12 | 13 | codebeat badge 14 | 15 | 16 |

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(transform: @escaping Transformer) { 60 | self.transform = transform 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(transform: { 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(transform: { 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 | // MARK: - Deprecated 105 | extension Filter { 106 | @available(*, deprecated, message: "Use init(transform:) instead.", renamed: "init(transform:)") 107 | public init(tranform: @escaping Transformer) { 108 | self.transform = tranform 109 | } 110 | } 111 | 112 | extension Kingfisher where Base: Image { 113 | /// Apply a `Filter` containing `CIImage` transformer to `self`. 114 | /// 115 | /// - parameter filter: The filter used to transform `self`. 116 | /// 117 | /// - returns: A transformed image by input `Filter`. 118 | /// 119 | /// - Note: Only CG-based images are supported. If any error happens during transforming, `self` will be returned. 120 | public func apply(_ filter: Filter) -> Image { 121 | 122 | guard let cgImage = cgImage else { 123 | assertionFailure("[Kingfisher] Tint image only works for CG-based image.") 124 | return base 125 | } 126 | 127 | let inputImage = CIImage(cgImage: cgImage) 128 | guard let outputImage = filter.transform(inputImage) else { 129 | return base 130 | } 131 | 132 | guard let result = ciContext.createCGImage(outputImage, from: outputImage.extent) else { 133 | assertionFailure("[Kingfisher] Can not make an tint image within context.") 134 | return base 135 | } 136 | 137 | #if os(macOS) 138 | return fixedForRetinaPixel(cgImage: result, to: size) 139 | #else 140 | return Image(cgImage: result, scale: base.scale, orientation: base.imageOrientation) 141 | #endif 142 | } 143 | 144 | } 145 | -------------------------------------------------------------------------------- /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: UIImage.RenderingMode 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: UIImage.RenderingMode = .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/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 | completion handler as well. 60 | */ 61 | public enum ImageTransition { 62 | /// No animation transition. 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: UIView.AnimationOptions, 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: UIView.AnimationOptions { 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 | // Already 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 = UIActivityIndicatorView.Style.gray 137 | #endif 138 | activityIndicatorView = UIActivityIndicatorView(style: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 | #else 46 | import WatchKit 47 | #endif 48 | #endif 49 | 50 | public final class Kingfisher { 51 | public let base: Base 52 | public init(_ base: Base) { 53 | self.base = base 54 | } 55 | } 56 | 57 | /** 58 | A type that has Kingfisher extensions. 59 | */ 60 | public protocol KingfisherCompatible { 61 | associatedtype CompatibleType 62 | var kf: CompatibleType { get } 63 | } 64 | 65 | public extension KingfisherCompatible { 66 | public var kf: Kingfisher { 67 | return Kingfisher(self) 68 | } 69 | } 70 | 71 | extension Image: KingfisherCompatible { } 72 | #if !os(watchOS) 73 | extension ImageView: KingfisherCompatible { } 74 | extension Button: KingfisherCompatible { } 75 | #else 76 | extension WKInterfaceImage: KingfisherCompatible { } 77 | #endif 78 | -------------------------------------------------------------------------------- /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: UIControl.State, 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: UIControl.State, 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: UIControl.State) -> URL? { 204 | return webURLs[NSNumber(value:state.rawValue)] as? URL 205 | } 206 | 207 | fileprivate func setWebURL(_ url: URL?, for state: UIControl.State) { 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: UIControl.State) -> URL? { 247 | return backgroundWebURLs[NSNumber(value:state.rawValue)] as? URL 248 | } 249 | 250 | fileprivate func setBackgroundWebURL(_ url: URL?, for state: UIControl.State) { 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/KFImageViewer.podspec.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "KFImageViewer", 3 | "version": "1.0.0", 4 | "summary": "KFImageViewer written in Swift with download progress, circular scrolling, timer and full screen viewer", 5 | "description": "KFImageViewer is a Kingfisher supported Swift library providing customizable image viewer with download progress, circular scrolling, timer and full screen viewer and extendable image source.", 6 | "homepage": "https://github.com/faisalazeez/KFImageViewer", 7 | "swift_version": "4.0", 8 | "license": { 9 | "type": "MIT", 10 | "file": "LICENSE" 11 | }, 12 | "authors": { 13 | "faisalazeez": "faisalazeez7@gmail.com" 14 | }, 15 | "source": { 16 | "git": "https://github.com/faisalazeez/KFImageViewer.git", 17 | "tag": "1.0.0" 18 | }, 19 | "platforms": { 20 | "ios": "9.0" 21 | }, 22 | "requires_arc": true, 23 | "default_subspecs": "Core", 24 | "subspecs": [ 25 | { 26 | "name": "Core", 27 | "source_files": "Core/**/*", 28 | "resources": "Core/Resources/*.png" 29 | }, 30 | { 31 | "name": "Kingfisher", 32 | "dependencies": { 33 | "KFImageViewer/Core": [ 34 | 35 | ], 36 | "Kingfisher": [ 37 | "> 4.0" 38 | ] 39 | }, 40 | "source_files": "Kingfisher/KingfisherSource.swift" 41 | } 42 | ] 43 | } 44 | -------------------------------------------------------------------------------- /Example/Pods/Manifest.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - KFImageViewer (1.0.0): 3 | - KFImageViewer/Core (= 1.0.0) 4 | - KFImageViewer/Core (1.0.0) 5 | - KFImageViewer/Kingfisher (1.0.0): 6 | - KFImageViewer/Core 7 | - Kingfisher (> 4.0) 8 | - Kingfisher (4.8.0) 9 | 10 | DEPENDENCIES: 11 | - KFImageViewer (from `../`) 12 | - KFImageViewer/Kingfisher (from `../`) 13 | 14 | SPEC REPOS: 15 | https://github.com/cocoapods/specs.git: 16 | - Kingfisher 17 | 18 | EXTERNAL SOURCES: 19 | KFImageViewer: 20 | :path: "../" 21 | 22 | SPEC CHECKSUMS: 23 | KFImageViewer: 98e9bbf8eb8547cbe068501675b95d8e14c5658a 24 | Kingfisher: 976d828df2b24834c6a3f2fc4d82cdbd26552be1 25 | 26 | PODFILE CHECKSUM: ec48d017d47cd4da82807056131dfeea12515cee 27 | 28 | COCOAPODS: 1.5.3 29 | -------------------------------------------------------------------------------- /Example/Pods/Pods.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Example/Pods/Pods.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/KFImageViewer/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/KFImageViewer/KFImageViewer-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_KFImageViewer : NSObject 3 | @end 4 | @implementation PodsDummy_KFImageViewer 5 | @end 6 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/KFImageViewer/KFImageViewer-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/KFImageViewer/KFImageViewer-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 KFImageViewerVersionNumber; 15 | FOUNDATION_EXPORT const unsigned char KFImageViewerVersionString[]; 16 | 17 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/KFImageViewer/KFImageViewer.modulemap: -------------------------------------------------------------------------------- 1 | framework module KFImageViewer { 2 | umbrella header "KFImageViewer-umbrella.h" 3 | 4 | export * 5 | module * { export * } 6 | } 7 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/KFImageViewer/KFImageViewer.xcconfig: -------------------------------------------------------------------------------- 1 | CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/KFImageViewer 2 | FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/Kingfisher" 3 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 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/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.8.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 | OTHER_LDFLAGS = -framework "CFNetwork" 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}/Kingfisher 9 | PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} 10 | SKIP_INSTALL = YES 11 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-KFImageViewer_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-KFImageViewer_Example/Pods-KFImageViewer_Example-acknowledgements.markdown: -------------------------------------------------------------------------------- 1 | # Acknowledgements 2 | This application makes use of the following third party libraries: 3 | 4 | ## KFImageViewer 5 | 6 | Copyright (c) 2018 faisalazeez 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 | 27 | ## Kingfisher 28 | 29 | The MIT License (MIT) 30 | 31 | Copyright (c) 2018 Wei Wang 32 | 33 | Permission is hereby granted, free of charge, to any person obtaining a copy 34 | of this software and associated documentation files (the "Software"), to deal 35 | in the Software without restriction, including without limitation the rights 36 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 37 | copies of the Software, and to permit persons to whom the Software is 38 | furnished to do so, subject to the following conditions: 39 | 40 | The above copyright notice and this permission notice shall be included in all 41 | copies or substantial portions of the Software. 42 | 43 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 44 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 45 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 46 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 47 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 48 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 49 | SOFTWARE. 50 | 51 | 52 | Generated by CocoaPods - https://cocoapods.org 53 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-KFImageViewer_Example/Pods-KFImageViewer_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 | Copyright (c) 2018 faisalazeez <faisal.azeez@msisoft.in> 18 | 19 | Permission is hereby granted, free of charge, to any person obtaining a copy 20 | of this software and associated documentation files (the "Software"), to deal 21 | in the Software without restriction, including without limitation the rights 22 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 23 | copies of the Software, and to permit persons to whom the Software is 24 | furnished to do so, subject to the following conditions: 25 | 26 | The above copyright notice and this permission notice shall be included in 27 | all copies or substantial portions of the Software. 28 | 29 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 30 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 31 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 32 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 33 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 34 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 35 | THE SOFTWARE. 36 | 37 | License 38 | MIT 39 | Title 40 | KFImageViewer 41 | Type 42 | PSGroupSpecifier 43 | 44 | 45 | FooterText 46 | The MIT License (MIT) 47 | 48 | Copyright (c) 2018 Wei Wang 49 | 50 | Permission is hereby granted, free of charge, to any person obtaining a copy 51 | of this software and associated documentation files (the "Software"), to deal 52 | in the Software without restriction, including without limitation the rights 53 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 54 | copies of the Software, and to permit persons to whom the Software is 55 | furnished to do so, subject to the following conditions: 56 | 57 | The above copyright notice and this permission notice shall be included in all 58 | copies or substantial portions of the Software. 59 | 60 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 61 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 62 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 63 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 64 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 65 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 66 | SOFTWARE. 67 | 68 | 69 | License 70 | MIT 71 | Title 72 | Kingfisher 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-KFImageViewer_Example/Pods-KFImageViewer_Example-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_Pods_KFImageViewer_Example : NSObject 3 | @end 4 | @implementation PodsDummy_Pods_KFImageViewer_Example 5 | @end 6 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-KFImageViewer_Example/Pods-KFImageViewer_Example-frameworks.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | set -u 4 | set -o pipefail 5 | 6 | if [ -z ${FRAMEWORKS_FOLDER_PATH+x} ]; then 7 | # If FRAMEWORKS_FOLDER_PATH is not set, then there's nowhere for us to copy 8 | # frameworks to, so exit 0 (signalling the script phase was successful). 9 | exit 0 10 | fi 11 | 12 | echo "mkdir -p ${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 13 | mkdir -p "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 14 | 15 | COCOAPODS_PARALLEL_CODE_SIGN="${COCOAPODS_PARALLEL_CODE_SIGN:-false}" 16 | SWIFT_STDLIB_PATH="${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" 17 | 18 | # Used as a return value for each invocation of `strip_invalid_archs` function. 19 | STRIP_BINARY_RETVAL=0 20 | 21 | # This protects against multiple targets copying the same framework dependency at the same time. The solution 22 | # was originally proposed here: https://lists.samba.org/archive/rsync/2008-February/020158.html 23 | RSYNC_PROTECT_TMP_FILES=(--filter "P .*.??????") 24 | 25 | # Copies and strips a vendored framework 26 | install_framework() 27 | { 28 | if [ -r "${BUILT_PRODUCTS_DIR}/$1" ]; then 29 | local source="${BUILT_PRODUCTS_DIR}/$1" 30 | elif [ -r "${BUILT_PRODUCTS_DIR}/$(basename "$1")" ]; then 31 | local source="${BUILT_PRODUCTS_DIR}/$(basename "$1")" 32 | elif [ -r "$1" ]; then 33 | local source="$1" 34 | fi 35 | 36 | local destination="${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 37 | 38 | if [ -L "${source}" ]; then 39 | echo "Symlinked..." 40 | source="$(readlink "${source}")" 41 | fi 42 | 43 | # Use filter instead of exclude so missing patterns don't throw errors. 44 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${destination}\"" 45 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${destination}" 46 | 47 | local basename 48 | basename="$(basename -s .framework "$1")" 49 | binary="${destination}/${basename}.framework/${basename}" 50 | if ! [ -r "$binary" ]; then 51 | binary="${destination}/${basename}" 52 | fi 53 | 54 | # Strip invalid architectures so "fat" simulator / device frameworks work on device 55 | if [[ "$(file "$binary")" == *"dynamically linked shared library"* ]]; then 56 | strip_invalid_archs "$binary" 57 | fi 58 | 59 | # Resign the code if required by the build settings to avoid unstable apps 60 | code_sign_if_enabled "${destination}/$(basename "$1")" 61 | 62 | # Embed linked Swift runtime libraries. No longer necessary as of Xcode 7. 63 | if [ "${XCODE_VERSION_MAJOR}" -lt 7 ]; then 64 | local swift_runtime_libs 65 | swift_runtime_libs=$(xcrun otool -LX "$binary" | grep --color=never @rpath/libswift | sed -E s/@rpath\\/\(.+dylib\).*/\\1/g | uniq -u && exit ${PIPESTATUS[0]}) 66 | for lib in $swift_runtime_libs; do 67 | echo "rsync -auv \"${SWIFT_STDLIB_PATH}/${lib}\" \"${destination}\"" 68 | rsync -auv "${SWIFT_STDLIB_PATH}/${lib}" "${destination}" 69 | code_sign_if_enabled "${destination}/${lib}" 70 | done 71 | fi 72 | } 73 | 74 | # Copies and strips a vendored dSYM 75 | install_dsym() { 76 | local source="$1" 77 | if [ -r "$source" ]; then 78 | # Copy the dSYM into a the targets temp dir. 79 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${DERIVED_FILES_DIR}\"" 80 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${DERIVED_FILES_DIR}" 81 | 82 | local basename 83 | basename="$(basename -s .framework.dSYM "$source")" 84 | binary="${DERIVED_FILES_DIR}/${basename}.framework.dSYM/Contents/Resources/DWARF/${basename}" 85 | 86 | # Strip invalid architectures so "fat" simulator / device frameworks work on device 87 | if [[ "$(file "$binary")" == *"Mach-O dSYM companion"* ]]; then 88 | strip_invalid_archs "$binary" 89 | fi 90 | 91 | if [[ $STRIP_BINARY_RETVAL == 1 ]]; then 92 | # Move the stripped file into its final destination. 93 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${DERIVED_FILES_DIR}/${basename}.framework.dSYM\" \"${DWARF_DSYM_FOLDER_PATH}\"" 94 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${DERIVED_FILES_DIR}/${basename}.framework.dSYM" "${DWARF_DSYM_FOLDER_PATH}" 95 | else 96 | # The dSYM was not stripped at all, in this case touch a fake folder so the input/output paths from Xcode do not reexecute this script because the file is missing. 97 | touch "${DWARF_DSYM_FOLDER_PATH}/${basename}.framework.dSYM" 98 | fi 99 | fi 100 | } 101 | 102 | # Signs a framework with the provided identity 103 | code_sign_if_enabled() { 104 | if [ -n "${EXPANDED_CODE_SIGN_IDENTITY}" -a "${CODE_SIGNING_REQUIRED:-}" != "NO" -a "${CODE_SIGNING_ALLOWED}" != "NO" ]; then 105 | # Use the current code_sign_identitiy 106 | echo "Code Signing $1 with Identity ${EXPANDED_CODE_SIGN_IDENTITY_NAME}" 107 | local code_sign_cmd="/usr/bin/codesign --force --sign ${EXPANDED_CODE_SIGN_IDENTITY} ${OTHER_CODE_SIGN_FLAGS:-} --preserve-metadata=identifier,entitlements '$1'" 108 | 109 | if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then 110 | code_sign_cmd="$code_sign_cmd &" 111 | fi 112 | echo "$code_sign_cmd" 113 | eval "$code_sign_cmd" 114 | fi 115 | } 116 | 117 | # Strip invalid architectures 118 | strip_invalid_archs() { 119 | binary="$1" 120 | # Get architectures for current target binary 121 | binary_archs="$(lipo -info "$binary" | rev | cut -d ':' -f1 | awk '{$1=$1;print}' | rev)" 122 | # Intersect them with the architectures we are building for 123 | intersected_archs="$(echo ${ARCHS[@]} ${binary_archs[@]} | tr ' ' '\n' | sort | uniq -d)" 124 | # If there are no archs supported by this binary then warn the user 125 | if [[ -z "$intersected_archs" ]]; then 126 | echo "warning: [CP] Vendored binary '$binary' contains architectures ($binary_archs) none of which match the current build architectures ($ARCHS)." 127 | STRIP_BINARY_RETVAL=0 128 | return 129 | fi 130 | stripped="" 131 | for arch in $binary_archs; do 132 | if ! [[ "${ARCHS}" == *"$arch"* ]]; then 133 | # Strip non-valid architectures in-place 134 | lipo -remove "$arch" -output "$binary" "$binary" || exit 1 135 | stripped="$stripped $arch" 136 | fi 137 | done 138 | if [[ "$stripped" ]]; then 139 | echo "Stripped $binary of architectures:$stripped" 140 | fi 141 | STRIP_BINARY_RETVAL=1 142 | } 143 | 144 | 145 | if [[ "$CONFIGURATION" == "Debug" ]]; then 146 | install_framework "${BUILT_PRODUCTS_DIR}/KFImageViewer/KFImageViewer.framework" 147 | install_framework "${BUILT_PRODUCTS_DIR}/Kingfisher/Kingfisher.framework" 148 | fi 149 | if [[ "$CONFIGURATION" == "Release" ]]; then 150 | install_framework "${BUILT_PRODUCTS_DIR}/KFImageViewer/KFImageViewer.framework" 151 | install_framework "${BUILT_PRODUCTS_DIR}/Kingfisher/Kingfisher.framework" 152 | fi 153 | if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then 154 | wait 155 | fi 156 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-KFImageViewer_Example/Pods-KFImageViewer_Example-resources.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | set -u 4 | set -o pipefail 5 | 6 | if [ -z ${UNLOCALIZED_RESOURCES_FOLDER_PATH+x} ]; then 7 | # If UNLOCALIZED_RESOURCES_FOLDER_PATH is not set, then there's nowhere for us to copy 8 | # resources to, so exit 0 (signalling the script phase was successful). 9 | exit 0 10 | fi 11 | 12 | mkdir -p "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 13 | 14 | RESOURCES_TO_COPY=${PODS_ROOT}/resources-to-copy-${TARGETNAME}.txt 15 | > "$RESOURCES_TO_COPY" 16 | 17 | XCASSET_FILES=() 18 | 19 | # This protects against multiple targets copying the same framework dependency at the same time. The solution 20 | # was originally proposed here: https://lists.samba.org/archive/rsync/2008-February/020158.html 21 | RSYNC_PROTECT_TMP_FILES=(--filter "P .*.??????") 22 | 23 | case "${TARGETED_DEVICE_FAMILY:-}" in 24 | 1,2) 25 | TARGET_DEVICE_ARGS="--target-device ipad --target-device iphone" 26 | ;; 27 | 1) 28 | TARGET_DEVICE_ARGS="--target-device iphone" 29 | ;; 30 | 2) 31 | TARGET_DEVICE_ARGS="--target-device ipad" 32 | ;; 33 | 3) 34 | TARGET_DEVICE_ARGS="--target-device tv" 35 | ;; 36 | 4) 37 | TARGET_DEVICE_ARGS="--target-device watch" 38 | ;; 39 | *) 40 | TARGET_DEVICE_ARGS="--target-device mac" 41 | ;; 42 | esac 43 | 44 | install_resource() 45 | { 46 | if [[ "$1" = /* ]] ; then 47 | RESOURCE_PATH="$1" 48 | else 49 | RESOURCE_PATH="${PODS_ROOT}/$1" 50 | fi 51 | if [[ ! -e "$RESOURCE_PATH" ]] ; then 52 | cat << EOM 53 | error: Resource "$RESOURCE_PATH" not found. Run 'pod install' to update the copy resources script. 54 | EOM 55 | exit 1 56 | fi 57 | case $RESOURCE_PATH in 58 | *.storyboard) 59 | 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}" || true 60 | 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} 61 | ;; 62 | *.xib) 63 | 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}" || true 64 | 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} 65 | ;; 66 | *.framework) 67 | echo "mkdir -p ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" || true 68 | mkdir -p "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 69 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" $RESOURCE_PATH ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" || true 70 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 71 | ;; 72 | *.xcdatamodel) 73 | echo "xcrun momc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH"`.mom\"" || true 74 | xcrun momc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodel`.mom" 75 | ;; 76 | *.xcdatamodeld) 77 | echo "xcrun momc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodeld`.momd\"" || true 78 | xcrun momc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodeld`.momd" 79 | ;; 80 | *.xcmappingmodel) 81 | echo "xcrun mapc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcmappingmodel`.cdm\"" || true 82 | xcrun mapc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcmappingmodel`.cdm" 83 | ;; 84 | *.xcassets) 85 | ABSOLUTE_XCASSET_FILE="$RESOURCE_PATH" 86 | XCASSET_FILES+=("$ABSOLUTE_XCASSET_FILE") 87 | ;; 88 | *) 89 | echo "$RESOURCE_PATH" || true 90 | echo "$RESOURCE_PATH" >> "$RESOURCES_TO_COPY" 91 | ;; 92 | esac 93 | } 94 | 95 | mkdir -p "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 96 | rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 97 | if [[ "${ACTION}" == "install" ]] && [[ "${SKIP_INSTALL}" == "NO" ]]; then 98 | mkdir -p "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 99 | rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 100 | fi 101 | rm -f "$RESOURCES_TO_COPY" 102 | 103 | if [[ -n "${WRAPPER_EXTENSION}" ]] && [ "`xcrun --find actool`" ] && [ -n "${XCASSET_FILES:-}" ] 104 | then 105 | # Find all other xcassets (this unfortunately includes those of path pods and other targets). 106 | OTHER_XCASSETS=$(find "$PWD" -iname "*.xcassets" -type d) 107 | while read line; do 108 | if [[ $line != "${PODS_ROOT}*" ]]; then 109 | XCASSET_FILES+=("$line") 110 | fi 111 | done <<<"$OTHER_XCASSETS" 112 | 113 | if [ -z ${ASSETCATALOG_COMPILER_APPICON_NAME+x} ]; then 114 | 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}" 115 | else 116 | 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}" --app-icon "${ASSETCATALOG_COMPILER_APPICON_NAME}" --output-partial-info-plist "${TARGET_TEMP_DIR}/assetcatalog_generated_info_cocoapods.plist" 117 | fi 118 | fi 119 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-KFImageViewer_Example/Pods-KFImageViewer_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_KFImageViewer_ExampleVersionNumber; 15 | FOUNDATION_EXPORT const unsigned char Pods_KFImageViewer_ExampleVersionString[]; 16 | 17 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-KFImageViewer_Example/Pods-KFImageViewer_Example.debug.xcconfig: -------------------------------------------------------------------------------- 1 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES 2 | FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/KFImageViewer" "${PODS_CONFIGURATION_BUILD_DIR}/Kingfisher" 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}/KFImageViewer/KFImageViewer.framework/Headers" -iquote "${PODS_CONFIGURATION_BUILD_DIR}/Kingfisher/Kingfisher.framework/Headers" 6 | OTHER_LDFLAGS = $(inherited) -framework "KFImageViewer" -framework "Kingfisher" 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_PODFILE_DIR_PATH = ${SRCROOT}/. 11 | PODS_ROOT = ${SRCROOT}/Pods 12 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-KFImageViewer_Example/Pods-KFImageViewer_Example.modulemap: -------------------------------------------------------------------------------- 1 | framework module Pods_KFImageViewer_Example { 2 | umbrella header "Pods-KFImageViewer_Example-umbrella.h" 3 | 4 | export * 5 | module * { export * } 6 | } 7 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-KFImageViewer_Example/Pods-KFImageViewer_Example.release.xcconfig: -------------------------------------------------------------------------------- 1 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES 2 | FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/KFImageViewer" "${PODS_CONFIGURATION_BUILD_DIR}/Kingfisher" 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}/KFImageViewer/KFImageViewer.framework/Headers" -iquote "${PODS_CONFIGURATION_BUILD_DIR}/Kingfisher/Kingfisher.framework/Headers" 6 | OTHER_LDFLAGS = $(inherited) -framework "KFImageViewer" -framework "Kingfisher" 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_PODFILE_DIR_PATH = ${SRCROOT}/. 11 | PODS_ROOT = ${SRCROOT}/Pods 12 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-KFImageViewer_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 | 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-KFImageViewer_Tests/Pods-KFImageViewer_Tests-acknowledgements.markdown: -------------------------------------------------------------------------------- 1 | # Acknowledgements 2 | This application makes use of the following third party libraries: 3 | Generated by CocoaPods - https://cocoapods.org 4 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-KFImageViewer_Tests/Pods-KFImageViewer_Tests-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 | Generated by CocoaPods - https://cocoapods.org 18 | Title 19 | 20 | Type 21 | PSGroupSpecifier 22 | 23 | 24 | StringsTable 25 | Acknowledgements 26 | Title 27 | Acknowledgements 28 | 29 | 30 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-KFImageViewer_Tests/Pods-KFImageViewer_Tests-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_Pods_KFImageViewer_Tests : NSObject 3 | @end 4 | @implementation PodsDummy_Pods_KFImageViewer_Tests 5 | @end 6 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-KFImageViewer_Tests/Pods-KFImageViewer_Tests-frameworks.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | set -u 4 | set -o pipefail 5 | 6 | if [ -z ${FRAMEWORKS_FOLDER_PATH+x} ]; then 7 | # If FRAMEWORKS_FOLDER_PATH is not set, then there's nowhere for us to copy 8 | # frameworks to, so exit 0 (signalling the script phase was successful). 9 | exit 0 10 | fi 11 | 12 | echo "mkdir -p ${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 13 | mkdir -p "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 14 | 15 | COCOAPODS_PARALLEL_CODE_SIGN="${COCOAPODS_PARALLEL_CODE_SIGN:-false}" 16 | SWIFT_STDLIB_PATH="${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" 17 | 18 | # Used as a return value for each invocation of `strip_invalid_archs` function. 19 | STRIP_BINARY_RETVAL=0 20 | 21 | # This protects against multiple targets copying the same framework dependency at the same time. The solution 22 | # was originally proposed here: https://lists.samba.org/archive/rsync/2008-February/020158.html 23 | RSYNC_PROTECT_TMP_FILES=(--filter "P .*.??????") 24 | 25 | # Copies and strips a vendored framework 26 | install_framework() 27 | { 28 | if [ -r "${BUILT_PRODUCTS_DIR}/$1" ]; then 29 | local source="${BUILT_PRODUCTS_DIR}/$1" 30 | elif [ -r "${BUILT_PRODUCTS_DIR}/$(basename "$1")" ]; then 31 | local source="${BUILT_PRODUCTS_DIR}/$(basename "$1")" 32 | elif [ -r "$1" ]; then 33 | local source="$1" 34 | fi 35 | 36 | local destination="${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 37 | 38 | if [ -L "${source}" ]; then 39 | echo "Symlinked..." 40 | source="$(readlink "${source}")" 41 | fi 42 | 43 | # Use filter instead of exclude so missing patterns don't throw errors. 44 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${destination}\"" 45 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${destination}" 46 | 47 | local basename 48 | basename="$(basename -s .framework "$1")" 49 | binary="${destination}/${basename}.framework/${basename}" 50 | if ! [ -r "$binary" ]; then 51 | binary="${destination}/${basename}" 52 | fi 53 | 54 | # Strip invalid architectures so "fat" simulator / device frameworks work on device 55 | if [[ "$(file "$binary")" == *"dynamically linked shared library"* ]]; then 56 | strip_invalid_archs "$binary" 57 | fi 58 | 59 | # Resign the code if required by the build settings to avoid unstable apps 60 | code_sign_if_enabled "${destination}/$(basename "$1")" 61 | 62 | # Embed linked Swift runtime libraries. No longer necessary as of Xcode 7. 63 | if [ "${XCODE_VERSION_MAJOR}" -lt 7 ]; then 64 | local swift_runtime_libs 65 | swift_runtime_libs=$(xcrun otool -LX "$binary" | grep --color=never @rpath/libswift | sed -E s/@rpath\\/\(.+dylib\).*/\\1/g | uniq -u && exit ${PIPESTATUS[0]}) 66 | for lib in $swift_runtime_libs; do 67 | echo "rsync -auv \"${SWIFT_STDLIB_PATH}/${lib}\" \"${destination}\"" 68 | rsync -auv "${SWIFT_STDLIB_PATH}/${lib}" "${destination}" 69 | code_sign_if_enabled "${destination}/${lib}" 70 | done 71 | fi 72 | } 73 | 74 | # Copies and strips a vendored dSYM 75 | install_dsym() { 76 | local source="$1" 77 | if [ -r "$source" ]; then 78 | # Copy the dSYM into a the targets temp dir. 79 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${DERIVED_FILES_DIR}\"" 80 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${DERIVED_FILES_DIR}" 81 | 82 | local basename 83 | basename="$(basename -s .framework.dSYM "$source")" 84 | binary="${DERIVED_FILES_DIR}/${basename}.framework.dSYM/Contents/Resources/DWARF/${basename}" 85 | 86 | # Strip invalid architectures so "fat" simulator / device frameworks work on device 87 | if [[ "$(file "$binary")" == *"Mach-O dSYM companion"* ]]; then 88 | strip_invalid_archs "$binary" 89 | fi 90 | 91 | if [[ $STRIP_BINARY_RETVAL == 1 ]]; then 92 | # Move the stripped file into its final destination. 93 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${DERIVED_FILES_DIR}/${basename}.framework.dSYM\" \"${DWARF_DSYM_FOLDER_PATH}\"" 94 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${DERIVED_FILES_DIR}/${basename}.framework.dSYM" "${DWARF_DSYM_FOLDER_PATH}" 95 | else 96 | # The dSYM was not stripped at all, in this case touch a fake folder so the input/output paths from Xcode do not reexecute this script because the file is missing. 97 | touch "${DWARF_DSYM_FOLDER_PATH}/${basename}.framework.dSYM" 98 | fi 99 | fi 100 | } 101 | 102 | # Signs a framework with the provided identity 103 | code_sign_if_enabled() { 104 | if [ -n "${EXPANDED_CODE_SIGN_IDENTITY}" -a "${CODE_SIGNING_REQUIRED:-}" != "NO" -a "${CODE_SIGNING_ALLOWED}" != "NO" ]; then 105 | # Use the current code_sign_identitiy 106 | echo "Code Signing $1 with Identity ${EXPANDED_CODE_SIGN_IDENTITY_NAME}" 107 | local code_sign_cmd="/usr/bin/codesign --force --sign ${EXPANDED_CODE_SIGN_IDENTITY} ${OTHER_CODE_SIGN_FLAGS:-} --preserve-metadata=identifier,entitlements '$1'" 108 | 109 | if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then 110 | code_sign_cmd="$code_sign_cmd &" 111 | fi 112 | echo "$code_sign_cmd" 113 | eval "$code_sign_cmd" 114 | fi 115 | } 116 | 117 | # Strip invalid architectures 118 | strip_invalid_archs() { 119 | binary="$1" 120 | # Get architectures for current target binary 121 | binary_archs="$(lipo -info "$binary" | rev | cut -d ':' -f1 | awk '{$1=$1;print}' | rev)" 122 | # Intersect them with the architectures we are building for 123 | intersected_archs="$(echo ${ARCHS[@]} ${binary_archs[@]} | tr ' ' '\n' | sort | uniq -d)" 124 | # If there are no archs supported by this binary then warn the user 125 | if [[ -z "$intersected_archs" ]]; then 126 | echo "warning: [CP] Vendored binary '$binary' contains architectures ($binary_archs) none of which match the current build architectures ($ARCHS)." 127 | STRIP_BINARY_RETVAL=0 128 | return 129 | fi 130 | stripped="" 131 | for arch in $binary_archs; do 132 | if ! [[ "${ARCHS}" == *"$arch"* ]]; then 133 | # Strip non-valid architectures in-place 134 | lipo -remove "$arch" -output "$binary" "$binary" || exit 1 135 | stripped="$stripped $arch" 136 | fi 137 | done 138 | if [[ "$stripped" ]]; then 139 | echo "Stripped $binary of architectures:$stripped" 140 | fi 141 | STRIP_BINARY_RETVAL=1 142 | } 143 | 144 | if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then 145 | wait 146 | fi 147 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-KFImageViewer_Tests/Pods-KFImageViewer_Tests-resources.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | set -u 4 | set -o pipefail 5 | 6 | if [ -z ${UNLOCALIZED_RESOURCES_FOLDER_PATH+x} ]; then 7 | # If UNLOCALIZED_RESOURCES_FOLDER_PATH is not set, then there's nowhere for us to copy 8 | # resources to, so exit 0 (signalling the script phase was successful). 9 | exit 0 10 | fi 11 | 12 | mkdir -p "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 13 | 14 | RESOURCES_TO_COPY=${PODS_ROOT}/resources-to-copy-${TARGETNAME}.txt 15 | > "$RESOURCES_TO_COPY" 16 | 17 | XCASSET_FILES=() 18 | 19 | # This protects against multiple targets copying the same framework dependency at the same time. The solution 20 | # was originally proposed here: https://lists.samba.org/archive/rsync/2008-February/020158.html 21 | RSYNC_PROTECT_TMP_FILES=(--filter "P .*.??????") 22 | 23 | case "${TARGETED_DEVICE_FAMILY:-}" in 24 | 1,2) 25 | TARGET_DEVICE_ARGS="--target-device ipad --target-device iphone" 26 | ;; 27 | 1) 28 | TARGET_DEVICE_ARGS="--target-device iphone" 29 | ;; 30 | 2) 31 | TARGET_DEVICE_ARGS="--target-device ipad" 32 | ;; 33 | 3) 34 | TARGET_DEVICE_ARGS="--target-device tv" 35 | ;; 36 | 4) 37 | TARGET_DEVICE_ARGS="--target-device watch" 38 | ;; 39 | *) 40 | TARGET_DEVICE_ARGS="--target-device mac" 41 | ;; 42 | esac 43 | 44 | install_resource() 45 | { 46 | if [[ "$1" = /* ]] ; then 47 | RESOURCE_PATH="$1" 48 | else 49 | RESOURCE_PATH="${PODS_ROOT}/$1" 50 | fi 51 | if [[ ! -e "$RESOURCE_PATH" ]] ; then 52 | cat << EOM 53 | error: Resource "$RESOURCE_PATH" not found. Run 'pod install' to update the copy resources script. 54 | EOM 55 | exit 1 56 | fi 57 | case $RESOURCE_PATH in 58 | *.storyboard) 59 | 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}" || true 60 | 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} 61 | ;; 62 | *.xib) 63 | 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}" || true 64 | 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} 65 | ;; 66 | *.framework) 67 | echo "mkdir -p ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" || true 68 | mkdir -p "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 69 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" $RESOURCE_PATH ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" || true 70 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 71 | ;; 72 | *.xcdatamodel) 73 | echo "xcrun momc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH"`.mom\"" || true 74 | xcrun momc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodel`.mom" 75 | ;; 76 | *.xcdatamodeld) 77 | echo "xcrun momc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodeld`.momd\"" || true 78 | xcrun momc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodeld`.momd" 79 | ;; 80 | *.xcmappingmodel) 81 | echo "xcrun mapc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcmappingmodel`.cdm\"" || true 82 | xcrun mapc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcmappingmodel`.cdm" 83 | ;; 84 | *.xcassets) 85 | ABSOLUTE_XCASSET_FILE="$RESOURCE_PATH" 86 | XCASSET_FILES+=("$ABSOLUTE_XCASSET_FILE") 87 | ;; 88 | *) 89 | echo "$RESOURCE_PATH" || true 90 | echo "$RESOURCE_PATH" >> "$RESOURCES_TO_COPY" 91 | ;; 92 | esac 93 | } 94 | 95 | mkdir -p "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 96 | rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 97 | if [[ "${ACTION}" == "install" ]] && [[ "${SKIP_INSTALL}" == "NO" ]]; then 98 | mkdir -p "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 99 | rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 100 | fi 101 | rm -f "$RESOURCES_TO_COPY" 102 | 103 | if [[ -n "${WRAPPER_EXTENSION}" ]] && [ "`xcrun --find actool`" ] && [ -n "${XCASSET_FILES:-}" ] 104 | then 105 | # Find all other xcassets (this unfortunately includes those of path pods and other targets). 106 | OTHER_XCASSETS=$(find "$PWD" -iname "*.xcassets" -type d) 107 | while read line; do 108 | if [[ $line != "${PODS_ROOT}*" ]]; then 109 | XCASSET_FILES+=("$line") 110 | fi 111 | done <<<"$OTHER_XCASSETS" 112 | 113 | if [ -z ${ASSETCATALOG_COMPILER_APPICON_NAME+x} ]; then 114 | 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}" 115 | else 116 | 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}" --app-icon "${ASSETCATALOG_COMPILER_APPICON_NAME}" --output-partial-info-plist "${TARGET_TEMP_DIR}/assetcatalog_generated_info_cocoapods.plist" 117 | fi 118 | fi 119 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-KFImageViewer_Tests/Pods-KFImageViewer_Tests-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_KFImageViewer_TestsVersionNumber; 15 | FOUNDATION_EXPORT const unsigned char Pods_KFImageViewer_TestsVersionString[]; 16 | 17 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-KFImageViewer_Tests/Pods-KFImageViewer_Tests.debug.xcconfig: -------------------------------------------------------------------------------- 1 | FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/KFImageViewer" "${PODS_CONFIGURATION_BUILD_DIR}/Kingfisher" 2 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 3 | LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks' 4 | OTHER_CFLAGS = $(inherited) -iquote "${PODS_CONFIGURATION_BUILD_DIR}/KFImageViewer/KFImageViewer.framework/Headers" -iquote "${PODS_CONFIGURATION_BUILD_DIR}/Kingfisher/Kingfisher.framework/Headers" 5 | PODS_BUILD_DIR = ${BUILD_DIR} 6 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 7 | PODS_PODFILE_DIR_PATH = ${SRCROOT}/. 8 | PODS_ROOT = ${SRCROOT}/Pods 9 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-KFImageViewer_Tests/Pods-KFImageViewer_Tests.modulemap: -------------------------------------------------------------------------------- 1 | framework module Pods_KFImageViewer_Tests { 2 | umbrella header "Pods-KFImageViewer_Tests-umbrella.h" 3 | 4 | export * 5 | module * { export * } 6 | } 7 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-KFImageViewer_Tests/Pods-KFImageViewer_Tests.release.xcconfig: -------------------------------------------------------------------------------- 1 | FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/KFImageViewer" "${PODS_CONFIGURATION_BUILD_DIR}/Kingfisher" 2 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 3 | LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks' 4 | OTHER_CFLAGS = $(inherited) -iquote "${PODS_CONFIGURATION_BUILD_DIR}/KFImageViewer/KFImageViewer.framework/Headers" -iquote "${PODS_CONFIGURATION_BUILD_DIR}/Kingfisher/Kingfisher.framework/Headers" 5 | PODS_BUILD_DIR = ${BUILD_DIR} 6 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 7 | PODS_PODFILE_DIR_PATH = ${SRCROOT}/. 8 | PODS_ROOT = ${SRCROOT}/Pods 9 | -------------------------------------------------------------------------------- /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 | import KFImageViewer 3 | 4 | class Tests: XCTestCase { 5 | 6 | override func setUp() { 7 | super.setUp() 8 | // Put setup code here. This method is called before the invocation of each test method in the class. 9 | } 10 | 11 | override func tearDown() { 12 | // Put teardown code here. This method is called after the invocation of each test method in the class. 13 | super.tearDown() 14 | } 15 | 16 | func testExample() { 17 | // This is an example of a functional test case. 18 | XCTAssert(true, "Pass") 19 | } 20 | 21 | func testPerformanceExample() { 22 | // This is an example of a performance test case. 23 | self.measure() { 24 | // Put the code you want to measure the time of here. 25 | } 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /KFImageViewer.podspec: -------------------------------------------------------------------------------- 1 | # 2 | # Be sure to run `pod lib lint KFImageViewer.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 https://guides.cocoapods.org/syntax/podspec.html 7 | # 8 | 9 | Pod::Spec.new do |s| 10 | s.name = 'KFImageViewer' 11 | s.version = '1.0.0' 12 | s.summary = 'KFImageViewer written in Swift with download progress, circular scrolling, timer and full screen viewer' 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 | KFImageViewer is a Kingfisher supported Swift library providing customizable image viewer with download progress, circular scrolling, timer and full screen viewer and extendable image source. 22 | DESC 23 | 24 | s.homepage = 'https://github.com/faisalazeez/KFImageViewer' 25 | s.swift_version = '4.0' 26 | # s.screenshots = 'www.example.com/screenshots_1', 'www.example.com/screenshots_2' 27 | s.license = { :type => 'MIT', :file => 'LICENSE' } 28 | s.author = { 'faisalazeez' => 'faisalazeez7@gmail.com' } 29 | s.source = { :git => 'https://github.com/faisalazeez/KFImageViewer.git', :tag => s.version.to_s } 30 | # s.social_media_url = 'https://twitter.com/' 31 | 32 | s.ios.deployment_target = '11.0' 33 | 34 | s.requires_arc = true 35 | 36 | s.subspec 'Core' do |core| 37 | core.source_files = 'Core/**/*' 38 | core.resources = 'Core/Resources/*.png' 39 | end 40 | 41 | s.subspec 'Kingfisher' do |subspec| 42 | subspec.dependency 'KFImageViewer/Core' 43 | subspec.dependency 'Kingfisher', '> 5.2' 44 | subspec.source_files = 'Kingfisher/KingfisherSource.swift' 45 | end 46 | 47 | s.default_subspec = 'Core' 48 | end 49 | -------------------------------------------------------------------------------- /Kingfisher/KingfisherSource.swift: -------------------------------------------------------------------------------- 1 | // 2 | // KingfisherSource.swift 3 | // KFImageViewer 4 | // 5 | // Created by feiin 6 | // 7 | // 8 | 9 | import Kingfisher 10 | 11 | /// Input Source to image using Kingfisher 12 | public class KingfisherSource: NSObject, InputSource { 13 | /// url to load 14 | public var url: URL 15 | 16 | /// placeholder used before image is loaded 17 | public var placeholder: UIImage? 18 | 19 | /// options for displaying, ie. [.transition(.fade(0.2))] 20 | public var options: KingfisherOptionsInfo? 21 | 22 | /// Initializes a new source with a URL 23 | /// - parameter url: a url to be loaded 24 | /// - parameter placeholder: a placeholder used before image is loaded 25 | /// - parameter options: options for displaying 26 | public init(url: URL, placeholder: UIImage? = nil, options: KingfisherOptionsInfo? = nil) { 27 | self.url = url 28 | self.placeholder = placeholder 29 | self.options = options 30 | super.init() 31 | } 32 | 33 | /// Initializes a new source with a URL string 34 | /// - parameter urlString: a string url to load 35 | /// - parameter placeholder: a placeholder used before image is loaded 36 | /// - parameter options: options for displaying 37 | public init?(urlString: String, placeholder: UIImage? = nil, options: KingfisherOptionsInfo? = nil) { 38 | if let validUrl = URL(string: urlString) { 39 | self.url = validUrl 40 | self.placeholder = placeholder 41 | self.options = options 42 | super.init() 43 | } else { 44 | return nil 45 | } 46 | } 47 | 48 | @objc public func load(to imageView: UIImageView, with callback: @escaping (UIImage?) -> Void) { 49 | imageView.kf.setImage(with: self.url, placeholder: self.placeholder, options: self.options, progressBlock: nil) { (image, _, _, _) in 50 | callback(image) 51 | } 52 | } 53 | 54 | public func loadWithProgress(to imageView: UIImageView, with callback: @escaping (UIImage?) -> Void, progress: @escaping (CGFloat) -> Void) { 55 | imageView.kf.setImage(with: self.url, placeholder: self.placeholder, options: self.options, progressBlock:{ 56 | (recieved,total) 57 | in 58 | progress((CGFloat(recieved)/CGFloat(total))) 59 | }) { (image, _, _, _) in 60 | callback(image) 61 | } 62 | } 63 | 64 | public func cancelLoad(on imageView: UIImageView) { 65 | imageView.kf.cancelDownloadTask() 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2018 faisalazeez 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 | -------------------------------------------------------------------------------- /Podfile: -------------------------------------------------------------------------------- 1 | # Uncomment the next line to define a global platform for your project 2 | # platform :ios, '9.0' 3 | 4 | target 'KFImageViewer' do 5 | # Comment the next line if you're not using Swift and don't want to use dynamic frameworks 6 | use_frameworks! 7 | 8 | # Pods for KFImageViewer 9 | 10 | end 11 | 12 | target 'Pods-KFImageViewer_Example' do 13 | # Comment the next line if you're not using Swift and don't want to use dynamic frameworks 14 | use_frameworks! 15 | 16 | # Pods for Pods-KFImageViewer_Example 17 | 18 | end 19 | 20 | target 'Pods-KFImageViewer_Tests' do 21 | # Uncomment the next line if you're using Swift or would like to use dynamic frameworks 22 | # use_frameworks! 23 | 24 | # Pods for Pods-KFImageViewer_Tests 25 | 26 | end 27 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 🖼 KFImageViewer 2 | 3 | **Customizable Kingfisher dependent Swift image slideshow with download progress, circular scrolling, timer and full screen viewer** 4 | 5 | ![](https://raw.githubusercontent.com/faisalazeez/KFImageViewer/master/Example/KFImageViewer/KFImageViewer_02.gif) 6 | ![](https://raw.githubusercontent.com/faisalazeez/KFImageViewer/master/Example/KFImageViewer/KFImageViewer.gif) 7 | 8 | ## 📱 Example 9 | 10 | To run the example project, clone the repo, and run `pod install` from the Example directory first. 11 | 12 | ## 🔧 Installation 13 | 14 | ### CocoaPods 15 | KFImageViewer is available through [CocoaPods](http://cocoapods.org). To install 16 | it, simply add the following line to your Podfile: 17 | 18 | ```ruby 19 | pod 'KFImageViewer', '~> 1.0.0' 20 | ``` 21 | 22 | ### Manually 23 | 24 | Alternatively can also grab the whole `KFImageViewer` directory and copy it to your project. 25 | 26 | ## 🔨 How to use 27 | 28 | Add KFImageViewer view to your view hiearchy either in Interface Builder or in code. 29 | 30 | ### Loading images 31 | 32 | Set images by using ```setImageInputs``` method on ```KFImageViewer``` instance with an array of *InputSource*s. You can also create your own input source by implementing ```InputSource``` protocol. 33 | 34 | | Library | InputSource name | Pod | 35 | | ------------------------------------------------------------- |:----------------:| ---------------------------------:| 36 | | [Kingfisher](https://github.com/onevcat/Kingfisher) | KingfisherSource | `pod "KFImageViewer/Kingfisher"` | 37 | 38 | 39 | ```swift 40 | slideshow.setImageInputs([ 41 | KingfisherSource(urlString: "https://images.unsplash.com/photo-1432679963831-2dab49187847?w=1080"), 42 | ]) 43 | ``` 44 | 45 | ### Configuration 46 | 47 | Behaviour is configurable by those properties: 48 | 49 | - ```slideshowInterval``` - slideshow interval in seconds (default `0` – disabled) 50 | - ```zoomEnabled``` - enables zooming (default `false`) 51 | - ```circular``` - enables circular scrolling (default `true`) 52 | - ```activityIndicator``` – allows to set custom activity indicator, see *Activity indicator* section 53 | - ```pageIndicator``` – allows to set custom page indicator, see *Page indicator* section; assign `nil` to hide page indicator 54 | - ```pageIndicatorPosition``` - configures position of the page indicator 55 | - ```contentScaleMode``` - configures the scaling (default `ScaleAspectFit`) 56 | - ```draggingEnabled``` - enables dragging (default `true`) 57 | - ```currentPageChanged``` - closure called on page change 58 | - ```willBeginDragging``` - closure called on scrollViewWillBeginDragging 59 | - ```didEndDecelerating``` - closure called on scrollViewDidEndDecelerating 60 | - ```preload``` - image preloading configuration (default `all` preloading, also `fixed`) 61 | 62 | ### Page Indicator 63 | 64 | Page indicator can be customized using the `pageIndicator` property on KFImageViewer. By defualt, a plain UIPageControl is used. If needed, page control can be customized: 65 | 66 | ```swift 67 | let pageIndicator = UIPageControl() 68 | pageIndicator.currentPageIndicatorTintColor = UIColor.lightGray 69 | pageIndicator.pageIndicatorTintColor = UIColor.black 70 | slideshow.pageIndicator = pageIndicator 71 | ``` 72 | 73 | Also, a simple label page indicator that shows pages in style "5/21" (fifth page from twenty one) is provided: 74 | 75 | ```swift 76 | slideshow.pageIndicator = LabelPageIndicator() 77 | ``` 78 | 79 | You can also use your own page indicator by adopting the `PageIndicatorView` protocol. 80 | 81 | Position of the page indicator can be configured by assigning a `PageIndicatorPosition` value to the `pageIndicatorPosition` property on KFImageViewer. You may specify the horizontal and vertical positioning separately. 82 | 83 | **Horizontal** positioning options are: `.left(padding: Int)`, `.center`, `.right(padding: Int)` 84 | 85 | **Vertical** positioning options are: `.top`, `.bottom`, `.under`, `customTop(padding: Int)`, `customBottom(padding: Int)`, `customUnder(padding: Int)` 86 | 87 | Example: 88 | ```swift 89 | slideshow.pageIndicatorPosition = PageIndicatorPosition(horizontal: .left(padding: 20), vertical: .bottom) 90 | ``` 91 | 92 | 93 | ### Activity Indicator 94 | 95 | By default activity indicator is not shown, but you can enable it by setting `DefaultActivityIndicator` instance to KFImageViewer : 96 | 97 | ```swift 98 | slideshow.activityIndicator = DefaultActivityIndicator() 99 | ``` 100 | 101 | You can customize style and color of the indicator: 102 | 103 | ```swift 104 | slideshow.activityIndicator = DefaultActivityIndicator(style: .white, color: nil) 105 | ``` 106 | 107 | There's also an option to use your own activity indicator. You just need to implement `ActivityIndicatorView` and `ActivityIndicatorFactory` protocols. See `ActivityIndicator.swift` for more information. 108 | 109 | ### Full Screen view 110 | 111 | There is also a possibility to open full-screen image view using attached `FullScreenSlideshowViewController`. The simplest way is to call: 112 | 113 | ```swift 114 | override func viewDidLoad() { 115 | let gestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(ViewController.didTap)) 116 | slideshow.addGestureRecognizer(gestureRecognizer) 117 | } 118 | 119 | func didTap() { 120 | slideshow.presentFullScreenController(from: self) 121 | } 122 | ``` 123 | 124 | `FullScreenSlideshowViewController` can also be instantiated and configured manually if more advanced behavior is needed. 125 | 126 | ## 👤 Author 127 | 128 | Faisal Azeez faisalazeez7@gmail.com 129 | 130 | ## 📄 License 131 | 132 | KFImageViewer is available under the MIT license. See the LICENSE file for more info. 133 | -------------------------------------------------------------------------------- /_Pods.xcodeproj: -------------------------------------------------------------------------------- 1 | Example/Pods/Pods.xcodeproj --------------------------------------------------------------------------------