diff --git a/BFRImageViewController/BFRBackLoadedImageSource.h b/BFRImageViewController/BFRBackLoadedImageSource.h index cded45e..204e337 100644 --- a/BFRImageViewController/BFRBackLoadedImageSource.h +++ b/BFRImageViewController/BFRBackLoadedImageSource.h @@ -9,14 +9,16 @@ #import #import +typedef void(^onHiResDownloadComplete)(UIImage * _Nullable, NSError * _Nullable); + /*! This class allows you to show an image that you already have available initially, while loading a higher fidelity version in the background which will replace the lower fidelity one. This class assumes that the new image will have the same aspect ratio as the old one. */ @interface BFRBackLoadedImageSource : NSObject /*! The image that is available for use right away. */ @property (strong, nonatomic, readonly, nonnull) UIImage *image; -/*! This is called on the main thread when the higher resolution image is finished loading. */ -@property (copy) void (^ _Nonnull onHighResImageLoaded)(UIImage * _Nullable highResImage); +/*! This is called on the main thread when the higher resolution image is finished loading. Assign to this if you wish to do any specific logic when the download completes. NOTE: Do not attempt to assign the image to any @c BFRImageContainerViewController, this is done for you. Use this block soley for any other business logic you might have to carry out. */ +@property (copy) onHiResDownloadComplete _Nullable onCompletion; /*! Use initWithInitialImage:hiResURL instead. */ - (instancetype _Nullable)init NS_UNAVAILABLE; diff --git a/BFRImageViewController/BFRBackLoadedImageSource.m b/BFRImageViewController/BFRBackLoadedImageSource.m index 4a8254c..7b1ce39 100644 --- a/BFRImageViewController/BFRBackLoadedImageSource.m +++ b/BFRImageViewController/BFRBackLoadedImageSource.m @@ -7,6 +7,7 @@ // #import "BFRBackLoadedImageSource.h" +#import "BFRImageViewerConstants.h" #import #import #import @@ -41,15 +42,19 @@ - (instancetype)initWithInitialImage:(UIImage *)image hiResURL:(NSURL *)url { - (void)loadHighFidelityImage { [[PINRemoteImageManager sharedImageManager] downloadImageWithURL:self.url options:PINRemoteImageManagerDisallowAlternateRepresentations progressDownload:nil completion:^(PINRemoteImageManagerResult * _Nonnull result) { dispatch_async(dispatch_get_main_queue(), ^{ - if (result.image) { - if (self.onHighResImageLoaded != nil) { - dispatch_async(dispatch_get_main_queue(), ^ { - self.onHighResImageLoaded(result.image); - }); + if (self.onCompletion != nil) { + if (result.image) { + self.onCompletion(result.image, nil); + } else { + NSLog(@"BFRImageViewer: Unable to load high resolution photo via backloading."); + NSError *downloadError = [NSError errorWithDomain:HI_RES_IMG_ERROR_DOMAIN + code:HI_RES_IMG_ERROR_CODE + userInfo:@{NSLocalizedFailureReasonErrorKey:[NSString stringWithFormat:@"Failed to download an image for high resolution url %@", self.url.absoluteString]}]; + self.onCompletion(nil, downloadError); } - } else { - NSLog(@"BFRImageViewer: Unable to load high resolution photo via backloading."); } + + [[NSNotificationCenter defaultCenter] postNotificationName:NOTE_HI_RES_IMG_DOWNLOADED object:result.image]; }); }]; } diff --git a/BFRImageViewController/BFRImageContainerViewController.h b/BFRImageViewController/BFRImageContainerViewController.h index eef43be..4a4270b 100644 --- a/BFRImageViewController/BFRImageContainerViewController.h +++ b/BFRImageViewController/BFRImageContainerViewController.h @@ -20,9 +20,12 @@ /*! A helper integer to simplify using this view controller inside a @c UIPagerViewController when swiping between views. */ @property (nonatomic, assign) NSUInteger pageIndex; -/*! Assigning YES to this property will make the background transparent. */ +/*! Assigning YES to this property will make the background transparent. You typically don't set this property yourself, instead, the value is derived from the containing @c BFRImageViewController instance. */ @property (nonatomic, getter=isUsingTransparentBackground) BOOL useTransparentBackground; +/*! Assigning YES to this property will disable long pressing media to present the activity view controller. You typically don't set this property yourself, instead, the value is derived from the containing @c BFRImageViewController instance. */ +@property (nonatomic, getter=shouldDisableSharingLongPress) BOOL disableSharingLongPress; + /*! If there is more than one image in the containing @c BFRImageViewController - this property is set to YES to make swiping from image to image easier. */ @property (nonatomic, getter=shouldDisableHorizontalDrag) BOOL disableHorizontalDrag; diff --git a/BFRImageViewController/BFRImageContainerViewController.m b/BFRImageViewController/BFRImageContainerViewController.m index c943fce..a775ac2 100644 --- a/BFRImageViewController/BFRImageContainerViewController.m +++ b/BFRImageViewController/BFRImageContainerViewController.m @@ -8,6 +8,7 @@ #import "BFRImageContainerViewController.h" #import "BFRBackLoadedImageSource.h" +#import "BFRImageViewerConstants.h" #import #import #import @@ -53,6 +54,10 @@ - (void)viewDidLoad { self.scrollView = [self createScrollView]; [self.view addSubview:self.scrollView]; + // Animator - used to snap the image back to the center when done dragging + self.animator = [[UIDynamicAnimator alloc] initWithReferenceView:self.scrollView]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handlePop) name:NOTE_VC_POPPED object:nil]; + // Fetch image - or just display it if ([self.imgSrc isKindOfClass:[NSURL class]]) { self.progressView = [self createProgressView]; @@ -74,21 +79,12 @@ - (void)viewDidLoad { [self.view addSubview:self.progressView]; [self retrieveImageFromURL]; } else if ([self.imgSrc isKindOfClass:[BFRBackLoadedImageSource class]]) { + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleHiResImageDownloaded:) name:NOTE_HI_RES_IMG_DOWNLOADED object:nil]; self.imgLoaded = ((BFRBackLoadedImageSource *)self.imgSrc).image; [self addImageToScrollView]; - - __weak BFRImageContainerViewController *weakSelf = self; - ((BFRBackLoadedImageSource *)self.imgSrc).onHighResImageLoaded = ^ (UIImage *highResImage) { - weakSelf.imgLoaded = highResImage; - weakSelf.imgView.image = weakSelf.imgLoaded; - }; } else { [self showError]; } - - // Animator - used to snap the image back to the center when done dragging - self.animator = [[UIDynamicAnimator alloc] initWithReferenceView:self.scrollView]; - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handlePop) name:@"ViewControllerPopped" object:nil]; } - (void)viewWillLayoutSubviews { @@ -180,12 +176,14 @@ - (FLAnimatedImageView *)createImageView { [resizableImageView addGestureRecognizer:doubleImgTap]; // Share options - UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(showActivitySheet:)]; - [resizableImageView addGestureRecognizer:longPress]; + if (self.shouldDisableSharingLongPress == NO) { + UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(showActivitySheet:)]; + [resizableImageView addGestureRecognizer:longPress]; + [singleImgTap requireGestureRecognizerToFail:longPress]; + } // Ensure the single tap doesn't fire when a user attempts to double tap [singleImgTap requireGestureRecognizerToFail:doubleImgTap]; - [singleImgTap requireGestureRecognizerToFail:longPress]; // Dragging to dismiss UIPanGestureRecognizer *panImg = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handleDrag:)]; @@ -205,6 +203,16 @@ - (void)addImageToScrollView { } } +#pragma mark - Backloaded Image Notification +- (void)handleHiResImageDownloaded:(NSNotification *)note { + UIImage *hiResImg = note.object; + + if (hiResImg && [hiResImg isKindOfClass:[UIImage class]]) { + self.imgLoaded = hiResImg; + self.imgView.image = self.imgLoaded; + } +} + #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 { @@ -411,18 +419,18 @@ - (void)retrieveImageFromURL { #pragma mark - Misc. Methods - (void)dismissUI { - [[NSNotificationCenter defaultCenter] postNotificationName:@"DismissUI" object:nil]; + [[NSNotificationCenter defaultCenter] postNotificationName:NOTE_VC_SHOULD_DISMISS object:nil]; } - (void)dimissUIFromDraggingGesture { // If we drag the image away to close things, don't do the custom dismissal transition - [[NSNotificationCenter defaultCenter] postNotificationName:@"DimissUIFromDraggingGesture" object:nil]; + [[NSNotificationCenter defaultCenter] postNotificationName:NOTE_VC_SHOULD_DISMISS_FROM_DRAGGING object:nil]; } - (void)showError { - UIAlertController *controller = [UIAlertController alertControllerWithTitle:@"Whoops" message:@"Looks like we ran into an issue loading the image, sorry about that!" preferredStyle:UIAlertControllerStyleAlert]; - UIAlertAction *closeAction = [UIAlertAction actionWithTitle:@"Ok" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action){ - [[NSNotificationCenter defaultCenter] postNotificationName:@"ImageLoadingError" object:nil]; + UIAlertController *controller = [UIAlertController alertControllerWithTitle:ERROR_TITLE message:ERROR_MESSAGE preferredStyle:UIAlertControllerStyleAlert]; + UIAlertAction *closeAction = [UIAlertAction actionWithTitle:GENERAL_OK style:UIAlertActionStyleDefault handler:^(UIAlertAction *action){ + [[NSNotificationCenter defaultCenter] postNotificationName:NOTE_IMG_FAILED object:nil]; }]; [controller addAction:closeAction]; [self presentViewController:controller animated:YES completion:nil]; diff --git a/BFRImageViewController/BFRImageTransitionAnimator.m b/BFRImageViewController/BFRImageTransitionAnimator.m index 51315d0..c50a63f 100644 --- a/BFRImageViewController/BFRImageTransitionAnimator.m +++ b/BFRImageViewController/BFRImageTransitionAnimator.m @@ -7,6 +7,7 @@ // #import "BFRImageTransitionAnimator.h" +#import "BFRImageViewerConstants.h" @interface BFRImageTransitionAnimator() @@ -32,7 +33,7 @@ - (instancetype)init { self.desiredContentMode = UIViewContentModeScaleAspectFill; self.animationDuration = DEFAULT_ANIMATION_DURATION; self.dismissWithoutCustomTransition = NO; - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleCancelCustomTransitionNotification:) name:@"CancelCustomDismissalTransition" object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleCancelCustomTransitionNotification:) name:NOTE_VC_SHOULD_CANCEL_CUSTOM_TRANSITION object:nil]; } return self; diff --git a/BFRImageViewController/BFRImageViewController.h b/BFRImageViewController/BFRImageViewController.h index 6551f3d..eaee0f5 100644 --- a/BFRImageViewController/BFRImageViewController.h +++ b/BFRImageViewController/BFRImageViewController.h @@ -18,9 +18,12 @@ /*! Initializes an instance of @C BFRImageViewController from the image source provided. The array can contain a mix of @c NSURL, @c UIImage, @c PHAsset, or @c NSStrings of URLS. This can be a mix of all these types, or just one. Additionally, this customizes the user interface to defer showing some of its user interface elements, such as the close button, until it's been fully popped.*/ - (instancetype _Nullable)initForPeekWithImageSource:(NSArray * _Nonnull)images; -/*! Assigning YES to this property will make the background transparent. */ +/*! Assigning YES to this property will make the background transparent. Default is NO. */ @property (nonatomic, getter=isUsingTransparentBackground) BOOL useTransparentBackground; +/*! Assigning YES to this property will disable long pressing media to present the activity view controller. Default is NO. */ +@property (nonatomic, getter=shouldDisableSharingLongPress) BOOL disableSharingLongPress; + /*! Flag property that toggles the doneButton. Defaults to YES */ @property (nonatomic) BOOL enableDoneButton; diff --git a/BFRImageViewController/BFRImageViewController.m b/BFRImageViewController/BFRImageViewController.m index 95b4c85..c28d9c9 100644 --- a/BFRImageViewController/BFRImageViewController.m +++ b/BFRImageViewController/BFRImageViewController.m @@ -10,6 +10,7 @@ #import "BFRImageContainerViewController.h" #import "BFRImageViewerLocalizations.h" #import "BFRImageTransitionAnimator.h" +#import "BFRImageViewerConstants.h" @interface BFRImageViewController () @@ -88,6 +89,7 @@ - (void)viewDidLoad { imgVC.pageIndex = self.startingIndex; imgVC.usedFor3DTouch = self.isBeingUsedFor3DTouch; imgVC.useTransparentBackground = self.isUsingTransparentBackground; + imgVC.disableSharingLongPress = self.shouldDisableSharingLongPress; imgVC.disableHorizontalDrag = (self.images.count > 1); [self.imageViewControllers addObject:imgVC]; } @@ -211,7 +213,7 @@ - (void)dismiss { } - (void)dismissWithoutCustomAnimation { - [[NSNotificationCenter defaultCenter] postNotificationName:@"CancelCustomDismissalTransition" object:@(1)]; + [[NSNotificationCenter defaultCenter] postNotificationName:NOTE_VC_SHOULD_CANCEL_CUSTOM_TRANSITION object:@(1)]; self.modalTransitionStyle = UIModalTransitionStyleCrossDissolve; [self dismissViewControllerAnimated:YES completion:nil]; @@ -228,10 +230,10 @@ - (void)handleDoneAction { /*! The images and scrollview are not part of this view controller, so instances of @c BFRimageContainerViewController will post notifications when they are touched for things to happen. */ - (void)registerNotifcations { - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(dismiss) name:@"DismissUI" object:nil]; - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(dismiss) name:@"ImageLoadingError" object:nil]; - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handlePop) name:@"ViewControllerPopped" object:nil]; - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(dismissWithoutCustomAnimation) name:@"DimissUIFromDraggingGesture" object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(dismiss) name:NOTE_VC_SHOULD_DISMISS object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(dismiss) name:NOTE_IMG_FAILED object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handlePop) name:NOTE_VC_POPPED object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(dismissWithoutCustomAnimation) name:NOTE_VC_SHOULD_DISMISS_FROM_DRAGGING object:nil]; } - (BOOL)prefersHomeIndicatorAutoHidden { diff --git a/BFRImageViewController/BFRImageViewerConstants.h b/BFRImageViewController/BFRImageViewerConstants.h new file mode 100644 index 0000000..23bf764 --- /dev/null +++ b/BFRImageViewController/BFRImageViewerConstants.h @@ -0,0 +1,28 @@ +// +// BFRImageViewerConstants.h +// BFRImageViewer +// +// Created by Jordan Morgan on 10/5/17. +// Copyright © 2017 Andrew Yates. All rights reserved. +// + +#import + +@interface BFRImageViewerConstants : NSObject + +// Notifications +extern NSString * const NOTE_VC_POPPED; +extern NSString * const NOTE_HI_RES_IMG_DOWNLOADED; +extern NSString * const NOTE_VC_SHOULD_DISMISS; +extern NSString * const NOTE_VC_SHOULD_DISMISS_FROM_DRAGGING; +extern NSString * const NOTE_VC_SHOULD_CANCEL_CUSTOM_TRANSITION; +extern NSString * const NOTE_IMG_FAILED; + +// NSError +extern NSString * const ERROR_TITLE; +extern NSString * const ERROR_MESSAGE; +extern NSString * const GENERAL_OK; +extern NSString * const HI_RES_IMG_ERROR_DOMAIN; +extern NSInteger const HI_RES_IMG_ERROR_CODE; + +@end diff --git a/BFRImageViewController/BFRImageViewerConstants.m b/BFRImageViewController/BFRImageViewerConstants.m new file mode 100644 index 0000000..be86b4e --- /dev/null +++ b/BFRImageViewController/BFRImageViewerConstants.m @@ -0,0 +1,26 @@ +// +// BFRImageViewerConstants.m +// BFRImageViewer +// +// Created by Jordan Morgan on 10/5/17. +// Copyright © 2017 Andrew Yates. All rights reserved. +// + +#import "BFRImageViewerConstants.h" + +@implementation BFRImageViewerConstants + +NSString * const NOTE_VC_POPPED = @"ViewControllerPopped"; +NSString * const NOTE_HI_RES_IMG_DOWNLOADED = @"HiResDownloadDone"; +NSString * const NOTE_VC_SHOULD_DISMISS = @"DismissUI"; +NSString * const NOTE_VC_SHOULD_DISMISS_FROM_DRAGGING = @"DimissUIFromDraggingGesture"; +NSString * const NOTE_VC_SHOULD_CANCEL_CUSTOM_TRANSITION = @"CancelCustomDismissalTransition"; +NSString * const NOTE_IMG_FAILED = @"ImageLoadingError"; + +NSString * const ERROR_TITLE = @"Whoops"; +NSString * const ERROR_MESSAGE = @"Looks like we ran into an issue loading the image, sorry about that!"; +NSString * const GENERAL_OK = @"Ok"; +NSString * const HI_RES_IMG_ERROR_DOMAIN = @"com.bfrImageViewer.backLoadedImgSource"; +NSInteger const HI_RES_IMG_ERROR_CODE = 44; + +@end diff --git a/BFRImageViewController/Resources/lowResImage.png b/BFRImageViewController/Resources/lowResImage.png index 18ffa92..ace4863 100644 Binary files a/BFRImageViewController/Resources/lowResImage.png and b/BFRImageViewController/Resources/lowResImage.png differ diff --git a/BFRImageViewerDemo/BFRImageViewer.xcodeproj/project.pbxproj b/BFRImageViewerDemo/BFRImageViewer.xcodeproj/project.pbxproj index 03df250..65d20d3 100644 --- a/BFRImageViewerDemo/BFRImageViewer.xcodeproj/project.pbxproj +++ b/BFRImageViewerDemo/BFRImageViewer.xcodeproj/project.pbxproj @@ -7,9 +7,10 @@ objects = { /* Begin PBXBuildFile section */ + 1702C69A1F86BBFC00104D0B /* BFRImageViewerConstants.m in Sources */ = {isa = PBXBuildFile; fileRef = 1702C6991F86BBFC00104D0B /* BFRImageViewerConstants.m */; }; + 1702C69C1F86C3A400104D0B /* lowResImage.png in Resources */ = {isa = PBXBuildFile; fileRef = 1702C69B1F86C32100104D0B /* lowResImage.png */; }; 171F4AD01E96BE0500F4AF01 /* BFRBackLoadedImageSource.m in Sources */ = {isa = PBXBuildFile; fileRef = 171F4ACF1E96BE0500F4AF01 /* BFRBackLoadedImageSource.m */; }; 171F4AD31E96C31400F4AF01 /* FourthViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 171F4AD21E96C31400F4AF01 /* FourthViewController.m */; }; - 171F4AD71E96D56D00F4AF01 /* lowResImage.png in Resources */ = {isa = PBXBuildFile; fileRef = 171F4AD61E96D56D00F4AF01 /* lowResImage.png */; }; 1797CB8E1E81BB4F00D9F729 /* BFRImageTransitionAnimator.m in Sources */ = {isa = PBXBuildFile; fileRef = 1797CB8D1E81BB4F00D9F729 /* BFRImageTransitionAnimator.m */; }; 1797CB911E81BBB200D9F729 /* ThirdViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 1797CB901E81BBB200D9F729 /* ThirdViewController.m */; }; 17C1AAB61D9D6EBF00FF1B67 /* BFRImageViewerLocalizations.bundle in Resources */ = {isa = PBXBuildFile; fileRef = 17C1AAB51D9D6EBF00FF1B67 /* BFRImageViewerLocalizations.bundle */; }; @@ -28,11 +29,13 @@ /* Begin PBXFileReference section */ 1264A436520DEA8753316972 /* Pods-BFRImageViewer.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-BFRImageViewer.release.xcconfig"; path = "Pods/Target Support Files/Pods-BFRImageViewer/Pods-BFRImageViewer.release.xcconfig"; sourceTree = ""; }; + 1702C6981F86BBFC00104D0B /* BFRImageViewerConstants.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BFRImageViewerConstants.h; sourceTree = ""; }; + 1702C6991F86BBFC00104D0B /* BFRImageViewerConstants.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BFRImageViewerConstants.m; sourceTree = ""; }; + 1702C69B1F86C32100104D0B /* lowResImage.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = lowResImage.png; sourceTree = ""; }; 171F4ACE1E96BE0500F4AF01 /* BFRBackLoadedImageSource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BFRBackLoadedImageSource.h; sourceTree = ""; }; 171F4ACF1E96BE0500F4AF01 /* BFRBackLoadedImageSource.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BFRBackLoadedImageSource.m; sourceTree = ""; }; 171F4AD11E96C31400F4AF01 /* FourthViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FourthViewController.h; sourceTree = ""; }; 171F4AD21E96C31400F4AF01 /* FourthViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FourthViewController.m; sourceTree = ""; }; - 171F4AD61E96D56D00F4AF01 /* lowResImage.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = lowResImage.png; sourceTree = ""; }; 1797CB8C1E81BB4F00D9F729 /* BFRImageTransitionAnimator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BFRImageTransitionAnimator.h; sourceTree = ""; }; 1797CB8D1E81BB4F00D9F729 /* BFRImageTransitionAnimator.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BFRImageTransitionAnimator.m; sourceTree = ""; }; 1797CB8F1E81BBB200D9F729 /* ThirdViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ThirdViewController.h; sourceTree = ""; }; @@ -83,7 +86,7 @@ 578DFD551CB6E7F400BFBD00 /* Resources */ = { isa = PBXGroup; children = ( - 171F4AD61E96D56D00F4AF01 /* lowResImage.png */, + 1702C69B1F86C32100104D0B /* lowResImage.png */, 578DFD581CB6F17B00BFBD00 /* cross.png */, 578DFD591CB6F17B00BFBD00 /* cross@2x.png */, 578DFD5A1CB6F17B00BFBD00 /* cross@3x.png */, @@ -152,6 +155,8 @@ 1797CB8D1E81BB4F00D9F729 /* BFRImageTransitionAnimator.m */, 171F4ACE1E96BE0500F4AF01 /* BFRBackLoadedImageSource.h */, 171F4ACF1E96BE0500F4AF01 /* BFRBackLoadedImageSource.m */, + 1702C6981F86BBFC00104D0B /* BFRImageViewerConstants.h */, + 1702C6991F86BBFC00104D0B /* BFRImageViewerConstants.m */, ); name = BFRImageViewController; path = ../../BFRImageViewController; @@ -227,12 +232,12 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( + 1702C69C1F86C3A400104D0B /* lowResImage.png in Resources */, 578DFD5C1CB6F17B00BFBD00 /* cross@2x.png in Resources */, 944B4DC11BFFC0C000B9BF87 /* LaunchScreen.storyboard in Resources */, 578DFD5D1CB6F17B00BFBD00 /* cross@3x.png in Resources */, 578DFD5B1CB6F17B00BFBD00 /* cross.png in Resources */, 17C1AAB61D9D6EBF00FF1B67 /* BFRImageViewerLocalizations.bundle in Resources */, - 171F4AD71E96D56D00F4AF01 /* lowResImage.png in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -298,6 +303,7 @@ 944B4DB31BFFC0C000B9BF87 /* AppDelegate.m in Sources */, 171F4AD01E96BE0500F4AF01 /* BFRBackLoadedImageSource.m in Sources */, 171F4AD31E96C31400F4AF01 /* FourthViewController.m in Sources */, + 1702C69A1F86BBFC00104D0B /* BFRImageViewerConstants.m in Sources */, 944B4DE91BFFC0E300B9BF87 /* BFRImageContainerViewController.m in Sources */, 944B4DB61BFFC0C000B9BF87 /* FirstViewController.m in Sources */, 944B4DB01BFFC0C000B9BF87 /* main.m in Sources */, diff --git a/BFRImageViewerDemo/BFRImageViewer/FourthViewController.m b/BFRImageViewerDemo/BFRImageViewer/FourthViewController.m index 623d086..528b789 100644 --- a/BFRImageViewerDemo/BFRImageViewer/FourthViewController.m +++ b/BFRImageViewerDemo/BFRImageViewer/FourthViewController.m @@ -33,7 +33,16 @@ - (void)viewDidLoad { [btn setTitle:@"Backload URL Image" forState:UIControlStateNormal]; [self.view addSubview:btn]; [btn.centerXAnchor constraintEqualToAnchor:self.view.centerXAnchor].active = YES; - [btn.centerYAnchor constraintEqualToAnchor:self.view.centerYAnchor].active = YES; + [btn.centerYAnchor constraintEqualToAnchor:self.view.centerYAnchor constant:-20].active = YES; + + + UIButton *btnClosure = [UIButton buttonWithType:UIButtonTypeRoundedRect]; + [btnClosure addTarget:self action:@selector(openImageViewerWithCompletionHandler) forControlEvents:UIControlEventTouchUpInside]; + btnClosure.translatesAutoresizingMaskIntoConstraints = NO; + [btnClosure setTitle:@"Backload URL Image + Completion Handler" forState:UIControlStateNormal]; + [self.view addSubview:btnClosure]; + [btnClosure.centerXAnchor constraintEqualToAnchor:self.view.centerXAnchor].active = YES; + [btnClosure.centerYAnchor constraintEqualToAnchor:self.view.centerYAnchor constant:20].active = YES; } - (void)openImageViewer { @@ -43,4 +52,26 @@ - (void)openImageViewer { [self presentViewController:imageVC animated:YES completion:nil]; } +- (void)openImageViewerWithCompletionHandler { + BFRBackLoadedImageSource *backloadedImage = [[BFRBackLoadedImageSource alloc] initWithInitialImage:[UIImage imageNamed:@"lowResImage"] hiResURL:[NSURL URLWithString:@"https://overflow.buffer.com/wp-content/uploads/2016/12/1-hByZ0VpJusdVwpZd-Z4-Zw.png"]]; + + backloadedImage.onCompletion = ^(UIImage * _Nullable img, NSError * _Nullable error) { + UIAlertController *alertVC = [UIAlertController alertControllerWithTitle:@"Download Done" message:[NSString stringWithFormat:@"Finished downloading hi res image.\nImage:%@\nError:%@", img, error] preferredStyle:UIAlertControllerStyleAlert]; + + UIAlertAction *close = [UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:nil]; + [alertVC addAction:close]; + + + UIViewController *topController = [UIApplication sharedApplication].keyWindow.rootViewController; + while (topController.presentedViewController) { + topController = topController.presentedViewController; + } + + [topController presentViewController:alertVC animated:YES completion:nil]; + }; + + BFRImageViewController *imageVC = [[BFRImageViewController alloc] initWithImageSource:@[backloadedImage]]; + [self presentViewController:imageVC animated:YES completion:nil]; +} + @end