Protocol indicator

This commit is contained in:
onevcat
2016-09-13 16:56:03 +09:00
parent 014ed4663e
commit 1869184c7b
6 changed files with 116 additions and 129 deletions

View File

@@ -62,12 +62,10 @@ extension ViewController {
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "collectionViewCell", for: indexPath) as! CollectionViewCell
cell.cellImageView.kf_showIndicatorWhenLoading = true
cell.cellImageView.kf_indicatorType = .activity
let url = URL(string: "https://raw.githubusercontent.com/onevcat/Kingfisher/master/images/kingfisher-\(indexPath.row + 1).jpg")!
_ = cell.cellImageView.kf_setImage(with: url,
placeholder: nil,
options: [.transition(ImageTransition.fade(1))],

View File

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 10 KiB

View File

@@ -57,7 +57,7 @@ extension ViewController: NSCollectionViewDataSource {
let url = URL(string: "https://raw.githubusercontent.com/onevcat/Kingfisher/master/images/kingfisher-\(indexPath.item + 1).jpg")!
item.imageView?.kf_showIndicatorWhenLoading = true
item.imageView?.kf_indicatorType = .activity
item.imageView?.kf_setImage(with: url, placeholder: nil, options: nil,
progressBlock: { receivedSize, totalSize in
print("\(indexPath.item + 1): \(receivedSize)/\(totalSize)")

View File

@@ -19,9 +19,18 @@
4B3766841C478F940001443F /* Kingfisher.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D13F49D61BEDA67C00CE335D /* Kingfisher.framework */; };
4B3766A01C4794460001443F /* CFNetwork.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4B37669F1C4794460001443F /* CFNetwork.framework */; };
4B3766A21C47944D0001443F /* CFNetwork.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4B3766A11C47944D0001443F /* CFNetwork.framework */; };
4B4307A51D87E6A700ED2DA9 /* loader.gif in Resources */ = {isa = PBXBuildFile; fileRef = 4B7742461D87E42E0077024E /* loader.gif */; };
4B6313F41D766BEF0078E017 /* Filter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B6313F31D766BEF0078E017 /* Filter.swift */; };
4B6313F51D766BEF0078E017 /* Filter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B6313F31D766BEF0078E017 /* Filter.swift */; };
4B6313F61D766BEF0078E017 /* Filter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B6313F31D766BEF0078E017 /* Filter.swift */; };
4B77423F1D87E08A0077024E /* Indicator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B77423E1D87E08A0077024E /* Indicator.swift */; };
4B7742401D87E08A0077024E /* Indicator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B77423E1D87E08A0077024E /* Indicator.swift */; };
4B7742411D87E08A0077024E /* Indicator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B77423E1D87E08A0077024E /* Indicator.swift */; };
4B7742431D87E2AA0077024E /* Box.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B7742421D87E2AA0077024E /* Box.swift */; };
4B7742441D87E2AA0077024E /* Box.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B7742421D87E2AA0077024E /* Box.swift */; };
4B7742451D87E2AA0077024E /* Box.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B7742421D87E2AA0077024E /* Box.swift */; };
4B7742471D87E42E0077024E /* loader.gif in Resources */ = {isa = PBXBuildFile; fileRef = 4B7742461D87E42E0077024E /* loader.gif */; };
4B7742481D87E42E0077024E /* loader.gif in Resources */ = {isa = PBXBuildFile; fileRef = 4B7742461D87E42E0077024E /* loader.gif */; };
4B98674F1CD1CF42003ADAC7 /* AnimatedImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B98674E1CD1CF42003ADAC7 /* AnimatedImageView.swift */; };
4B9867501CD1CF42003ADAC7 /* AnimatedImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B98674E1CD1CF42003ADAC7 /* AnimatedImageView.swift */; };
4BB24C3D1D79215A00CD5F9C /* CacheSerializer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BB24C3C1D79215A00CD5F9C /* CacheSerializer.swift */; };
@@ -475,6 +484,9 @@
4B3766A11C47944D0001443F /* CFNetwork.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CFNetwork.framework; path = Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.11.sdk/System/Library/Frameworks/CFNetwork.framework; sourceTree = DEVELOPER_DIR; };
4B3E714D1B01FEB200F5AAED /* WatchKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = WatchKit.framework; path = System/Library/Frameworks/WatchKit.framework; sourceTree = SDKROOT; };
4B6313F31D766BEF0078E017 /* Filter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Filter.swift; path = Sources/Filter.swift; sourceTree = "<group>"; };
4B77423E1D87E08A0077024E /* Indicator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Indicator.swift; path = Sources/Indicator.swift; sourceTree = "<group>"; };
4B7742421D87E2AA0077024E /* Box.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Box.swift; path = Sources/Box.swift; sourceTree = "<group>"; };
4B7742461D87E42E0077024E /* loader.gif */ = {isa = PBXFileReference; lastKnownFileType = image.gif; path = loader.gif; sourceTree = "<group>"; };
4B98674E1CD1CF42003ADAC7 /* AnimatedImageView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = AnimatedImageView.swift; path = Sources/AnimatedImageView.swift; sourceTree = "<group>"; };
4BB24C3C1D79215A00CD5F9C /* CacheSerializer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = CacheSerializer.swift; path = Sources/CacheSerializer.swift; sourceTree = "<group>"; };
4BCCF3361D5B02F8003387C2 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
@@ -871,6 +883,7 @@
isa = PBXGroup;
children = (
D10945EA1C526B6C001408EB /* Image.swift */,
4B77423E1D87E08A0077024E /* Indicator.swift */,
D10945EB1C526B6C001408EB /* ImageCache.swift */,
D10945EC1C526B6C001408EB /* ImageDownloader.swift */,
D9638B9F1C7DBA660046523D /* ImagePrefetcher.swift */,
@@ -907,6 +920,7 @@
4BF39F3F1D796BDD0035ECC8 /* Helpers */ = {
isa = PBXGroup;
children = (
4B7742421D87E2AA0077024E /* Box.swift */,
D10945F41C526B6C001408EB /* String+MD5.swift */,
D10945F51C526B6C001408EB /* ThreadHelper.swift */,
);
@@ -1017,6 +1031,7 @@
D12E0C8B1C47F91800AC98AD /* Kingfisher-Demo */ = {
isa = PBXGroup;
children = (
4B7742461D87E42E0077024E /* loader.gif */,
D12E0C8C1C47F91800AC98AD /* AppDelegate.swift */,
D12E0C8D1C47F91800AC98AD /* LaunchScreen.xib */,
D12E0C8F1C47F91800AC98AD /* Main.storyboard */,
@@ -1479,6 +1494,7 @@
files = (
4BCCF33E1D5B02F8003387C2 /* Assets.xcassets in Resources */,
4BCCF3401D5B02F8003387C2 /* Cell.xib in Resources */,
4B4307A51D87E6A700ED2DA9 /* loader.gif in Resources */,
4BCCF33F1D5B02F8003387C2 /* Main.storyboard in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
@@ -1631,6 +1647,7 @@
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
4B7742481D87E42E0077024E /* loader.gif in Resources */,
D12E0CA31C47F92200AC98AD /* Assets.xcassets in Resources */,
D12E0CA41C47F92200AC98AD /* Main.storyboard in Resources */,
);
@@ -1672,6 +1689,7 @@
buildActionMask = 2147483647;
files = (
D12E0C991C47F91800AC98AD /* Images.xcassets in Resources */,
4B7742471D87E42E0077024E /* loader.gif in Resources */,
D12E0C961C47F91800AC98AD /* LaunchScreen.xib in Resources */,
D12E0C971C47F91800AC98AD /* Main.storyboard in Resources */,
);
@@ -2042,8 +2060,10 @@
D109461D1C526C61001408EB /* ImageTransition.swift in Sources */,
4B2B8E4C1D70141000FC4749 /* ImageProcessor.swift in Sources */,
4BFBEE7F1D7D0C3600699FD3 /* RequrstModifier.swift in Sources */,
4B7742411D87E08A0077024E /* Indicator.swift in Sources */,
D109461E1C526C61001408EB /* ImageView+Kingfisher.swift in Sources */,
D109461F1C526C61001408EB /* KingfisherManager.swift in Sources */,
4B7742451D87E2AA0077024E /* Box.swift in Sources */,
4B6313F61D766BEF0078E017 /* Filter.swift in Sources */,
182FFF781CC9ACBA004B728D /* NSButton+Kingfisher.swift in Sources */,
D10946201C526C61001408EB /* KingfisherOptionsInfo.swift in Sources */,
@@ -2116,11 +2136,13 @@
files = (
D109460E1C526C0D001408EB /* Image.swift in Sources */,
4BFBEE7E1D7D0C3600699FD3 /* RequrstModifier.swift in Sources */,
4B7742441D87E2AA0077024E /* Box.swift in Sources */,
4B6313F51D766BEF0078E017 /* Filter.swift in Sources */,
4B9867501CD1CF42003ADAC7 /* AnimatedImageView.swift in Sources */,
D109460F1C526C0D001408EB /* ImageCache.swift in Sources */,
D10946101C526C0D001408EB /* ImageDownloader.swift in Sources */,
4B2B8E4B1D70140F00FC4749 /* ImageProcessor.swift in Sources */,
4B7742401D87E08A0077024E /* Indicator.swift in Sources */,
D10946111C526C0D001408EB /* ImageTransition.swift in Sources */,
D10946121C526C0D001408EB /* ImageView+Kingfisher.swift in Sources */,
D10946131C526C0D001408EB /* KingfisherManager.swift in Sources */,
@@ -2179,11 +2201,13 @@
files = (
D10945F71C526B86001408EB /* Image.swift in Sources */,
4BFBEE7D1D7D0C3600699FD3 /* RequrstModifier.swift in Sources */,
4B7742431D87E2AA0077024E /* Box.swift in Sources */,
4B6313F41D766BEF0078E017 /* Filter.swift in Sources */,
4B98674F1CD1CF42003ADAC7 /* AnimatedImageView.swift in Sources */,
D10945F81C526B86001408EB /* ImageCache.swift in Sources */,
D10945F91C526B86001408EB /* ImageDownloader.swift in Sources */,
4B2B8E4A1D70128200FC4749 /* ImageProcessor.swift in Sources */,
4B77423F1D87E08A0077024E /* Indicator.swift in Sources */,
D10945FA1C526B86001408EB /* ImageTransition.swift in Sources */,
D10945FB1C526B86001408EB /* ImageView+Kingfisher.swift in Sources */,
D10945FC1C526B86001408EB /* KingfisherManager.swift in Sources */,

View File

@@ -28,11 +28,9 @@
#if os(macOS)
import AppKit
typealias ImageView = NSImageView
public typealias IndicatorView = NSProgressIndicator
#else
import UIKit
typealias ImageView = UIImageView
public typealias IndicatorView = UIActivityIndicatorView
#endif
// MARK: - Set Images
@@ -69,13 +67,8 @@ extension ImageView {
return .empty
}
let showIndicatorWhenLoading = kf_showIndicatorWhenLoading
var indicator: IndicatorView? = nil
if showIndicatorWhenLoading {
indicator = kf_indicator
indicator?.isHidden = false
indicator?.kf_startAnimating()
}
let maybeIndicator = kf_indicator
maybeIndicator?.startAnimatingView()
kf_setWebURL(resource.downloadURL)
@@ -100,7 +93,7 @@ extension ImageView {
sSelf.kf_setImageTask(nil)
guard let image = image else {
indicator?.kf_stopAnimating()
maybeIndicator?.stopAnimatingView()
completionHandler?(nil, error, cacheType, imageURL)
return
}
@@ -108,7 +101,7 @@ extension ImageView {
guard let transitionItem = options.kf_firstMatchIgnoringAssociatedValue(.transition(.none)),
case .transition(let transition) = transitionItem, ( options.forceTransition || cacheType == .none) else
{
indicator?.kf_stopAnimating()
maybeIndicator?.stopAnimatingView()
sSelf.image = image
completionHandler?(image, error, cacheType, imageURL)
return
@@ -116,7 +109,7 @@ extension ImageView {
#if !os(macOS)
UIView.transition(with: sSelf, duration: 0.0, options: [],
animations: { indicator?.kf_stopAnimating() },
animations: { maybeIndicator?.stopAnimatingView() },
completion: { _ in
UIView.transition(with: sSelf, duration: transition.duration,
options: [transition.animationOptions, .allowUserInteraction],
@@ -156,10 +149,26 @@ extension ImageView {
}
}
/**
Enum for the types of indicators that the user can choose from.
*/
extension ImageView {
public enum IndicatorType {
/// No indicator.
case none
/// Use system activity indicator.
case activity
/// Use an image as indicator. GIF is supported.
case image(imageData: Data)
/// Use a custom indicator, which conforms to the `Indicator` protocol.
case custom(indicator: Indicator)
}
}
// MARK: - Associated Object
private var lastURLKey: Void?
private var indicatorKey: Void?
private var showIndicatorWhenLoadingKey: Void?
private var indicatorTypeKey: Void?
private var imageTaskKey: Void?
extension ImageView {
@@ -172,61 +181,55 @@ extension ImageView {
objc_setAssociatedObject(self, &lastURLKey, url, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
}
/// Whether show an animating indicator when the image view is loading an image or not.
/// Default is false.
public var kf_showIndicatorWhenLoading: Bool {
/// Holds which indicator type is going to be used.
/// Default is .None
public var kf_indicatorType: IndicatorType {
get {
if let result = objc_getAssociatedObject(self, &showIndicatorWhenLoadingKey) as? NSNumber {
return result.boolValue
} else {
return false
}
let indicator = (objc_getAssociatedObject(self, &indicatorTypeKey) as? Box<IndicatorType?>)?.value
return indicator ?? .none
}
set {
if kf_showIndicatorWhenLoading == newValue {
return
} else {
if newValue {
#if os(macOS)
let indicator = NSProgressIndicator(frame: CGRect(x: 0, y: 0, width: 16, height: 16))
indicator.controlSize = .small
indicator.style = .spinningStyle
#else
#if os(tvOS)
let indicatorStyle = UIActivityIndicatorViewStyle.white
#else
let indicatorStyle = UIActivityIndicatorViewStyle.gray
#endif
let indicator = UIActivityIndicatorView(activityIndicatorStyle:indicatorStyle)
indicator.autoresizingMask = [.flexibleLeftMargin, .flexibleRightMargin, .flexibleBottomMargin, .flexibleTopMargin]
#endif
indicator.kf_center = CGPoint(x: bounds.midX, y: bounds.midY)
indicator.isHidden = true
self.addSubview(indicator)
kf_setIndicator(indicator)
} else {
kf_indicator?.removeFromSuperview()
kf_setIndicator(nil)
}
objc_setAssociatedObject(self, &showIndicatorWhenLoadingKey, NSNumber(value: newValue), .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
switch newValue {
case .none:
kf_indicator = nil
case .activity:
kf_indicator = ActivityIndicator()
case .image(let data):
kf_indicator = ImageIndicator(imageData: data)
case .custom(let indicator):
kf_indicator = indicator
}
objc_setAssociatedObject(self, &indicatorTypeKey, Box(value: newValue), .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
}
}
/// The indicator view showing when loading. This will be `nil` if `kf_showIndicatorWhenLoading` is false.
/// You may want to use this to set the indicator style or color when you set `kf_showIndicatorWhenLoading` to true.
public var kf_indicator: IndicatorView? {
return objc_getAssociatedObject(self, &indicatorKey) as? IndicatorView
}
fileprivate func kf_setIndicator(_ indicator: IndicatorView?) {
objc_setAssociatedObject(self, &indicatorKey, indicator, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
/// `kf_indicator` holds any type that conforms to the protocol `Indicator`.
/// The protocol `Indicator` has a `view` property that will be shown when loading an image.
/// Everything will be `nil` if `kf_indicatorType` is .None.
public private(set) var kf_indicator: Indicator? {
get {
return (objc_getAssociatedObject(self, &indicatorKey) as? Box<Indicator?>)?.value
}
set {
// Remove previous
if let previousIndicator = kf_indicator {
previousIndicator.view.removeFromSuperview()
}
// Add new
if var newIndicator = newValue {
newIndicator.view.frame = self.frame
newIndicator.viewCenter = CGPoint(x: bounds.midX, y: bounds.midY)
newIndicator.view.isHidden = true
self.addSubview(newIndicator.view)
}
// Save in associated object
objc_setAssociatedObject(self, &indicatorKey, Box(value: newValue), .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
}
}
fileprivate var kf_imageTask: RetrieveImageTask? {
@@ -237,45 +240,3 @@ extension ImageView {
objc_setAssociatedObject(self, &imageTaskKey, task, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
}
}
extension IndicatorView {
func kf_startAnimating() {
#if os(macOS)
startAnimation(nil)
#else
startAnimating()
#endif
isHidden = false
}
func kf_stopAnimating() {
#if os(macOS)
stopAnimation(nil)
#else
stopAnimating()
#endif
isHidden = true
}
#if os(macOS)
var kf_center: CGPoint {
get {
return CGPoint(x: frame.origin.x + frame.size.width / 2.0, y: frame.origin.y + frame.size.height / 2.0 )
}
set {
let newFrame = CGRect(x: newValue.x - frame.size.width / 2.0, y: newValue.y - frame.size.height / 2.0, width: frame.size.width, height: frame.size.height)
frame = newFrame
}
}
#else
var kf_center: CGPoint {
get {
return center
}
set {
center = newValue
}
}
#endif
}

View File

@@ -93,7 +93,7 @@ struct ActivityIndicator: Indicator {
#else
activityIndicatorView.startAnimating()
#endif
activityIndicatorView.hidden = false
activityIndicatorView.isHidden = false
}
func stopAnimatingView() {
@@ -102,27 +102,22 @@ struct ActivityIndicator: Indicator {
#else
activityIndicatorView.stopAnimating()
#endif
activityIndicatorView.hidden = true
activityIndicatorView.isHidden = true
}
init() {
#if os(OSX)
activityIndicatorView = NSProgressIndicator(frame: CGRect(x: 0, y: 0, width: 16, height: 16))
#if swift(>=2.3)
activityIndicatorView.controlSize = .Small
#else
activityIndicatorView.controlSize = .SmallControlSize
#endif
activityIndicatorView.style = .SpinningStyle
activityIndicatorView.controlSize = .small
activityIndicatorView.style = .spinningStyle
#else
#if os(tvOS)
let indicatorStyle = UIActivityIndicatorViewStyle.White
let indicatorStyle = UIActivityIndicatorViewStyle.white
#else
let indicatorStyle = UIActivityIndicatorViewStyle.Gray
let indicatorStyle = UIActivityIndicatorViewStyle.gray
#endif
activityIndicatorView = UIActivityIndicatorView(activityIndicatorStyle:indicatorStyle)
activityIndicatorView.autoresizingMask = [.FlexibleLeftMargin, .FlexibleRightMargin, .FlexibleBottomMargin, .FlexibleTopMargin]
activityIndicatorView.autoresizingMask = [.flexibleLeftMargin, .flexibleRightMargin, .flexibleBottomMargin, .flexibleTopMargin]
#endif
}
}
@@ -136,23 +131,32 @@ struct ImageIndicator: Indicator {
return animatedImageIndicatorView
}
init(imageData data: NSData) {
init?(imageData data: Data, processor: ImageProcessor = DefaultImageProcessor.default, options: KingfisherOptionsInfo = KingfisherEmptyOptionsInfo) {
var options = options
// Use normal image view to show gif, so we need to preload all gif data.
if !options.preloadAllGIFData {
options.append(.preloadAllGIFData)
}
guard let image = processor.process(item: .data(data), options: options) else {
return nil
}
let image = Image.kf_imageWithData(data, scale: 1.0, preloadAllGIFData: true)
animatedImageIndicatorView = ImageView()
animatedImageIndicatorView.image = image
#if os(OSX)
// Need for gif to animate on OSX
self.animatedImageIndicatorView.imageScaling = .ScaleNone
self.animatedImageIndicatorView.imageScaling = .scaleNone
self.animatedImageIndicatorView.canDrawSubviewsIntoLayer = true
#else
animatedImageIndicatorView.contentMode = .Center
animatedImageIndicatorView.contentMode = .center
animatedImageIndicatorView.autoresizingMask = [.FlexibleLeftMargin,
.FlexibleRightMargin,
.FlexibleBottomMargin,
.FlexibleTopMargin]
animatedImageIndicatorView.autoresizingMask = [.flexibleLeftMargin,
.flexibleRightMargin,
.flexibleBottomMargin,
.flexibleTopMargin]
#endif
}
@@ -162,7 +166,7 @@ struct ImageIndicator: Indicator {
#else
animatedImageIndicatorView.startAnimating()
#endif
animatedImageIndicatorView.hidden = false
animatedImageIndicatorView.isHidden = false
}
func stopAnimatingView() {
@@ -171,6 +175,6 @@ struct ImageIndicator: Indicator {
#else
animatedImageIndicatorView.stopAnimating()
#endif
animatedImageIndicatorView.hidden = true
animatedImageIndicatorView.isHidden = true
}
}