From 8aa3dea1b6aac32e149248558ed7acd79926396b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=96mer=20Aslan?= Date: Sat, 21 Oct 2017 00:18:47 +0300 Subject: [PATCH 1/3] Implemented Live Photo feature --- .../BFRImageContainerViewController.h | 3 + .../BFRImageContainerViewController.m | 45 +++++- .../BFRImageViewController.h | 3 + .../BFRImageViewController.m | 3 + .../BFRImageViewer.xcodeproj/project.pbxproj | 6 + .../BFRImageViewer/AppDelegate.m | 7 +- .../BFRImageViewer/FifthViewController.h | 13 ++ .../BFRImageViewer/FifthViewController.m | 135 ++++++++++++++++++ BFRImageViewerDemo/BFRImageViewer/Info.plist | 2 + 9 files changed, 215 insertions(+), 2 deletions(-) create mode 100644 BFRImageViewerDemo/BFRImageViewer/FifthViewController.h create mode 100644 BFRImageViewerDemo/BFRImageViewer/FifthViewController.m diff --git a/BFRImageViewController/BFRImageContainerViewController.h b/BFRImageViewController/BFRImageContainerViewController.h index 4a4270b..b0ae111 100644 --- a/BFRImageViewController/BFRImageContainerViewController.h +++ b/BFRImageViewController/BFRImageContainerViewController.h @@ -29,4 +29,7 @@ /*! 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; +/*! Assigning YES to this property will disable autoplay for live photos when it used with 3DTouch peek feature */ +@property (nonatomic, getter=shouldDisableAutoplayForLivePhoto) BOOL disableAutoplayForLivePhoto; + @end diff --git a/BFRImageViewController/BFRImageContainerViewController.m b/BFRImageViewController/BFRImageContainerViewController.m index a775ac2..5159779 100644 --- a/BFRImageViewController/BFRImageContainerViewController.m +++ b/BFRImageViewController/BFRImageContainerViewController.m @@ -10,6 +10,7 @@ #import "BFRBackLoadedImageSource.h" #import "BFRImageViewerConstants.h" #import +#import #import #import #import @@ -67,7 +68,17 @@ - (void)viewDidLoad { self.imgLoaded = (UIImage *)self.imgSrc; [self addImageToScrollView]; } else if ([self.imgSrc isKindOfClass:[PHAsset class]]) { - [self retrieveImageFromAsset]; + PHAsset *assetSource = (PHAsset *)self.imgSrc; + + if (@available(iOS 9.1, *)) { + if (assetSource.mediaSubtypes & PHAssetMediaSubtypePhotoLive) { + [self retrieveLivePhotoFromAsset]; + } else { + [self retrieveImageFromAsset]; + } + } else { + [self retrieveImageFromAsset]; + } } else if ([self.imgSrc isKindOfClass:[FLAnimatedImage class]]) { self.imgLoaded = ((FLAnimatedImage *)self.imgSrc).posterImage; [self retrieveImageFromFLAnimatedImage]; @@ -417,6 +428,38 @@ - (void)retrieveImageFromURL { }]; } +- (void)retrieveLivePhotoFromAsset { + if (![self.imgSrc isKindOfClass:[PHAsset class]]) { + return; + } + + PHAsset *assetSource = (PHAsset *)self.imgSrc; + + if (!(assetSource.mediaSubtypes & PHAssetMediaSubtypePhotoLive)) { + return; + } + + PHLivePhotoView *livePhotoView = [[PHLivePhotoView alloc] + initWithFrame:self.view.frame]; + + if (self.shouldDisableAutoplayForLivePhoto == NO) { + [livePhotoView startPlaybackWithStyle:PHLivePhotoViewPlaybackStyleFull]; + } + + [self.scrollView addSubview:livePhotoView]; + + PHLivePhotoRequestOptions *liveOptions = [[PHLivePhotoRequestOptions alloc] init]; + liveOptions.deliveryMode = PHImageRequestOptionsDeliveryModeHighQualityFormat; + + [[PHImageManager defaultManager] + requestLivePhotoForAsset:assetSource + targetSize:self.view.frame.size + contentMode:PHImageContentModeAspectFit + options:liveOptions + resultHandler:^(PHLivePhoto * _Nullable livePhoto, NSDictionary * _Nullable info) { + livePhotoView.livePhoto = livePhoto; + }]; +} #pragma mark - Misc. Methods - (void)dismissUI { [[NSNotificationCenter defaultCenter] postNotificationName:NOTE_VC_SHOULD_DISMISS object:nil]; diff --git a/BFRImageViewController/BFRImageViewController.h b/BFRImageViewController/BFRImageViewController.h index eaee0f5..6a1258d 100644 --- a/BFRImageViewController/BFRImageViewController.h +++ b/BFRImageViewController/BFRImageViewController.h @@ -33,4 +33,7 @@ /*! Allows you to assign an index which to show first when opening multiple images. */ @property (nonatomic, assign) NSInteger startingIndex; +/*! Allows you to enable autoplay for peek&play feature on photo live view. Default to YES */ +@property (nonatomic, getter=shouldDisableAutoplayForLivePhoto) BOOL disableAutoplayForLivePhoto; + @end diff --git a/BFRImageViewController/BFRImageViewController.m b/BFRImageViewController/BFRImageViewController.m index c28d9c9..03dd26d 100644 --- a/BFRImageViewController/BFRImageViewController.m +++ b/BFRImageViewController/BFRImageViewController.m @@ -49,6 +49,7 @@ - (instancetype)initWithImageSource:(NSArray *)images { self.modalTransitionStyle = UIModalTransitionStyleCrossDissolve; self.enableDoneButton = YES; self.showDoneButtonOnLeft = YES; + self.disableAutoplayForLivePhoto = YES; } return self; @@ -64,6 +65,7 @@ - (instancetype)initForPeekWithImageSource:(NSArray *)images { self.enableDoneButton = YES; self.showDoneButtonOnLeft = YES; self.usedFor3DTouch = YES; + self.disableAutoplayForLivePhoto = YES; } return self; @@ -91,6 +93,7 @@ - (void)viewDidLoad { imgVC.useTransparentBackground = self.isUsingTransparentBackground; imgVC.disableSharingLongPress = self.shouldDisableSharingLongPress; imgVC.disableHorizontalDrag = (self.images.count > 1); + imgVC.disableAutoplayForLivePhoto = self.shouldDisableAutoplayForLivePhoto; [self.imageViewControllers addObject:imgVC]; } diff --git a/BFRImageViewerDemo/BFRImageViewer.xcodeproj/project.pbxproj b/BFRImageViewerDemo/BFRImageViewer.xcodeproj/project.pbxproj index 65d20d3..66f3c0c 100644 --- a/BFRImageViewerDemo/BFRImageViewer.xcodeproj/project.pbxproj +++ b/BFRImageViewerDemo/BFRImageViewer.xcodeproj/project.pbxproj @@ -25,6 +25,7 @@ 944B4DC11BFFC0C000B9BF87 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 944B4DBF1BFFC0C000B9BF87 /* LaunchScreen.storyboard */; }; 944B4DE91BFFC0E300B9BF87 /* BFRImageContainerViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 944B4DE61BFFC0E300B9BF87 /* BFRImageContainerViewController.m */; }; 944B4DEA1BFFC0E300B9BF87 /* BFRImageViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 944B4DE81BFFC0E300B9BF87 /* BFRImageViewController.m */; }; + 95D4797D1F97C348001E54D4 /* FifthViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 95D4797C1F97C348001E54D4 /* FifthViewController.m */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ @@ -61,6 +62,8 @@ 944B4DE61BFFC0E300B9BF87 /* BFRImageContainerViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BFRImageContainerViewController.m; sourceTree = ""; }; 944B4DE71BFFC0E300B9BF87 /* BFRImageViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BFRImageViewController.h; sourceTree = ""; }; 944B4DE81BFFC0E300B9BF87 /* BFRImageViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BFRImageViewController.m; sourceTree = ""; }; + 95D4797B1F97C348001E54D4 /* FifthViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FifthViewController.h; sourceTree = ""; }; + 95D4797C1F97C348001E54D4 /* FifthViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FifthViewController.m; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -130,6 +133,8 @@ 944B4DAE1BFFC0C000B9BF87 /* Supporting Files */, 171F4AD11E96C31400F4AF01 /* FourthViewController.h */, 171F4AD21E96C31400F4AF01 /* FourthViewController.m */, + 95D4797B1F97C348001E54D4 /* FifthViewController.h */, + 95D4797C1F97C348001E54D4 /* FifthViewController.m */, ); path = BFRImageViewer; sourceTree = ""; @@ -299,6 +304,7 @@ 944B4DB91BFFC0C000B9BF87 /* SecondViewController.m in Sources */, 1797CB8E1E81BB4F00D9F729 /* BFRImageTransitionAnimator.m in Sources */, 1797CB911E81BBB200D9F729 /* ThirdViewController.m in Sources */, + 95D4797D1F97C348001E54D4 /* FifthViewController.m in Sources */, 944B4DEA1BFFC0E300B9BF87 /* BFRImageViewController.m in Sources */, 944B4DB31BFFC0C000B9BF87 /* AppDelegate.m in Sources */, 171F4AD01E96BE0500F4AF01 /* BFRBackLoadedImageSource.m in Sources */, diff --git a/BFRImageViewerDemo/BFRImageViewer/AppDelegate.m b/BFRImageViewerDemo/BFRImageViewer/AppDelegate.m index ee3d10b..82b1ecd 100644 --- a/BFRImageViewerDemo/BFRImageViewer/AppDelegate.m +++ b/BFRImageViewerDemo/BFRImageViewer/AppDelegate.m @@ -11,6 +11,7 @@ #import "SecondViewController.h" #import "ThirdViewController.h" #import "FourthViewController.h" +#import "FifthViewController.h" @interface AppDelegate () @@ -24,7 +25,11 @@ - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:( UITabBarController *tabVC = [UITabBarController new]; tabVC.view.backgroundColor = [UIColor whiteColor]; - tabVC.viewControllers = @[[FirstViewController new], [SecondViewController new], [ThirdViewController new], [FourthViewController new]]; + tabVC.viewControllers = @[[FirstViewController new], + [SecondViewController new], + [ThirdViewController new], + [FourthViewController new], + [FifthViewController new]]; self.window.rootViewController = tabVC; [self.window makeKeyAndVisible]; diff --git a/BFRImageViewerDemo/BFRImageViewer/FifthViewController.h b/BFRImageViewerDemo/BFRImageViewer/FifthViewController.h new file mode 100644 index 0000000..77de02c --- /dev/null +++ b/BFRImageViewerDemo/BFRImageViewer/FifthViewController.h @@ -0,0 +1,13 @@ +// +// FifthViewController.h +// BFRImageViewer +// +// Created by Omer Emre Aslan on 18.10.2017. +// Copyright © 2017 Andrew Yates. All rights reserved. +// + +#import + +@interface FifthViewController : UIViewController + +@end diff --git a/BFRImageViewerDemo/BFRImageViewer/FifthViewController.m b/BFRImageViewerDemo/BFRImageViewer/FifthViewController.m new file mode 100644 index 0000000..d0f712a --- /dev/null +++ b/BFRImageViewerDemo/BFRImageViewer/FifthViewController.m @@ -0,0 +1,135 @@ +// +// FifthViewController.m +// BFRImageViewer +// +// Created by Omer Emre Aslan on 18.10.2017. +// Copyright © 2017 Andrew Yates. All rights reserved. +// + +#import +#import "FifthViewController.h" +#import "BFRImageViewController.h" + +@interface FifthViewController () + +@end + +@implementation FifthViewController + +- (instancetype) init { + if (self = [super init]) { + self.title = @"Live Photo"; + } + + return self; +} + +- (void)viewDidLoad { + [super viewDidLoad]; + + [self check3DTouch]; + + [self addImageButtonToView]; +} + +#pragma mark - 3D Touch +- (void)check3DTouch { + if ([self.traitCollection respondsToSelector:@selector(forceTouchCapability)] && self.traitCollection.forceTouchCapability == UIForceTouchCapabilityAvailable) { + [self registerForPreviewingWithDelegate:self sourceView:self.view]; + } +} + +- (UIViewController *)previewingContext:(id)previewingContext viewControllerForLocation:(CGPoint)location { + PHAuthorizationStatus status = [PHPhotoLibrary authorizationStatus]; + + if (status == PHAuthorizationStatusAuthorized) { + return [self imageViewControllerForLivePhotoDisableAutoplay:NO]; + } else { + [self showAuthorizationAlertViewControllerAnimated:YES]; + return nil; + } +} + +- (void)previewingContext:(id)previewingContext commitViewController:(UIViewController *)viewControllerToCommit { + [self presentViewController:viewControllerToCommit animated:YES completion:nil]; +} + +- (void)traitCollectionDidChange:(UITraitCollection *)previousTraitCollection { + if ([self.traitCollection respondsToSelector:@selector(forceTouchCapability)] && self.traitCollection.forceTouchCapability == UIForceTouchCapabilityAvailable) { + [self check3DTouch]; + } +} + +#pragma mark - Misc +- (void)addImageButtonToView { + UIButton *openImageFromURL = [UIButton buttonWithType:UIButtonTypeRoundedRect]; + openImageFromURL.translatesAutoresizingMaskIntoConstraints = NO; + [openImageFromURL setTitle:@"Open Image" forState:UIControlStateNormal]; + [openImageFromURL addTarget:self + action:@selector(openImage:) + forControlEvents:UIControlEventTouchUpInside]; + [self.view addSubview:openImageFromURL]; + [openImageFromURL.centerXAnchor constraintEqualToAnchor:self.view.centerXAnchor].active = YES; + [openImageFromURL.centerYAnchor constraintEqualToAnchor:self.view.centerYAnchor].active = YES; +} + +#pragma mark - Actions + +- (void)openImage:(UIButton *)sender { + PHAuthorizationStatus status = [PHPhotoLibrary authorizationStatus]; + + if (status == PHAuthorizationStatusAuthorized) { + BFRImageViewController *imageViewController = [self + imageViewControllerForLivePhotoDisableAutoplay:YES]; + [self presentViewController:imageViewController + animated:YES + completion:nil]; + } else { + [PHPhotoLibrary requestAuthorization:^(PHAuthorizationStatus status) { + if (status == PHAuthorizationStatusAuthorized) { + BFRImageViewController *imageViewController = [self imageViewControllerForLivePhotoDisableAutoplay:YES]; + [self presentViewController:imageViewController + animated:YES + completion:nil]; + } else { + [self showAuthorizationAlertViewControllerAnimated:YES]; + } + }]; + } + +} + +- (void)showAuthorizationAlertViewControllerAnimated:(BOOL)isAnimated { + UIAlertController *controller = [UIAlertController + alertControllerWithTitle:NSLocalizedString(@"Authorization Failed!", nil) + message:NSLocalizedString(@"In order to access live photo feature, please allow authorization on Settings.", nil) + preferredStyle:UIAlertControllerStyleAlert]; + UIAlertAction *closeAction = [UIAlertAction + actionWithTitle:NSLocalizedString(@"Close", nil) + style:UIAlertActionStyleDefault + handler:nil]; + [controller addAction:closeAction]; + + [self presentViewController:controller + animated:isAnimated + completion:nil]; +} + +- (BFRImageViewController *)imageViewControllerForLivePhotoDisableAutoplay:(BOOL)shouldDisableAutoPlay { + PHFetchOptions *options = [[PHFetchOptions alloc] init]; + options.sortDescriptors = @[[NSSortDescriptor sortDescriptorWithKey:@"creationDate" ascending:NO]]; + options.predicate = [NSPredicate predicateWithFormat:@"mediaType == %d", PHAssetMediaTypeImage]; + options.predicate = [NSPredicate predicateWithFormat:@"mediaSubtype == %d", PHAssetMediaSubtypePhotoLive]; + options.includeAllBurstAssets = NO; + PHFetchResult *allLivePhotos = [PHAsset fetchAssetsWithOptions:options]; + + PHAsset *firstImage = (PHAsset *)[allLivePhotos firstObject]; + + BFRImageViewController *viewController = [[BFRImageViewController alloc] + initWithImageSource:@[firstImage]]; + viewController.disableAutoplayForLivePhoto = shouldDisableAutoPlay; + return viewController; +} + + +@end diff --git a/BFRImageViewerDemo/BFRImageViewer/Info.plist b/BFRImageViewerDemo/BFRImageViewer/Info.plist index 7bfe257..a0e47a9 100644 --- a/BFRImageViewerDemo/BFRImageViewer/Info.plist +++ b/BFRImageViewerDemo/BFRImageViewer/Info.plist @@ -2,6 +2,8 @@ + NSPhotoLibraryUsageDescription + The app will open an image from your library. CFBundleDevelopmentRegion en CFBundleExecutable From 505e73fd9f03238215c0bec35e2fc20235e0d2e5 Mon Sep 17 00:00:00 2001 From: DreamingInBinary Date: Wed, 2 May 2018 10:19:25 -0500 Subject: [PATCH 2/3] Support for Live Photo finished up --- .../BFRImageContainerViewController.m | 117 +++++++++++++----- 1 file changed, 87 insertions(+), 30 deletions(-) diff --git a/BFRImageViewController/BFRImageContainerViewController.m b/BFRImageViewController/BFRImageContainerViewController.m index 406ae99..0cbf8f6 100644 --- a/BFRImageViewController/BFRImageContainerViewController.m +++ b/BFRImageViewController/BFRImageContainerViewController.m @@ -44,8 +44,12 @@ @interface BFRImageContainerViewController () Date: Wed, 2 May 2018 10:26:44 -0500 Subject: [PATCH 3/3] Show a few Live Photos --- .../BFRImageViewer/FifthViewController.m | 41 ++++++++++++++++--- 1 file changed, 35 insertions(+), 6 deletions(-) diff --git a/BFRImageViewerDemo/BFRImageViewer/FifthViewController.m b/BFRImageViewerDemo/BFRImageViewer/FifthViewController.m index d0f712a..2617e34 100644 --- a/BFRImageViewerDemo/BFRImageViewer/FifthViewController.m +++ b/BFRImageViewerDemo/BFRImageViewer/FifthViewController.m @@ -123,13 +123,42 @@ - (BFRImageViewController *)imageViewControllerForLivePhotoDisableAutoplay:(BOOL options.includeAllBurstAssets = NO; PHFetchResult *allLivePhotos = [PHAsset fetchAssetsWithOptions:options]; - PHAsset *firstImage = (PHAsset *)[allLivePhotos firstObject]; + NSMutableArray *livePhotosToShow = [NSMutableArray new]; - BFRImageViewController *viewController = [[BFRImageViewController alloc] - initWithImageSource:@[firstImage]]; - viewController.disableAutoplayForLivePhoto = shouldDisableAutoPlay; - return viewController; + if (allLivePhotos.count > 0) { + NSInteger maxResults = 4; + NSInteger currentFetchCount = 0; + + for (PHFetchResult *result in allLivePhotos) { + if (currentFetchCount == maxResults) { + break; + } + + [livePhotosToShow addObject:result]; + currentFetchCount++; + } + + BFRImageViewController *viewController = [[BFRImageViewController alloc] + initWithImageSource:[livePhotosToShow copy]]; + viewController.disableAutoplayForLivePhoto = shouldDisableAutoPlay; + return viewController; + } else { + UIAlertController *controller = [UIAlertController + alertControllerWithTitle:@"No Live Photos" + message:@"There doesn't appear to be any live photos on your device." + preferredStyle:UIAlertControllerStyleAlert]; + UIAlertAction *closeAction = [UIAlertAction + actionWithTitle:NSLocalizedString(@"Close", nil) + style:UIAlertActionStyleDefault + handler:nil]; + [controller addAction:closeAction]; + + [self presentViewController:controller + animated:YES + completion:nil]; + + return nil; + } } - @end