diff --git a/BFRImageViewController/BFRBackLoadedImageSource.m b/BFRImageViewController/BFRBackLoadedImageSource.m index 7b1ce39..164f778 100644 --- a/BFRImageViewController/BFRBackLoadedImageSource.m +++ b/BFRImageViewController/BFRBackLoadedImageSource.m @@ -25,6 +25,7 @@ @interface BFRBackLoadedImageSource() @implementation BFRBackLoadedImageSource #pragma mark - Initializers + - (instancetype)initWithInitialImage:(UIImage *)image hiResURL:(NSURL *)url { self = [super init]; @@ -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(), ^{ diff --git a/BFRImageViewController/BFRImageContainerViewController.m b/BFRImageViewController/BFRImageContainerViewController.m index a775ac2..afd017b 100644 --- a/BFRImageViewController/BFRImageContainerViewController.m +++ b/BFRImageViewController/BFRImageContainerViewController.m @@ -42,6 +42,7 @@ @interface BFRImageContainerViewController () )transitionContext { return self.animationDuration; } @@ -170,6 +173,7 @@ - (void)performDismissingAnimation:(id)tra } #pragma mark - Transitioning Delegate + - (id )animationControllerForPresentedController:(UIViewController *)presented presentingController:(UIViewController *)presenting sourceController:(UIViewController *)source { return self; } diff --git a/BFRImageViewController/BFRImageViewController.m b/BFRImageViewController/BFRImageViewController.m index c28d9c9..756c09b 100644 --- a/BFRImageViewController/BFRImageViewController.m +++ b/BFRImageViewController/BFRImageViewController.m @@ -12,7 +12,7 @@ #import "BFRImageTransitionAnimator.h" #import "BFRImageViewerConstants.h" -@interface BFRImageViewController () +@interface BFRImageViewController () /*! 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; @@ -35,11 +35,15 @@ @interface BFRImageViewController () /*! 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]; @@ -49,6 +53,7 @@ - (instancetype)initWithImageSource:(NSArray *)images { self.modalTransitionStyle = UIModalTransitionStyleCrossDissolve; self.enableDoneButton = YES; self.showDoneButtonOnLeft = YES; + self.parallaxView = [UIView new]; } return self; @@ -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]; @@ -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]; @@ -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; } @@ -138,6 +162,7 @@ - (UIStatusBarAnimation)preferredStatusBarUpdateAnimation { } #pragma mark - Chrome + - (void)addChromeToUI { if (self.enableDoneButton) { NSBundle *bundle = [NSBundle bundleForClass:[self class]]; @@ -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; @@ -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) { @@ -241,6 +306,7 @@ - (BOOL)prefersHomeIndicatorAutoHidden { } #pragma mark - Memory Considerations + - (void)didReceiveMemoryWarning { [super didReceiveMemoryWarning]; NSLog(@"BFRImageViewer: Dismissing due to memory warning."); diff --git a/BFRImageViewController/BFRImageViewerConstants.h b/BFRImageViewController/BFRImageViewerConstants.h index 23bf764..aa92d20 100644 --- a/BFRImageViewController/BFRImageViewerConstants.h +++ b/BFRImageViewController/BFRImageViewerConstants.h @@ -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 diff --git a/BFRImageViewController/BFRImageViewerConstants.m b/BFRImageViewController/BFRImageViewerConstants.m index be86b4e..5191512 100644 --- a/BFRImageViewController/BFRImageViewerConstants.m +++ b/BFRImageViewController/BFRImageViewerConstants.m @@ -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 diff --git a/BFRImageViewerDemo/BFRImageViewer/ThirdViewController.m b/BFRImageViewerDemo/BFRImageViewer/ThirdViewController.m index 73be809..05f81d6 100644 --- a/BFRImageViewerDemo/BFRImageViewer/ThirdViewController.m +++ b/BFRImageViewerDemo/BFRImageViewer/ThirdViewController.m @@ -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 @@ -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;