Skip to content

Support for a custom downloader and delegate to provide them #95

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 5 additions & 4 deletions JTSImageViewController.podspec
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
Pod::Spec.new do |s|
s.name = "JTSImageViewController"
s.version = "1.5.1"
s.version = "1.5.3"
s.summary = "An interactive iOS image viewer that does it all: double tap to zoom, flick to dismiss, et cetera."
s.homepage = "https://github.com/jaredsinclair/JTSImageViewController"
s.homepage = "https://github.com/brucehappy/JTSImageViewController"
s.license = { :type => 'MIT', :file => 'LICENSE' }
s.author = { "Jared Sinclair" => "desk@jaredsinclair.com" }
s.source = { :git => "https://github.com/jaredsinclair/JTSImageViewController.git", :tag => s.version.to_s }
s.authors = { "Jared Sinclair" => "desk@jaredsinclair.com",
"Bruce Duncan" => "bduncan@rassilon.co" }
s.source = { :git => "https://github.com/brucehappy/JTSImageViewController.git", :tag => s.version.to_s }
s.platform = :ios, '7.0'
s.requires_arc = true
s.frameworks = 'UIKit', 'ImageIO', 'Accelerate'
Expand Down
5 changes: 4 additions & 1 deletion Sample App/JTSImageVC/JTSImageVC/JTSViewController.m
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#import "JTSImageInfo.h"

#define TRY_AN_ANIMATED_GIF 0
#define TRY_AN_IMAGE_URL 0

@interface JTSViewController ()

