diff --git a/NYTPhotoViewer.podspec b/NYTPhotoViewer.podspec index bbc2525e..dcc185fa 100644 --- a/NYTPhotoViewer.podspec +++ b/NYTPhotoViewer.podspec @@ -22,7 +22,7 @@ Pod::Spec.new do |s| end s.subspec 'AnimatedGifSupport' do |ss| - ss.xcconfig = { 'GCC_PREPROCESSOR_DEFINITIONS' => 'ANIMATED_GIF_SUPPORT=1'} + ss.xcconfig = { 'GCC_PREPROCESSOR_DEFINITIONS' => 'ANIMATED_GIF_SUPPORT=1 INTERACTIVE_RELOAD=1'} ss.dependency 'NYTPhotoViewer/Core' ss.dependency 'FLAnimatedImage', '~> 1.0.12' diff --git a/NYTPhotoViewer/NYTPhotoViewController.m b/NYTPhotoViewer/NYTPhotoViewController.m index d37b25f6..9390d26c 100644 --- a/NYTPhotoViewer/NYTPhotoViewController.m +++ b/NYTPhotoViewer/NYTPhotoViewController.m @@ -10,6 +10,8 @@ #import "NYTPhoto.h" #import "NYTScalingImageView.h" +#define INTERACTIVE_RELOAD + #ifdef ANIMATED_GIF_SUPPORT #import #endif @@ -202,4 +204,28 @@ - (void)scrollViewDidEndZooming:(UIScrollView *)scrollView withView:(UIView *)vi } } +#ifdef INTERACTIVE_RELOAD +- (void)scrollViewDidScroll:(UIScrollView *)scrollView { + NYTScalingImageView *scalingImageView = self.scalingImageView; + UIImageView *imageView = scalingImageView.imageView; + + CGFloat imageHeight = CGRectGetWidth(imageView.frame) * CGRectGetHeight(imageView.frame) / CGRectGetWidth(imageView.frame); + CGFloat imageWidth = CGRectGetHeight(imageView.frame) * CGRectGetWidth(imageView.frame) / CGRectGetHeight(imageView.frame); + + BOOL imageHeightSmallerThanContent = (imageHeight < scalingImageView.frame.size.height) ? YES : NO; + BOOL imageWidthSmallerThanContent = (imageWidth < scalingImageView.frame.size.width) ? YES : NO; + + CGFloat topOffset = (imageView.frame.size.height - imageHeight) / 2; + CGFloat leftOffset = (imageView.frame.size.width - imageWidth) / 2; + + if (imageHeightSmallerThanContent) { + topOffset = topOffset - ((scalingImageView.frame.size.height - imageHeight)/2); + } + + if (imageWidthSmallerThanContent) { + leftOffset = leftOffset - ((scalingImageView.frame.size.width - imageWidth)/2); + } + scalingImageView.contentInset = UIEdgeInsetsMake(topOffset * -1, leftOffset * -1, topOffset * -1, leftOffset * -1); +} +#endif @end diff --git a/NYTPhotoViewer/NYTPhotosOverlayView.h b/NYTPhotoViewer/NYTPhotosOverlayView.h index cca8d9d4..1839752c 100644 --- a/NYTPhotoViewer/NYTPhotosOverlayView.h +++ b/NYTPhotoViewer/NYTPhotosOverlayView.h @@ -7,6 +7,7 @@ // @import UIKit; +@class NYTLoadingIndicatorView; NS_ASSUME_NONNULL_BEGIN @@ -55,6 +56,16 @@ NS_ASSUME_NONNULL_BEGIN */ @property (nonatomic, nullable) UIView *captionView; +/** + * Enables or disables gradient behind UINavigaitonBar + */ +@property (nonatomic, assign, getter=hasNavigationBarGradient) BOOL navigationBarGradient; + +/** + * Loading indicator shown at the top + */ +@property (nonatomic, nullable) NYTLoadingIndicatorView *loadingIndicatorView; + @end NS_ASSUME_NONNULL_END diff --git a/NYTPhotoViewer/NYTPhotosOverlayView.m b/NYTPhotoViewer/NYTPhotosOverlayView.m index fc001066..f22b76fe 100644 --- a/NYTPhotoViewer/NYTPhotosOverlayView.m +++ b/NYTPhotoViewer/NYTPhotosOverlayView.m @@ -8,11 +8,13 @@ #import "NYTPhotosOverlayView.h" #import "NYTPhotoCaptionViewLayoutWidthHinting.h" +#import "NYTLoadingIndicatorView.h" @interface NYTPhotosOverlayView () @property (nonatomic) UINavigationItem *navigationItem; @property (nonatomic) UINavigationBar *navigationBar; +@property (nonatomic) CAGradientLayer *gradientLayer; @end @@ -25,8 +27,9 @@ - (instancetype)initWithFrame:(CGRect)frame { if (self) { [self setupNavigationBar]; + [self setupLoadingIndicator]; } - + return self; } @@ -50,13 +53,19 @@ - (void)layoutSubviews { }]; [super layoutSubviews]; + [self layoutNavbarGradient]; + self.loadingIndicatorView.frame = (CGRect){0, 0, self.bounds.size.width, 2}; if ([self.captionView conformsToProtocol:@protocol(NYTPhotoCaptionViewLayoutWidthHinting)]) { [(id) self.captionView setPreferredMaxLayoutWidth:self.bounds.size.width]; } } #pragma mark - NYTPhotosOverlayView +- (void)setupLoadingIndicator { + self.loadingIndicatorView = [[NYTLoadingIndicatorView alloc] init]; + [self addSubview:self.loadingIndicatorView]; +} - (void)setupNavigationBar { self.navigationBar = [[UINavigationBar alloc] init]; @@ -80,6 +89,19 @@ - (void)setupNavigationBar { [self addConstraints:@[topConstraint, widthConstraint, horizontalPositionConstraint]]; } +- (void)setupGradient { + self.gradientLayer = [CAGradientLayer layer]; + self.gradientLayer.frame = self.navigationBar.layer.bounds; + self.gradientLayer.colors = [NSArray arrayWithObjects:(id)[[UIColor blackColor] colorWithAlphaComponent:0.85].CGColor, (id)[UIColor clearColor].CGColor, nil]; + [self.navigationBar.layer insertSublayer:self.gradientLayer atIndex:1]; +} + +- (void)layoutNavbarGradient { + if (self.gradientLayer) { + self.gradientLayer.frame = self.navigationBar.bounds; + } +} + - (void)setCaptionView:(UIView *)captionView { if (self.captionView == captionView) { return; @@ -146,4 +168,14 @@ - (void)setTitleTextAttributes:(NSDictionary *)titleTextAttributes { self.navigationBar.titleTextAttributes = titleTextAttributes; } +- (void)setNavigationBarGradient:(BOOL)navigationBarGradient { + if (_navigationBarGradient) { + [self.gradientLayer removeFromSuperlayer]; + } + if (navigationBarGradient) { + [self setupGradient]; + } + _navigationBarGradient = navigationBarGradient; +} + @end diff --git a/NYTPhotoViewer/NYTScalingImageView.m b/NYTPhotoViewer/NYTScalingImageView.m index 596d089c..e9c78a7b 100644 --- a/NYTPhotoViewer/NYTScalingImageView.m +++ b/NYTPhotoViewer/NYTScalingImageView.m @@ -14,6 +14,12 @@ #import #endif +#define INTERACTIVE_RELOAD + +#ifdef INTERACTIVE_RELOAD +#define IsValidZoomScale(zoomScale) (zoomScale != 0 && zoomScale != 1) +#endif + @interface NYTScalingImageView () - (instancetype)initWithCoder:(NSCoder *)aDecoder NS_DESIGNATED_INITIALIZER; @@ -23,6 +29,10 @@ - (instancetype)initWithCoder:(NSCoder *)aDecoder NS_DESIGNATED_INITIALIZER; #else @property (nonatomic) UIImageView *imageView; #endif + +@property (nonatomic) CGFloat baseZoomScale; +@property (nonatomic) CGSize baseSize; + @end @implementation NYTScalingImageView @@ -117,7 +127,9 @@ - (void)updateImage:(UIImage *)image imageData:(NSData *)imageData { UIImage *imageToUse = image ?: [UIImage imageWithData:imageData]; // Remove any transform currently applied by the scroll view zooming. +#ifndef INTERACTIVE_RELOAD self.imageView.transform = CGAffineTransformIdentity; +#endif self.imageView.image = imageToUse; #ifdef ANIMATED_GIF_SUPPORT @@ -125,12 +137,23 @@ - (void)updateImage:(UIImage *)image imageData:(NSData *)imageData { self.imageView.animatedImage = [[FLAnimatedImage alloc] initWithAnimatedGIFData:imageData]; #endif +#ifndef INTERACTIVE_RELOAD self.imageView.frame = CGRectMake(0, 0, imageToUse.size.width, imageToUse.size.height); +#endif self.contentSize = imageToUse.size; +#ifdef INTERACTIVE_RELOAD + self.baseZoomScale = self.zoomScale; + if (CGSizeEqualToSize(self.baseSize, CGSizeZero)) { + self.baseSize = image.size; + } +#endif [self updateZoomScale]; + +#ifndef INTERACTIVE_RELOAD [self centerScrollViewContents]; +#endif } - (void)setupImageScrollView { @@ -149,22 +172,37 @@ - (void)updateZoomScale { #endif CGRect scrollViewFrame = self.bounds; +#ifdef INTERACTIVE_RELOAD + CGFloat scaleWidth = scrollViewFrame.size.width / self.baseSize.width; + CGFloat scaleHeight = scrollViewFrame.size.height / self.baseSize.height; +#else CGFloat scaleWidth = scrollViewFrame.size.width / self.imageView.image.size.width; CGFloat scaleHeight = scrollViewFrame.size.height / self.imageView.image.size.height; +#endif CGFloat minScale = MIN(scaleWidth, scaleHeight); self.minimumZoomScale = minScale; self.maximumZoomScale = MAX(minScale, self.maximumZoomScale); +#ifdef INTERACTIVE_RELOAD + if (IsValidZoomScale(self.baseZoomScale)) { + self.zoomScale = self.baseZoomScale; + } else { + self.zoomScale = self.minimumZoomScale; + } + self.baseZoomScale = self.zoomScale; +#else self.zoomScale = self.minimumZoomScale; - +#endif // scrollView.panGestureRecognizer.enabled is on by default and enabled by // viewWillLayoutSubviews in the container controller so disable it here // to prevent an interference with the container controller's pan gesture. // // This is enabled in scrollViewWillBeginZooming so panning while zoomed-in // is unaffected. +#ifndef INTERACTIVE_RELOAD self.panGestureRecognizer.enabled = NO; +#endif } } @@ -175,11 +213,19 @@ - (void)centerScrollViewContents { CGFloat verticalInset = 0; if (self.contentSize.width < CGRectGetWidth(self.bounds)) { +#ifdef INTERACTIVE_RELOAD + horizontalInset = (CGRectGetWidth(self.bounds) - self.baseSize.width) * 0.5; +#else horizontalInset = (CGRectGetWidth(self.bounds) - self.contentSize.width) * 0.5; +#endif } if (self.contentSize.height < CGRectGetHeight(self.bounds)) { +#ifdef INTERACTIVE_RELOAD + verticalInset = (CGRectGetHeight(self.bounds) - self.baseSize.height) * 0.5; +#else verticalInset = (CGRectGetHeight(self.bounds) - self.contentSize.height) * 0.5; +#endif } if (self.window.screen.scale < 2.0) { @@ -189,6 +235,13 @@ - (void)centerScrollViewContents { // Use `contentInset` to center the contents in the scroll view. Reasoning explained here: http://petersteinberger.com/blog/2013/how-to-center-uiscrollview/ self.contentInset = UIEdgeInsetsMake(verticalInset, horizontalInset, verticalInset, horizontalInset); + +#ifdef INTERACTIVE_RELOAD + // Fixes image jumping issue when loading full size image + if ([self.delegate respondsToSelector:@selector(scrollViewDidScroll:)]) { + [self.delegate scrollViewDidScroll:self]; + } +#endif } @end diff --git a/Pod/Classes/ios/NYTLoadingIndicatorView.h b/Pod/Classes/ios/NYTLoadingIndicatorView.h new file mode 100644 index 00000000..6fd1d37b --- /dev/null +++ b/Pod/Classes/ios/NYTLoadingIndicatorView.h @@ -0,0 +1,18 @@ +// +// NYTLoadingIndicatorView.h +// Pods +// +// Created by Marcus Kida on 15/01/2016. +// +// + +#import + +@interface NYTLoadingIndicatorView : UIView + +/** + * The progress to be shown, ranging 0.0 to 1.0 + */ +@property (nonatomic, assign) CGFloat progress; + +@end diff --git a/Pod/Classes/ios/NYTLoadingIndicatorView.m b/Pod/Classes/ios/NYTLoadingIndicatorView.m new file mode 100644 index 00000000..3407c61c --- /dev/null +++ b/Pod/Classes/ios/NYTLoadingIndicatorView.m @@ -0,0 +1,71 @@ +// +// NYTLoadingIndicatorView.m +// Pods +// +// Created by Marcus Kida on 15/01/2016. +// +// + +#import "NYTLoadingIndicatorView.h" + +@interface NYTLoadingIndicatorView () + +@property (nonatomic) UIView *indicatorView; + +@end + +@implementation NYTLoadingIndicatorView + +- (instancetype)init { + self = [super init]; + if (self) { + [self setup]; + } + return self; +} + +- (instancetype)initWithFrame:(CGRect)frame { + self = [super initWithFrame:frame]; + if (self) { + [self setup]; + } + return self; +} + +- (void)setup { + self.backgroundColor = [UIColor clearColor]; + self.indicatorView = [[UIView alloc] init]; + self.indicatorView.backgroundColor = [UIColor whiteColor]; + [self addSubview:self.indicatorView]; +} + +- (void)setFrame:(CGRect)frame { + [super setFrame:frame]; + [self updateIndicatorView]; +} + +- (void)resetIndicatorHidden:(BOOL)hidden { + self.indicatorView.frame = (CGRect){0, 0, 0, self.bounds.size.height}; + self.indicatorView.hidden = hidden; +} + +- (void)updateIndicatorView { + if (self.progress == 0.0f) { + return [self resetIndicatorHidden:NO]; + } + if (self.progress >= 1.0f) { + return [self resetIndicatorHidden:YES]; + } + self.indicatorView.frame = (CGRect){0, 0, [self progressWidth:self.progress], self.bounds.size.height}; +} + +- (CGFloat)progressWidth:(CGFloat)progress { + return (self.bounds.size.width / 100) * (progress * 100); +} + +- (void)setProgress:(CGFloat)progress { + _progress = progress; + [self updateIndicatorView]; +} + +@end