diff --git a/Sample App/JTSImageVC/JTSImageVC.xcodeproj/project.pbxproj b/Sample App/JTSImageVC/JTSImageVC.xcodeproj/project.pbxproj index 44f4caf..b355c94 100644 --- a/Sample App/JTSImageVC/JTSImageVC.xcodeproj/project.pbxproj +++ b/Sample App/JTSImageVC/JTSImageVC.xcodeproj/project.pbxproj @@ -32,6 +32,7 @@ 7FA8566719B2762F00913254 /* banecat-red.png in Resources */ = {isa = PBXBuildFile; fileRef = 7FA8566619B2762F00913254 /* banecat-red.png */; }; 7FE0848218E7C5C600F2F6B4 /* banecat.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 7FE0848118E7C5C600F2F6B4 /* banecat.jpg */; }; 7FE95B4E18EE0E8100F18370 /* UIApplication+JTSImageViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 7FE95B4D18EE0E8100F18370 /* UIApplication+JTSImageViewController.m */; }; + C30DCC58191007E900B8445C /* jimpam.gif in Resources */ = {isa = PBXBuildFile; fileRef = C30DCC57191007E900B8445C /* jimpam.gif */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -81,6 +82,7 @@ 7FE0848118E7C5C600F2F6B4 /* banecat.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = banecat.jpg; sourceTree = ""; }; 7FE95B4C18EE0E8100F18370 /* UIApplication+JTSImageViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "UIApplication+JTSImageViewController.h"; path = "../../../Source/UIApplication+JTSImageViewController.h"; sourceTree = ""; }; 7FE95B4D18EE0E8100F18370 /* UIApplication+JTSImageViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "UIApplication+JTSImageViewController.m"; path = "../../../Source/UIApplication+JTSImageViewController.m"; sourceTree = ""; }; + C30DCC57191007E900B8445C /* jimpam.gif */ = {isa = PBXFileReference; lastKnownFileType = image.gif; path = jimpam.gif; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -141,6 +143,7 @@ 7F9A790418E754A7000CAA69 /* Sample App */ = { isa = PBXGroup; children = ( + C30DCC57191007E900B8445C /* jimpam.gif */, 7FE0848118E7C5C600F2F6B4 /* banecat.jpg */, 7FA8566619B2762F00913254 /* banecat-red.png */, 47A333FD19D8F34A00F60031 /* Default-1334.png */, @@ -285,6 +288,7 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( + C30DCC58191007E900B8445C /* jimpam.gif in Resources */, 472FDE9A19DA4B68000C0864 /* Default-1136.png in Resources */, 7FE0848218E7C5C600F2F6B4 /* banecat.jpg in Resources */, 7FA8566719B2762F00913254 /* banecat-red.png in Resources */, diff --git a/Sample App/JTSImageVC/JTSImageVC.xcodeproj/project.xcworkspace/xcshareddata/JTSImageVC.xccheckout b/Sample App/JTSImageVC/JTSImageVC.xcodeproj/project.xcworkspace/xcshareddata/JTSImageVC.xccheckout new file mode 100644 index 0000000..0546713 --- /dev/null +++ b/Sample App/JTSImageVC/JTSImageVC.xcodeproj/project.xcworkspace/xcshareddata/JTSImageVC.xccheckout @@ -0,0 +1,41 @@ + + + + + IDESourceControlProjectFavoriteDictionaryKey + + IDESourceControlProjectIdentifier + A53BEEAD-1C96-4AD9-B9A4-DD2DC3EE505A + IDESourceControlProjectName + JTSImageVC + IDESourceControlProjectOriginsDictionary + + 80DA628EAE81E418FCA5040B6FD2F14B521C07CB + https://github.com/geoffmacd/JTSImageViewController.git + + IDESourceControlProjectPath + Sample App/JTSImageVC/JTSImageVC.xcodeproj + IDESourceControlProjectRelativeInstallPathDictionary + + 80DA628EAE81E418FCA5040B6FD2F14B521C07CB + ../../../.. + + IDESourceControlProjectURL + https://github.com/geoffmacd/JTSImageViewController.git + IDESourceControlProjectVersion + 111 + IDESourceControlProjectWCCIdentifier + 80DA628EAE81E418FCA5040B6FD2F14B521C07CB + IDESourceControlProjectWCConfigurations + + + IDESourceControlRepositoryExtensionIdentifierKey + public.vcs.git + IDESourceControlWCCIdentifierKey + 80DA628EAE81E418FCA5040B6FD2F14B521C07CB + IDESourceControlWCCName + JTSImageViewController + + + + diff --git a/Sample App/JTSImageVC/JTSImageVC/Base.lproj/Main.storyboard b/Sample App/JTSImageVC/JTSImageVC/Base.lproj/Main.storyboard index f077467..35602e0 100644 --- a/Sample App/JTSImageVC/JTSImageVC/Base.lproj/Main.storyboard +++ b/Sample App/JTSImageVC/JTSImageVC/Base.lproj/Main.storyboard @@ -13,37 +13,65 @@ - - - - - - - - - - - - - + + + + + + - + + + @@ -52,6 +80,7 @@ + diff --git a/Sample App/JTSImageVC/JTSImageVC/JTSViewController.h b/Sample App/JTSImageVC/JTSImageVC/JTSViewController.h index 8488746..13df2a9 100644 --- a/Sample App/JTSImageVC/JTSImageVC/JTSViewController.h +++ b/Sample App/JTSImageVC/JTSImageVC/JTSViewController.h @@ -8,8 +8,10 @@ #import -@interface JTSViewController : UIViewController +@interface JTSViewController : UIViewController -@property (weak, nonatomic) IBOutlet UIImageView *bigImageButton; +@property (weak, nonatomic) IBOutlet UIButton *bigImageButton; +@property (weak, nonatomic) IBOutlet UIButton *simpleImageButton; +@property (weak, nonatomic) IBOutlet UIButton *customBigImageButton; @end diff --git a/Sample App/JTSImageVC/JTSImageVC/JTSViewController.m b/Sample App/JTSImageVC/JTSImageVC/JTSViewController.m index 7af0307..1cbc32d 100644 --- a/Sample App/JTSImageVC/JTSImageVC/JTSViewController.m +++ b/Sample App/JTSImageVC/JTSImageVC/JTSViewController.m @@ -10,31 +10,35 @@ #import "JTSImageViewController.h" #import "JTSImageInfo.h" +#import "JTSAnimatedGIFUtility.h" @interface JTSViewController () +@property NSProgress *customProgress; +@property JTSImageViewController * imageViewerForCustomLoading; + @end + @implementation JTSViewController - (void)viewDidLoad { [super viewDidLoad]; - UITapGestureRecognizer *tapRecognizer = [[UITapGestureRecognizer alloc] init]; - [tapRecognizer addTarget:self action:@selector(bigButtonTapped:)]; - [self.bigImageButton addGestureRecognizer:tapRecognizer]; + [self.bigImageButton setAccessibilityLabel:@"Photo of a cat wearing a Bane costume."]; - self.bigImageButton.layer.cornerRadius = self.bigImageButton.bounds.size.width/2.0f; + [self.simpleImageButton setAccessibilityLabel:@"Photo of jim and pam in love"]; + [self.customBigImageButton setAccessibilityLabel:@"Photo of jim and pam in love"]; } - (NSUInteger)supportedInterfaceOrientations { return UIInterfaceOrientationMaskPortrait; } -- (void)bigButtonTapped:(id)sender { +- (IBAction)bigButtonTapped:(id)sender { // Create image info JTSImageInfo *imageInfo = [[JTSImageInfo alloc] init]; - imageInfo.image = self.bigImageButton.image; + imageInfo.image = [self.bigImageButton backgroundImageForState:UIControlStateNormal]; imageInfo.referenceRect = self.bigImageButton.frame; imageInfo.referenceView = self.bigImageButton.superview; imageInfo.referenceContentMode = self.bigImageButton.contentMode; @@ -46,10 +50,81 @@ - (void)bigButtonTapped:(id)sender { mode:JTSImageViewControllerMode_Image backgroundStyle:JTSImageViewControllerBackgroundOption_Scaled]; + + // Present the view controller. + [imageViewer showFromViewController:self transition:JTSImageViewControllerTransition_FromOriginalPosition]; +} + +- (IBAction)simpleButtonTapped:(id)sender { + // Create image info + JTSImageInfo *imageInfo = [[JTSImageInfo alloc] init]; + imageInfo.imageURL = [NSURL URLWithString:@"http://i.imgur.com/iGRxQNb.gif"]; + imageInfo.referenceRect = self.simpleImageButton.frame; + imageInfo.referenceView = self.simpleImageButton.superview; + + // Setup view controller + JTSImageViewController *imageViewer = [[JTSImageViewController alloc] + initWithImageInfo:imageInfo + mode:JTSImageViewControllerMode_Image + backgroundStyle:JTSImageViewControllerBackgroundOption_Blurred]; + + // Present the view controller. [imageViewer showFromViewController:self transition:JTSImageViewControllerTransition_FromOriginalPosition]; } +- (IBAction)customBigButtonTapped:(id)sender { + //download image with custom progress from SDWebImage + + // Create image info + JTSImageInfo *imageInfo = [[JTSImageInfo alloc] init]; + imageInfo.referenceRect = self.customBigImageButton.frame; + imageInfo.referenceView = self.customBigImageButton.superview; + imageInfo.referenceContentMode = self.bigImageButton.contentMode; + imageInfo.referenceCornerRadius = self.bigImageButton.layer.cornerRadius; + + NSProgress * customProgress = [NSProgress progressWithTotalUnitCount:0]; + customProgress.kind = NSProgressKindFile; + self.customProgress = customProgress; + + //with custom progress + JTSImageViewController *imageViewer = [[JTSImageViewController alloc] + initWithImageInfo:imageInfo + mode:JTSImageViewControllerMode_Image + backgroundStyle:JTSImageViewControllerBackgroundOption_Blurred + customImageLoadingProgress:self.customProgress]; + NSURLSessionConfiguration * sessionConfig = [NSURLSessionConfiguration defaultSessionConfiguration]; + [sessionConfig setRequestCachePolicy:NSURLRequestReloadIgnoringCacheData];//no cache to allow repeatable testing + NSURLSession * session = [NSURLSession sessionWithConfiguration:sessionConfig delegate:self delegateQueue:[NSOperationQueue mainQueue]]; //main thread response for delegates + NSURLSessionDownloadTask * task = [session downloadTaskWithRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"http://i.imgur.com/iGRxQNb.gif"]]]; + + self.imageViewerForCustomLoading = imageViewer; + + [task resume]; + // Present the view controller. + [imageViewer showFromViewController:self transition:JTSImageViewControllerTransition_FromOriginalPosition]; + +} +#pragma mark - url session download delegate +-(void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didResumeAtOffset:(int64_t)fileOffset expectedTotalBytes:(int64_t)expectedTotalBytes{ + + _customProgress.totalUnitCount = expectedTotalBytes; + _customProgress.completedUnitCount = 0; + +} + +-(void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite{ + + _customProgress.totalUnitCount = totalBytesExpectedToWrite; + _customProgress.completedUnitCount = totalBytesWritten; +} + +-(void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location{ + + UIImage * image = [JTSAnimatedGIFUtility animatedImageWithAnimatedGIFURL:location]; + [self.imageViewerForCustomLoading customImageLoadingDidFinish:image]; +} + @end diff --git a/Sample App/JTSImageVC/JTSImageVC/jimpam.gif b/Sample App/JTSImageVC/JTSImageVC/jimpam.gif new file mode 100644 index 0000000..f180a6e Binary files /dev/null and b/Sample App/JTSImageVC/JTSImageVC/jimpam.gif differ diff --git a/Source/JTSImageViewController.h b/Source/JTSImageViewController.h index 40a2340..de46bf8 100644 --- a/Source/JTSImageViewController.h +++ b/Source/JTSImageViewController.h @@ -70,13 +70,36 @@ extern CGFloat const JTSImageViewController_DefaultBackgroundBlurRadius; @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. + @param backgroundStyle Currently, either scaled-and-dimmed, or scaled-dimmed-and-blurred. The latter is like Tweetbot 3.0's background style. */ - (instancetype)initWithImageInfo:(JTSImageInfo *)imageInfo mode:(JTSImageViewControllerMode)mode backgroundStyle:(JTSImageViewControllerBackgroundOptions)backgroundOptions; +/** + Initializer for using JTSImageViewController with a custom image downloader for the purpose of easily allowing cooperation with 3rd party libraries for downloading or caching images such as SDWebImage. Caller is responsible for setting the image view via customImageLoadingDidFinish: when image download is finished. + + @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 customImageProgress Progress object to be updated by the caller with completed and totalUnitCount to inform of download progress. + */ +- (instancetype)initWithImageInfo:(JTSImageInfo *)imageInfo + mode:(JTSImageViewControllerMode)mode + backgroundStyle:(JTSImageViewControllerBackgroundOptions)backgroundStyle + customImageLoadingProgress:(NSProgress*)customImageProgress; + + +/** + Image setter intended for use only when using custom image downloader to provide the UIImage to be used upon download/retrieval completion. + + @param image Image to be displayed + */ +-(void)customImageLoadingDidFinish:(UIImage*)image; /** JTSImageViewController is presented from viewController as a UIKit modal view controller. @@ -120,7 +143,7 @@ extern CGFloat const JTSImageViewController_DefaultBackgroundBlurRadius; and dismissal animations. This may be helpful if the reference image in your presenting view controller has been - dimmed, such as for a dark mode. JTSImageViewController otherwise presents the animated + dimmed, such as for a dark mode. JTSImageViewController otherwise presents the animated image view at full opacity, which can look jarring. */ - (BOOL)imageViewerShouldFadeThumbnailsDuringPresentationAndDismissal:(JTSImageViewController *)imageViewer; @@ -141,9 +164,9 @@ extern CGFloat const JTSImageViewController_DefaultBackgroundBlurRadius; /** The background color of the image view itself, not to be confused with the background - color for the view controller's view. + color for the view controller's view. - You may wish to override this method if displaying an image with dark content on an + You may wish to override this method if displaying an image with dark content on an otherwise clear background color (such as images from the XKCD What If? site). The default color is `[UIColor clearColor]`. @@ -182,7 +205,7 @@ extern CGFloat const JTSImageViewController_DefaultBackgroundBlurRadius; /** Called when the image viewer is deciding whether to respond to user interactions. - You may need to return NO if you are presenting custom, temporary UI on top of the image viewer. + You may need to return NO if you are presenting custom, temporary UI on top of the image viewer. This method is called more than once. Returning NO does not "lock" the image viewer. */ - (BOOL)imageViewerShouldTemporarilyIgnoreTouches:(JTSImageViewController *)imageViewer; @@ -235,4 +258,3 @@ extern CGFloat const JTSImageViewController_DefaultBackgroundBlurRadius; - diff --git a/Source/JTSImageViewController.m b/Source/JTSImageViewController.m index d1e1dbb..fb9ce7e 100644 --- a/Source/JTSImageViewController.m +++ b/Source/JTSImageViewController.m @@ -61,10 +61,10 @@ @interface JTSImageViewController () < - UIScrollViewDelegate, - UITextViewDelegate, - UIViewControllerTransitioningDelegate, - UIGestureRecognizerDelegate +UIScrollViewDelegate, +UITextViewDelegate, +UIViewControllerTransitioningDelegate, +UIGestureRecognizerDelegate > // General Info @@ -109,6 +109,8 @@ @interface JTSImageViewController () // Image Downloading @property (strong, nonatomic) NSURLSessionDataTask *imageDownloadDataTask; @property (strong, nonatomic) NSTimer *downloadProgressTimer; +@property (assign, nonatomic) BOOL imageDownloadUsingCustom; +@property (strong, nonatomic) NSProgress * customImageProgress; @end @@ -138,6 +140,47 @@ - (instancetype)initWithImageInfo:(JTSImageInfo *)imageInfo return self; } +- (instancetype)initWithImageInfo:(JTSImageInfo *)imageInfo + mode:(JTSImageViewControllerMode)mode + backgroundStyle:(JTSImageViewControllerBackgroundOptions)backgroundStyle + customImageLoadingProgress:(NSProgress*)customImageProgress{ + + 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 = backgroundStyle; + if (_mode == JTSImageViewControllerMode_Image) { + _imageDownloadUsingCustom = YES; + //set placeholder + [self setImage:imageInfo.placeholderImage]; + //retain NSProgress to be used in place of imageDownloadDataTask + _customImageProgress = customImageProgress; + [self startProgressTimer]; + } + } + return self; +} + +-(void)customImageLoadingDidFinish:(UIImage*)image{ + + if (image) { + if (self.isViewLoaded) { + [self updateInterfaceWithImage:image]; + } else { + [self setImage:image]; + } + } else if (self.image == nil) { + _flags.imageDownloadFailed = YES; + if (_flags.isPresented && _flags.isAnimatingAPresentationOrDismissal == NO) { + [self dismiss:YES]; + } + // If we're still presenting, at the end of presentation we'll auto dismiss. + } +} + - (void)showFromViewController:(UIViewController *)viewController transition:(JTSImageViewControllerTransition)transition { @@ -676,7 +719,7 @@ - (void)showImageViewerByExpandingFromOriginalPositionFromViewController:(UIView } else { [[UIApplication sharedApplication] setStatusBarHidden:YES withAnimation:UIStatusBarAnimationFade]; } - + CGFloat scaling; if (!(weakSelf.backgroundOptions & JTSImageViewControllerBackgroundOption_Scaled)) { scaling = 1.0; @@ -1039,7 +1082,7 @@ - (void)dismissByCollapsingImageBackToOriginalPosition { [weakSelf.imageView.layer addAnimation:cornerRadiusAnimation forKey:@"cornerRadius"]; weakSelf.imageView.layer.cornerRadius = weakSelf.imageInfo.referenceCornerRadius; - [UIView animateWithDuration:duration delay:0 options:UIViewAnimationOptionBeginFromCurrentState | UIViewAnimationOptionCurveEaseInOut animations:^{ + [UIView animateWithDuration:duration delay:0 options:UIViewAnimationOptionBeginFromCurrentState| UIViewAnimationOptionCurveEaseInOut animations:^{ if ([weakSelf.animationDelegate respondsToSelector:@selector(imageViewerWillAnimateDismissal:withContainerView:duration:)]) { [weakSelf.animationDelegate imageViewerWillAnimateDismissal:weakSelf withContainerView:weakSelf.view duration:duration]; @@ -1872,13 +1915,25 @@ - (void)cancelProgressTimer { - (void)progressTimerFired:(NSTimer *)timer { CGFloat progress = 0; - CGFloat bytesExpected = self.imageDownloadDataTask.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; + if(self.imageDownloadUsingCustom){ + if(self.customImageProgress){ + //use progress object to determine completion + progress = _customImageProgress.fractionCompleted; + if(_customImageProgress.totalUnitCount){ + self.spinner.alpha = 0; + self.progressView.alpha = 1; + } + } + } else { + //JTSSimpleImageDownloader + CGFloat bytesExpected = self.imageDownloadDataTask.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; + } } self.progressView.progress = progress; } @@ -1963,4 +2018,3 @@ - (NSString *)defaultAccessibilityHintForScrollView:(BOOL)zoomedIn { @end -