Skip to content
Merged
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
2 changes: 2 additions & 0 deletions BFRImageViewController/BFRBackLoadedImageSource.m
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ @interface BFRBackLoadedImageSource()
@implementation BFRBackLoadedImageSource

#pragma mark - Initializers

- (instancetype)initWithInitialImage:(UIImage *)image hiResURL:(NSURL *)url {
self = [super init];

Expand All @@ -39,6 +40,7 @@ - (instancetype)initWithInitialImage:(UIImage *)image hiResURL:(NSURL *)url {
}

#pragma mark - Backloading

- (void)loadHighFidelityImage {
[[PINRemoteImageManager sharedImageManager] downloadImageWithURL:self.url options:PINRemoteImageManagerDisallowAlternateRepresentations progressDownload:nil completion:^(PINRemoteImageManagerResult * _Nonnull result) {
dispatch_async(dispatch_get_main_queue(), ^{
Expand Down
9 changes: 8 additions & 1 deletion BFRImageViewController/BFRImageContainerViewController.m
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ @interface BFRImageContainerViewController () <UIScrollViewDelegate, UIGestureRe
@implementation BFRImageContainerViewController

#pragma mark - Lifecycle

// With peeking and popping, setting up your subviews in loadView will throw an exception
- (void)viewDidLoad {
[super viewDidLoad];
Expand Down Expand Up @@ -117,6 +118,7 @@ - (void)dealloc {
}

#pragma mark - UI Methods

- (UIScrollView *)createScrollView {
UIScrollView *sv = [[UIScrollView alloc] initWithFrame:self.view.bounds];
sv.delegate = self;
Expand Down Expand Up @@ -204,6 +206,7 @@ - (void)addImageToScrollView {
}

#pragma mark - Backloaded Image Notification

- (void)handleHiResImageDownloaded:(NSNotification *)note {
UIImage *hiResImg = note.object;

Expand All @@ -214,13 +217,15 @@ - (void)handleHiResImageDownloaded:(NSNotification *)note {
}

#pragma mark - Gesture Recognizer Delegate

// If we have more than one image, this will cancel out dragging horizontally to make it easy to navigate between images
- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer {
CGPoint velocity = [(UIPanGestureRecognizer *)gestureRecognizer velocityInView:self.scrollView];
return fabs(velocity.y) > fabs(velocity.x);
}

#pragma mark - Scrollview Delegate

- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView {
return self.scrollView.subviews.firstObject;
}
Expand All @@ -231,6 +236,7 @@ - (void)scrollViewDidZoom:(UIScrollView *)scrollView {
}

#pragma mark - Scrollview Util Methods

/*! This calculates the correct zoom scale for the scrollview once we have the image's size */
- (void)setMaxMinZoomScalesForCurrentBounds {
// Sizes
Expand Down Expand Up @@ -342,7 +348,6 @@ - (void)handleDrag:(UIPanGestureRecognizer *)recognizer {
}

- (void)showActivitySheet:(UILongPressGestureRecognizer *)longPress {

UIActivityViewController *activityVC;
if (longPress.state == UIGestureRecognizerStateBegan) {
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone) {
Expand All @@ -362,6 +367,7 @@ - (void)showActivitySheet:(UILongPressGestureRecognizer *)longPress {
}

#pragma mark - Image Asset Retrieval

- (void)retrieveImageFromAsset {
if (![self.imgSrc isKindOfClass:[PHAsset class]]) {
return;
Expand Down Expand Up @@ -418,6 +424,7 @@ - (void)retrieveImageFromURL {
}

#pragma mark - Misc. Methods

- (void)dismissUI {
[[NSNotificationCenter defaultCenter] postNotificationName:NOTE_VC_SHOULD_DISMISS object:nil];
}
Expand Down
4 changes: 4 additions & 0 deletions BFRImageViewController/BFRImageTransitionAnimator.m
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ @interface BFRImageTransitionAnimator()
@implementation BFRImageTransitionAnimator

#pragma mark - Initialization

- (instancetype)init {
self = [super init];

Expand All @@ -50,6 +51,7 @@ - (void)handleCancelCustomTransitionNotification:(NSNotification *)notification
}

#pragma mark - Utils

- (UIImageView *)temporaryImageView {
if (self.animatedImage == nil) return nil;

Expand Down Expand Up @@ -86,6 +88,7 @@ - (CGRect)imageFinalFrameDestinationForImageView:(UIImageView *)imageView inView
}

#pragma mark - Animator delegate

- (NSTimeInterval)transitionDuration:(id<UIViewControllerContextTransitioning>)transitionContext {
return self.animationDuration;
}
Expand Down Expand Up @@ -170,6 +173,7 @@ - (void)performDismissingAnimation:(id<UIViewControllerContextTransitioning>)tra
}

#pragma mark - Transitioning Delegate

- (id <UIViewControllerAnimatedTransitioning>)animationControllerForPresentedController:(UIViewController *)presented presentingController:(UIViewController *)presenting sourceController:(UIViewController *)source {
return self;
}
Expand Down
70 changes: 68 additions & 2 deletions BFRImageViewController/BFRImageViewController.m
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
#import "BFRImageTransitionAnimator.h"
#import "BFRImageViewerConstants.h"

@interface BFRImageViewController () <UIPageViewControllerDataSource>
@interface BFRImageViewController () <UIPageViewControllerDataSource, UIScrollViewDelegate>

/*! This view controller just acts as a container to hold a page view controller, which pages between the view controllers that hold an image. */
@property (strong, nonatomic, nonnull) UIPageViewController *pagerVC;
Expand All @@ -35,11 +35,15 @@ @interface BFRImageViewController () <UIPageViewControllerDataSource>
/*! This is used for nothing more than to defer the hiding of the status bar until the view appears to avoid any awkward jumps in the presenting view. */
@property (nonatomic, getter=shouldHideStatusBar) BOOL hideStatusBar;

/*! This creates the parallax scrolling effect by essentially clipping the scrolled images and moving with the touch point in scrollViewDidScroll. */
@property (strong, nonatomic, nonnull) UIView *parallaxView;

@end

@implementation BFRImageViewController

#pragma mark - Initializers

- (instancetype)initWithImageSource:(NSArray *)images {
self = [super init];

Expand All @@ -49,6 +53,7 @@ - (instancetype)initWithImageSource:(NSArray *)images {
self.modalTransitionStyle = UIModalTransitionStyleCrossDissolve;
self.enableDoneButton = YES;
self.showDoneButtonOnLeft = YES;
self.parallaxView = [UIView new];
}

return self;
Expand All @@ -64,12 +69,14 @@ - (instancetype)initForPeekWithImageSource:(NSArray *)images {
self.enableDoneButton = YES;
self.showDoneButtonOnLeft = YES;
self.usedFor3DTouch = YES;
self.parallaxView = [UIView new];
}

return self;
}

#pragma mark - View Lifecycle

- (void)viewDidLoad {
[super viewDidLoad];

Expand Down Expand Up @@ -106,6 +113,22 @@ - (void)viewDidLoad {
[[self view] addSubview:[self.pagerVC view]];
[self.pagerVC didMoveToParentViewController:self];

// Attach to pager controller's scrollview for parallax effect when swiping between images
for (UIView *subview in self.pagerVC.view.subviews) {
if ([subview isKindOfClass:[UIScrollView class]]) {
((UIScrollView *)subview).delegate = self;
self.parallaxView.backgroundColor = self.view.backgroundColor;
self.parallaxView.hidden = YES;
[subview addSubview:self.parallaxView];

CGRect parallaxSeparatorFrame = CGRectZero;
parallaxSeparatorFrame.size = [self sizeForParallaxView];
self.parallaxView.frame = parallaxSeparatorFrame;

break;
}
}

// Add chrome to UI now if we aren't waiting to be peeked into
if (!self.isBeingUsedFor3DTouch) {
[self addChromeToUI];
Expand All @@ -123,12 +146,13 @@ - (void)viewWillAppear:(BOOL)animated {
}];
}

- (void)viewWillLayoutSubviews {
- (void)viewDidLayoutSubviews {
[super viewWillLayoutSubviews];
[self updateChromeFrames];
}

#pragma mark - Status bar

- (BOOL)prefersStatusBarHidden{
return self.shouldHideStatusBar;
}
Expand All @@ -138,6 +162,7 @@ - (UIStatusBarAnimation)preferredStatusBarUpdateAnimation {
}

#pragma mark - Chrome

- (void)addChromeToUI {
if (self.enableDoneButton) {
NSBundle *bundle = [NSBundle bundleForClass:[self class]];
Expand Down Expand Up @@ -167,9 +192,12 @@ - (void)updateChromeFrames {

self.doneButton.frame = CGRectMake(buttonX, closeButtonY, 17, 17);
}

self.parallaxView.hidden = YES;
}

#pragma mark - Pager Datasource

- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerBeforeViewController:(UIViewController *)viewController {
NSUInteger index = ((BFRImageContainerViewController *)viewController).pageIndex;

Expand Down Expand Up @@ -200,7 +228,44 @@ - (UIViewController *)pageViewController:(UIPageViewController *)pageViewControl
return vc;
}

#pragma mark - Scrollview Delegate + Parallax Effect

- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView {
self.parallaxView.hidden = NO;
}

- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
[self updateParallaxViewFrame:scrollView];
}

- (void)updateParallaxViewFrame:(UIScrollView *)scrollView {
CGRect bounds = scrollView.bounds;
CGRect parallaxSeparatorFrame = self.parallaxView.frame;

CGPoint offset = bounds.origin;
CGFloat pageWidth = bounds.size.width;

NSInteger firstPageIndex = floorf(CGRectGetMinX(bounds) / pageWidth);

CGFloat x = offset.x - pageWidth * firstPageIndex;
CGFloat percentage = x / pageWidth;

parallaxSeparatorFrame.origin.x = pageWidth * (firstPageIndex + 1) - parallaxSeparatorFrame.size.width * percentage;

self.parallaxView.frame = parallaxSeparatorFrame;
}

- (CGSize)sizeForParallaxView {
CGSize parallaxSeparatorSize = CGSizeZero;

parallaxSeparatorSize.width = PARALLAX_EFFECT_WIDTH * 2;
parallaxSeparatorSize.height = self.view.bounds.size.height;

return parallaxSeparatorSize;
}

#pragma mark - Utility methods

- (void)dismiss {
// If we dismiss from a different image than what was animated in - don't do the custom dismiss transition animation
if (self.startingIndex != ((BFRImageContainerViewController *)self.pagerVC.viewControllers.firstObject).pageIndex) {
Expand Down Expand Up @@ -241,6 +306,7 @@ - (BOOL)prefersHomeIndicatorAutoHidden {
}

#pragma mark - Memory Considerations

- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
NSLog(@"BFRImageViewer: Dismissing due to memory warning.");
Expand Down
3 changes: 3 additions & 0 deletions BFRImageViewController/BFRImageViewerConstants.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,7 @@ extern NSString * const GENERAL_OK;
extern NSString * const HI_RES_IMG_ERROR_DOMAIN;
extern NSInteger const HI_RES_IMG_ERROR_CODE;

// Misc
extern NSInteger const PARALLAX_EFFECT_WIDTH;

@end
2 changes: 2 additions & 0 deletions BFRImageViewController/BFRImageViewerConstants.m
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,6 @@ @implementation BFRImageViewerConstants
NSString * const HI_RES_IMG_ERROR_DOMAIN = @"com.bfrImageViewer.backLoadedImgSource";
NSInteger const HI_RES_IMG_ERROR_CODE = 44;

NSInteger const PARALLAX_EFFECT_WIDTH = 20;

@end
6 changes: 3 additions & 3 deletions BFRImageViewerDemo/BFRImageViewer/ThirdViewController.m
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,8 @@ - (void)viewDidLoad {

// To use the custom transition animation with BFRImageViewer
// 1) Have an instance of BFRImageTransitionAnimator around
// 2) Set it's aniamtedImage, animatedImageContainer and imageOriginFrame. Optionally, set the desiredContentMode
// 3) When you present the BFRImageViewController, set it's transitioningDelegate to your BFRImageTransitionAnimator instance.
// 2) Set its animatedImage, animatedImageContainer and imageOriginFrame. Optionally, set the desiredContentMode
// 3) When you present the BFRImageViewController, set its transitioningDelegate to your BFRImageTransitionAnimator instance.
// You can see all of this in action in openImageViewerWithTransition below

// Object to create all the animations
Expand Down Expand Up @@ -68,7 +68,7 @@ - (void)openImageViewerWithTransition {
self.imageViewAnimator.animatedImageContainer = self.imageView;
// The image that will be animated
self.imageViewAnimator.animatedImage = self.imageView.image;
// The rect the image will aniamte to and from
// The rect the image will animate to and from
self.imageViewAnimator.imageOriginFrame = self.imageView.frame;
// Optional - but you'll want this to match the view's content mode that the image is housed in
self.imageViewAnimator.desiredContentMode = self.imageView.contentMode;
Expand Down