Expand All @@ -37,7 +38,9 @@ - (void)bigButtonTapped:(id)sender {
// Create image info
JTSImageInfo *imageInfo = [[JTSImageInfo alloc] init];
#if TRY_AN_ANIMATED_GIF == 1
imageInfo.imageURL = [NSURL URLWithString:@"http://media.giphy.com/media/O3QpFiN97YjJu/giphy.gif"];
imageInfo.imageURL = [NSURL URLWithString:@"https://media.giphy.com/media/O3QpFiN97YjJu/giphy.gif"];
#elif TRY_AN_IMAGE_URL == 1
imageInfo.imageURL = [NSURL URLWithString:@"https://upload.wikimedia.org/wikipedia/commons/6/67/Inside_the_Batad_rice_terraces.jpg"];
#else
imageInfo.image = self.bigImageButton.image;
#endif
Expand Down
70 changes: 69 additions & 1 deletion Source/JTSImageViewController.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@
@protocol JTSImageViewControllerInteractionsDelegate;
@protocol JTSImageViewControllerAccessibilityDelegate;
@protocol JTSImageViewControllerAnimationDelegate;
@protocol JTSImageViewControllerDownloader;
@protocol JTSImageViewControllerDownloaderDelegate;

typedef NS_ENUM(NSInteger, JTSImageViewControllerMode) {
JTSImageViewControllerMode_Image,
Expand Down Expand Up @@ -63,8 +65,10 @@ extern CGFloat const JTSImageViewController_DefaultBackgroundBlurRadius;

@property (weak, nonatomic, readwrite) id <JTSImageViewControllerAnimationDelegate> animationDelegate;

@property (weak, nonatomic, readonly) id <JTSImageViewControllerDownloaderDelegate> downloaderDelegate;

/**
Designated initializer.
Convenience initializer.

@param imageInfo The source info for image and transition metadata. Required.

Expand All @@ -77,6 +81,25 @@ extern CGFloat const JTSImageViewController_DefaultBackgroundBlurRadius;
mode:(JTSImageViewControllerMode)mode
backgroundStyle:(JTSImageViewControllerBackgroundOptions)backgroundOptions;

/**
Designated initializer.

@param imageInfo The source info for image and transition metadata. Required.

@param mode The mode to be used. (JTSImageViewController has an alternate alt text mode). Required.

@param backgroundStyle Currently, either scaled-and-dimmed, or scaled-dimmed-and-blurred.
The latter is like Tweetbot 3.0's background style.

@param downloaderDelegate The downloaderDelegate to be used. Optional.
*/

- (instancetype)initWithImageInfo:(JTSImageInfo *)imageInfo
mode:(JTSImageViewControllerMode)mode
backgroundStyle:(JTSImageViewControllerBackgroundOptions)backgroundOptions
downloaderDelegate:(id <JTSImageViewControllerDownloaderDelegate>)downloaderDelegate;


/**
JTSImageViewController is presented from viewController as a UIKit modal view controller.

Expand Down Expand Up @@ -229,6 +252,51 @@ extern CGFloat const JTSImageViewController_DefaultBackgroundBlurRadius;

@end

///---------------------------------------------------------------------------------------------------
/// Downloader
///---------------------------------------------------------------------------------------------------

@protocol JTSImageViewControllerDownloader <NSObject>

/**
The number of bytes that are expected to be recieved.
*/
@property (readonly) int64_t countOfBytesExpectedToReceive;

/**
The number of bytes that have been received.
*/
@property (readonly) int64_t countOfBytesReceived;

/**
Called to begin downloading the image.

Calls block with non-nil value on success, and nil on failure.
*/
- (void)downloadImage:(void (^)(UIImage *image))completion;

@optional

/**
Called to cancel the download if it is in progress.
*/
- (void)cancel;

@end

///---------------------------------------------------------------------------------------------------
/// Downloader Delegate
///---------------------------------------------------------------------------------------------------

@protocol JTSImageViewControllerDownloaderDelegate <NSObject>

/**
Called to retrieve a downloader for the given image information.
*/
- (id <JTSImageViewControllerDownloader>)downloaderForImageInfo:(JTSImageInfo *)imageInfo;

@end




Expand Down
104 changes: 90 additions & 14 deletions Source/JTSImageViewController.m
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
#import "UIImage+JTSImageEffects.h"
#import "UIApplication+JTSImageViewController.h"

#pragma mark - Definitions

CG_INLINE CGFLOAT_TYPE JTSImageFloatAbs(CGFLOAT_TYPE aFloat) {
#if CGFLOAT_IS_DOUBLE
return fabs(aFloat);
Expand Down Expand Up @@ -61,18 +63,30 @@ CG_INLINE CGFLOAT_TYPE JTSImageFloatAbs(CGFLOAT_TYPE aFloat) {
BOOL imageDownloadFailed;
} JTSImageViewControllerFlags;

// Private Downloader Interface
@interface __PrivateJTSImageViewControllerDownloader: NSObject <JTSImageViewControllerDownloader>
- (id)initWithImageInfo:(JTSImageInfo *)imageInfo;
- (int64_t)countOfBytesExpectedToReceive;
- (int64_t)countOfBytesReceived;
@property (strong, nonatomic, readwrite) NSURLSessionDataTask *task;
@property (strong, nonatomic, readwrite) JTSImageInfo *info;
@end

#define USE_DEBUG_SLOW_ANIMATIONS 0

///--------------------------------------------------------------------------------------------------------------------
/// Anonymous Category
///--------------------------------------------------------------------------------------------------------------------

#pragma mark - Public Interface

@interface JTSImageViewController ()
<
UIScrollViewDelegate,
UITextViewDelegate,
UIViewControllerTransitioningDelegate,
UIGestureRecognizerDelegate
UIGestureRecognizerDelegate,
JTSImageViewControllerDownloaderDelegate
>

// General Info
Expand Down Expand Up @@ -115,7 +129,7 @@ @interface JTSImageViewController ()
@property (assign, nonatomic) UIOffset imageDragOffsetFromImageCenter;

// Image Downloading
@property (strong, nonatomic) NSURLSessionDataTask *imageDownloadDataTask;
@property (strong, nonatomic) id<JTSImageViewControllerDownloader> imageDownloader;
@property (strong, nonatomic) NSTimer *downloadProgressTimer;

@end
Expand All @@ -124,21 +138,30 @@ @interface JTSImageViewController ()
/// Implementation
///--------------------------------------------------------------------------------------------------------------------

@implementation JTSImageViewController

#pragma mark - Public

@implementation JTSImageViewController

- (instancetype)initWithImageInfo:(JTSImageInfo *)imageInfo
mode:(JTSImageViewControllerMode)mode
backgroundStyle:(JTSImageViewControllerBackgroundOptions)backgroundOptions {

return [self initWithImageInfo:imageInfo mode:mode backgroundStyle:backgroundOptions downloaderDelegate:nil];
}

- (instancetype)initWithImageInfo:(JTSImageInfo *)imageInfo
mode:(JTSImageViewControllerMode)mode
backgroundStyle:(JTSImageViewControllerBackgroundOptions)backgroundOptions
downloaderDelegate:(id<JTSImageViewControllerDownloaderDelegate>)downloaderDelegate {

self = [super initWithNibName:nil bundle:nil];
if (self) {
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(deviceOrientationDidChange:) name:UIDeviceOrientationDidChangeNotification object:nil];
_imageInfo = imageInfo;
_currentSnapshotRotationTransform = CGAffineTransformIdentity;
_mode = mode;
_backgroundOptions = backgroundOptions;
_downloaderDelegate = downloaderDelegate;
if (_mode == JTSImageViewControllerMode_Image) {
[self setupImageAndDownloadIfNecessary:imageInfo];
}
Expand Down Expand Up @@ -203,7 +226,9 @@ - (void)dismiss:(BOOL)animated {

- (void)dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIDeviceOrientationDidChangeNotification object:nil];
[_imageDownloadDataTask cancel];
if ([self.imageDownloader respondsToSelector:NSSelectorFromString(@"cancel:")]) {
[self.imageDownloader cancel];
}
[self cancelProgressTimer];
}

Expand Down Expand Up @@ -383,6 +408,12 @@ one landscape orientation to the other (or from one portrait orientation to anot
}
}

#pragma mark - Downloader Delegate

- (id <JTSImageViewControllerDownloader>)downloaderForImageInfo:(JTSImageInfo *)imageInfo {
return [[__PrivateJTSImageViewControllerDownloader alloc] initWithImageInfo:imageInfo];
}

#pragma mark - Setup

- (void)setupImageAndDownloadIfNecessary:(JTSImageInfo *)imageInfo {
Expand All @@ -396,8 +427,18 @@ - (void)setupImageAndDownloadIfNecessary:(JTSImageInfo *)imageInfo {
BOOL fromDisk = [imageInfo.imageURL.absoluteString hasPrefix:@"file://"];
_flags.imageIsBeingReadFromDisk = fromDisk;

id<JTSImageViewControllerDownloaderDelegate> delegate = self.downloaderDelegate != nil ? self.downloaderDelegate : self;
self.imageDownloader = [delegate downloaderForImageInfo:imageInfo];

//Setup the timer before we call download image, since its possible that
//it may immediately call cancelProgressTimer BEFORE we call startProgressTimer
//If that happens, it will invalidate the timer we create here via the call
//to setup, and then when we call start, we will simeply return since the timer
//will be nil
[self setupProgressTimer];

typeof(self) __weak weakSelf = self;
NSURLSessionDataTask *task = [JTSSimpleImageDownloader downloadImageForURL:imageInfo.imageURL canonicalURL:imageInfo.canonicalImageURL completion:^(UIImage *image) {
[self.imageDownloader downloadImage:^(UIImage *image){
typeof(self) strongSelf = weakSelf;
[strongSelf cancelProgressTimer];
if (image) {
Expand All @@ -415,8 +456,6 @@ - (void)setupImageAndDownloadIfNecessary:(JTSImageInfo *)imageInfo {
}
}];

self.imageDownloadDataTask = task;

[self startProgressTimer];
}
}
Expand Down Expand Up @@ -1864,30 +1903,37 @@ - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRequire

#pragma mark - Progress Bar

- (void)startProgressTimer {
- (void)setupProgressTimer {
self.downloadProgressTimer = [[NSTimer alloc] initWithFireDate:[NSDate date]
interval:0.05
target:self
selector:@selector(progressTimerFired:)
userInfo:nil
repeats:YES];
[[NSRunLoop mainRunLoop] addTimer:self.downloadProgressTimer forMode:NSRunLoopCommonModes];
}

- (void)startProgressTimer {
if (self.downloadProgressTimer != nil) {
[[NSRunLoop mainRunLoop] addTimer:self.downloadProgressTimer forMode:NSRunLoopCommonModes];
}
}

- (void)cancelProgressTimer {
[self.downloadProgressTimer invalidate];
self.downloadProgressTimer = nil;
if (self.downloadProgressTimer != nil) {
[self.downloadProgressTimer invalidate];
self.downloadProgressTimer = nil;
}
}

- (void)progressTimerFired:(NSTimer *)timer {
CGFloat progress = 0;
CGFloat bytesExpected = self.imageDownloadDataTask.countOfBytesExpectedToReceive;
CGFloat bytesExpected = self.imageDownloader.countOfBytesExpectedToReceive;
if (bytesExpected > 0 && _flags.imageIsBeingReadFromDisk == NO) {
[UIView animateWithDuration:0.25 delay:0 options:UIViewAnimationOptionBeginFromCurrentState | UIViewAnimationOptionCurveLinear animations:^{
self.spinner.alpha = 0;
self.progressView.alpha = 1;
} completion:nil];
progress = self.imageDownloadDataTask.countOfBytesReceived / bytesExpected;
progress = self.imageDownloader.countOfBytesReceived / bytesExpected;
}
self.progressView.progress = progress;
}
Expand Down Expand Up @@ -1971,5 +2017,35 @@ - (NSString *)defaultAccessibilityHintForScrollView:(BOOL)zoomedIn {

@end

///--------------------------------------------------------------------------------------------------------------------
/// Private Downloader Implementation
///--------------------------------------------------------------------------------------------------------------------

#pragma mark - Private Downloader
@implementation __PrivateJTSImageViewControllerDownloader

- (instancetype)initWithImageInfo:(JTSImageInfo *)imageInfo {
self = [super init];
_info = imageInfo;
return self;
}

- (void)downloadImage:(void (^)(UIImage *))completion {
_task = [JTSSimpleImageDownloader downloadImageForURL:_info.imageURL canonicalURL:_info.canonicalImageURL completion:completion];
}

- (void)cancel {
[_task cancel];
}

- (int64_t)countOfBytesExpectedToReceive {
return _task.countOfBytesExpectedToReceive;
}

- (int64_t)countOfBytesReceived {
return _task.countOfBytesReceived;
}

@end