Skip to content
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

Proposal: Interactive reload and loading indicator #160

Open
wants to merge 13 commits into
base: develop
Choose a base branch
from
2 changes: 1 addition & 1 deletion NYTPhotoViewer.podspec
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down
26 changes: 26 additions & 0 deletions NYTPhotoViewer/NYTPhotoViewController.m
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
#import "NYTPhoto.h"
#import "NYTScalingImageView.h"

#define INTERACTIVE_RELOAD

#ifdef ANIMATED_GIF_SUPPORT
#import <FLAnimatedImage/FLAnimatedImage.h>
#endif
Expand Down Expand Up @@ -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);
Copy link
Contributor

@cdzombak cdzombak Feb 28, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the goal, in this calculation, of performing GetHeight * GetWidth / GetHeight? Isn't that equivalent to just GetWidth?


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
11 changes: 11 additions & 0 deletions NYTPhotoViewer/NYTPhotosOverlayView.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
//

@import UIKit;
@class NYTLoadingIndicatorView;

NS_ASSUME_NONNULL_BEGIN

Expand Down Expand Up @@ -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
34 changes: 33 additions & 1 deletion NYTPhotoViewer/NYTPhotosOverlayView.m
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -25,8 +27,9 @@ - (instancetype)initWithFrame:(CGRect)frame {

if (self) {
[self setupNavigationBar];
[self setupLoadingIndicator];
}

return self;
}

Expand All @@ -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<NYTPhotoCaptionViewLayoutWidthHinting>) 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];
Expand All @@ -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;
Expand Down Expand Up @@ -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
55 changes: 54 additions & 1 deletion NYTPhotoViewer/NYTScalingImageView.m
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,12 @@
#import <FLAnimatedImage/FLAnimatedImage.h>
#endif

#define INTERACTIVE_RELOAD

#ifdef INTERACTIVE_RELOAD
#define IsValidZoomScale(zoomScale) (zoomScale != 0 && zoomScale != 1)
#endif

@interface NYTScalingImageView ()

- (instancetype)initWithCoder:(NSCoder *)aDecoder NS_DESIGNATED_INITIALIZER;
Expand All @@ -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
Expand Down Expand Up @@ -117,20 +127,33 @@ - (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
// It's necessarry to first assign the UIImage so calulations for layout go right (see above)
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 {
Expand All @@ -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
}
}

Expand All @@ -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) {
Expand All @@ -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
18 changes: 18 additions & 0 deletions Pod/Classes/ios/NYTLoadingIndicatorView.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
//
// NYTLoadingIndicatorView.h
// Pods
//
// Created by Marcus Kida on 15/01/2016.
//
//

#import <Foundation/Foundation.h>

@interface NYTLoadingIndicatorView : UIView

/**
* The progress to be shown, ranging 0.0 to 1.0
*/
@property (nonatomic, assign) CGFloat progress;

@end
71 changes: 71 additions & 0 deletions Pod/Classes/ios/NYTLoadingIndicatorView.m
Original file line number Diff line number Diff line change
@@ -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