Skip to content

Commit

Permalink
Protocol indicator
Browse files Browse the repository at this point in the history
  • Loading branch information
onevcat committed Sep 13, 2016
1 parent 014ed46 commit 1869184
Show file tree
Hide file tree
Showing 6 changed files with 116 additions and 129 deletions.
6 changes: 2 additions & 4 deletions Demo/Kingfisher-Demo/ViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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))],
Expand Down
File renamed without changes
2 changes: 1 addition & 1 deletion Demo/Kingfisher-macOS-Demo/ViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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)")
Expand Down
24 changes: 24 additions & 0 deletions Kingfisher.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -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 */; };
Expand Down Expand Up @@ -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>"; };
Expand Down Expand Up @@ -871,6 +883,7 @@
isa = PBXGroup;
children = (
D10945EA1C526B6C001408EB /* Image.swift */,
4B77423E1D87E08A0077024E /* Indicator.swift */,
D10945EB1C526B6C001408EB /* ImageCache.swift */,
D10945EC1C526B6C001408EB /* ImageDownloader.swift */,
D9638B9F1C7DBA660046523D /* ImagePrefetcher.swift */,
Expand Down Expand Up @@ -907,6 +920,7 @@
4BF39F3F1D796BDD0035ECC8 /* Helpers */ = {
isa = PBXGroup;
children = (
4B7742421D87E2AA0077024E /* Box.swift */,
D10945F41C526B6C001408EB /* String+MD5.swift */,
D10945F51C526B6C001408EB /* ThreadHelper.swift */,
);
Expand Down Expand Up @@ -1017,6 +1031,7 @@
D12E0C8B1C47F91800AC98AD /* Kingfisher-Demo */ = {
isa = PBXGroup;
children = (
4B7742461D87E42E0077024E /* loader.gif */,
D12E0C8C1C47F91800AC98AD /* AppDelegate.swift */,
D12E0C8D1C47F91800AC98AD /* LaunchScreen.xib */,
D12E0C8F1C47F91800AC98AD /* Main.storyboard */,
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -1631,6 +1647,7 @@
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
4B7742481D87E42E0077024E /* loader.gif in Resources */,
D12E0CA31C47F92200AC98AD /* Assets.xcassets in Resources */,
D12E0CA41C47F92200AC98AD /* Main.storyboard in Resources */,
);
Expand Down Expand Up @@ -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 */,
);
Expand Down Expand Up @@ -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 */,
Expand Down Expand Up @@ -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 */,
Expand Down Expand Up @@ -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 */,
Expand Down
165 changes: 63 additions & 102 deletions Sources/ImageView+Kingfisher.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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)

Expand All @@ -100,23 +93,23 @@ extension ImageView {
sSelf.kf_setImageTask(nil)

guard let image = image else {
indicator?.kf_stopAnimating()
maybeIndicator?.stopAnimatingView()
completionHandler?(nil, error, cacheType, imageURL)
return
}

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
}

#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],
Expand Down Expand Up @@ -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 {
Expand All @@ -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? {
Expand All @@ -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
}
Loading

0 comments on commit 1869184

Please sign in to comment.