5 // Created by João D. Moreira on 30/08/16.
7 // Copyright (c) 2018 Wei Wang <onevcat@gmail.com>
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:
16 // The above copyright notice and this permission notice shall be included in
17 // all copies or substantial portions of the Software.
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
34 public typealias IndicatorView = NSView
36 public typealias IndicatorView = UIView
39 public enum IndicatorType {
42 /// Use system activity indicator.
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)
50 // MARK: - Indicator Protocol
51 public protocol Indicator {
52 func startAnimatingView()
53 func stopAnimatingView()
55 var viewCenter: CGPoint { get set }
56 var view: IndicatorView { get }
61 public var viewCenter: CGPoint {
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 )
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)
76 public var viewCenter: CGPoint {
81 view.center = newValue
87 // MARK: - ActivityIndicator
88 // Displays a NSProgressIndicator / UIActivityIndicatorView
89 final class ActivityIndicator: Indicator {
92 private let activityIndicatorView: NSProgressIndicator
94 private let activityIndicatorView: UIActivityIndicatorView
96 private var animatingCount = 0
98 var view: IndicatorView {
99 return activityIndicatorView
102 func startAnimatingView() {
105 if animatingCount == 1 {
107 activityIndicatorView.startAnimation(nil)
109 activityIndicatorView.startAnimating()
111 activityIndicatorView.isHidden = false
115 func stopAnimatingView() {
116 animatingCount = max(animatingCount - 1, 0)
117 if animatingCount == 0 {
119 activityIndicatorView.stopAnimation(nil)
121 activityIndicatorView.stopAnimating()
123 activityIndicatorView.isHidden = true
129 activityIndicatorView = NSProgressIndicator(frame: CGRect(x: 0, y: 0, width: 16, height: 16))
130 activityIndicatorView.controlSize = .small
131 activityIndicatorView.style = .spinning
134 let indicatorStyle = UIActivityIndicatorViewStyle.white
136 let indicatorStyle = UIActivityIndicatorViewStyle.gray
138 activityIndicatorView = UIActivityIndicatorView(activityIndicatorStyle:indicatorStyle)
139 activityIndicatorView.autoresizingMask = [.flexibleLeftMargin, .flexibleRightMargin, .flexibleBottomMargin, .flexibleTopMargin]
144 // MARK: - ImageIndicator
145 // Displays an ImageView. Supports gif
146 final class ImageIndicator: Indicator {
147 private let animatedImageIndicatorView: ImageView
149 var view: IndicatorView {
150 return animatedImageIndicatorView
153 init?(imageData data: Data, processor: ImageProcessor = DefaultImageProcessor.default, options: KingfisherOptionsInfo = KingfisherEmptyOptionsInfo) {
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)
161 guard let image = processor.process(item: .data(data), options: options) else {
165 animatedImageIndicatorView = ImageView()
166 animatedImageIndicatorView.image = image
167 animatedImageIndicatorView.frame = CGRect(x: 0, y: 0, width: image.size.width, height: image.size.height)
170 // Need for gif to animate on macOS
171 self.animatedImageIndicatorView.imageScaling = .scaleNone
172 self.animatedImageIndicatorView.canDrawSubviewsIntoLayer = true
174 animatedImageIndicatorView.contentMode = .center
175 animatedImageIndicatorView.autoresizingMask = [.flexibleLeftMargin,
176 .flexibleRightMargin,
177 .flexibleBottomMargin,
182 func startAnimatingView() {
184 animatedImageIndicatorView.animates = true
186 animatedImageIndicatorView.startAnimating()
188 animatedImageIndicatorView.isHidden = false
191 func stopAnimatingView() {
193 animatedImageIndicatorView.animates = false
195 animatedImageIndicatorView.stopAnimating()
197 animatedImageIndicatorView.isHidden = true