diff --git a/Example/Podfile.lock b/Example/Podfile.lock index e2332d86..78852a67 100644 --- a/Example/Podfile.lock +++ b/Example/Podfile.lock @@ -1,5 +1,5 @@ PODS: - - WPMediaPicker (0.7.3) + - WPMediaPicker (0.8.0) DEPENDENCIES: - WPMediaPicker (from `../`) @@ -9,6 +9,6 @@ EXTERNAL SOURCES: :path: ../ SPEC CHECKSUMS: - WPMediaPicker: 6bbce465f5a5c071267ae28bdf31d0fd6e109721 + WPMediaPicker: cc2e022caf5cfd1f3c517c794aa5e3a81e065b05 COCOAPODS: 0.39.0 diff --git a/Example/WPMediaPicker.xcodeproj/project.pbxproj b/Example/WPMediaPicker.xcodeproj/project.pbxproj index c5a87df2..99033ef1 100644 --- a/Example/WPMediaPicker.xcodeproj/project.pbxproj +++ b/Example/WPMediaPicker.xcodeproj/project.pbxproj @@ -471,7 +471,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 7.1; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; TARGETED_DEVICE_FAMILY = "1,2"; @@ -504,7 +504,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 7.1; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; SDKROOT = iphoneos; TARGETED_DEVICE_FAMILY = "1,2"; VALIDATE_PRODUCT = YES; diff --git a/Example/WPMediaPicker/DemoViewController.m b/Example/WPMediaPicker/DemoViewController.m index 7720b947..8fef656e 100644 --- a/Example/WPMediaPicker/DemoViewController.m +++ b/Example/WPMediaPicker/DemoViewController.m @@ -32,7 +32,6 @@ - (void)viewDidLoad [self.tableView registerClass:[WPMediaGroupTableViewCell class] forCellReuseIdentifier:NSStringFromClass([WPMediaGroupTableViewCell class])]; self.options = @{ MediaPickerOptionsShowMostRecentFirst:@(YES), - MediaPickerOptionsUsePhotosLibrary:@(YES), MediaPickerOptionsShowCameraCapture:@(YES), MediaPickerOptionsAllowMultipleSelection:@(YES) }; @@ -115,12 +114,12 @@ - (void) showPicker:(id) sender WPMediaPickerViewController *mediaPicker = [[WPMediaPickerViewController alloc] init]; mediaPicker.delegate = self; mediaPicker.showMostRecentFirst = [self.options[MediaPickerOptionsShowMostRecentFirst] boolValue]; - if ([self.options[MediaPickerOptionsUsePhotosLibrary] boolValue]){ - self.customDataSource = [[WPPHAssetDataSource alloc] init]; - mediaPicker.dataSource = self.customDataSource; - } mediaPicker.allowCaptureOfMedia = [self.options[MediaPickerOptionsShowCameraCapture] boolValue]; mediaPicker.allowMultipleSelection = [self.options[MediaPickerOptionsAllowMultipleSelection] boolValue]; + mediaPicker.modalPresentationStyle = UIModalPresentationPopover; + UIPopoverPresentationController *ppc = mediaPicker.popoverPresentationController; + ppc.barButtonItem = sender; + [self presentViewController:mediaPicker animated:YES completion:nil]; } diff --git a/Example/WPMediaPicker/OptionsViewController.h b/Example/WPMediaPicker/OptionsViewController.h index b542c88b..7597d5c7 100644 --- a/Example/WPMediaPicker/OptionsViewController.h +++ b/Example/WPMediaPicker/OptionsViewController.h @@ -1,7 +1,6 @@ @import UIKit; extern NSString const *MediaPickerOptionsShowMostRecentFirst; -extern NSString const *MediaPickerOptionsUsePhotosLibrary; extern NSString const *MediaPickerOptionsShowCameraCapture; extern NSString const *MediaPickerOptionsAllowMultipleSelection; diff --git a/Example/WPMediaPicker/OptionsViewController.m b/Example/WPMediaPicker/OptionsViewController.m index bed1d548..a340e8ce 100644 --- a/Example/WPMediaPicker/OptionsViewController.m +++ b/Example/WPMediaPicker/OptionsViewController.m @@ -7,7 +7,6 @@ typedef NS_ENUM(NSInteger, OptionsViewControllerCell){ OptionsViewControllerCellShowMostRecentFirst, - OptionsViewControllerCellUsePhotosLibrary, OptionsViewControllerCellShowCameraCapture, OptionsViewControllerCellAllowMultipleSelection, OptionsViewControllerCellTotal @@ -16,7 +15,6 @@ typedef NS_ENUM(NSInteger, OptionsViewControllerCell){ @interface OptionsViewController () @property (nonatomic, strong) UITableViewCell *showMostRecentFirstCell; -@property (nonatomic, strong) UITableViewCell *usePhotosLibraryCell; @property (nonatomic, strong) UITableViewCell *showCameraCaptureCell; @property (nonatomic, strong) UITableViewCell *allowMultipleSelectionCell; @@ -38,11 +36,6 @@ - (void)viewDidLoad ((UISwitch *)self.showMostRecentFirstCell.accessoryView).on = [self.options[MediaPickerOptionsShowMostRecentFirst] boolValue]; self.showMostRecentFirstCell.textLabel.text = @"Show Most Recent First"; - self.usePhotosLibraryCell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:nil]; - self.usePhotosLibraryCell.accessoryView = [[UISwitch alloc] init]; - ((UISwitch *)self.usePhotosLibraryCell.accessoryView).on = [self.options[MediaPickerOptionsUsePhotosLibrary] boolValue]; - self.usePhotosLibraryCell.textLabel.text = @"Use Photos Library (iOS 8 Only)"; - self.showCameraCaptureCell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:nil]; self.showCameraCaptureCell.accessoryView = [[UISwitch alloc] init]; ((UISwitch *)self.showCameraCaptureCell.accessoryView).on = [self.options[MediaPickerOptionsShowCameraCapture] boolValue]; @@ -73,9 +66,6 @@ - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(N case OptionsViewControllerCellShowMostRecentFirst: return self.showMostRecentFirstCell; break; - case OptionsViewControllerCellUsePhotosLibrary: - return self.usePhotosLibraryCell; - break; case OptionsViewControllerCellShowCameraCapture: return self.showCameraCaptureCell; break; @@ -94,7 +84,6 @@ - (void) done:(id) sender id delegate = self.delegate; NSDictionary *newOptions = @{ MediaPickerOptionsShowMostRecentFirst:@(((UISwitch *)self.showMostRecentFirstCell.accessoryView).on), - MediaPickerOptionsUsePhotosLibrary:@(((UISwitch *)self.usePhotosLibraryCell.accessoryView).on), MediaPickerOptionsShowCameraCapture:@(((UISwitch *)self.showCameraCaptureCell.accessoryView).on), MediaPickerOptionsAllowMultipleSelection:@(((UISwitch *)self.allowMultipleSelectionCell.accessoryView).on) }; diff --git a/Pod/Classes/WPALAssetDataSource.h b/Pod/Classes/WPALAssetDataSource.h deleted file mode 100644 index 3cba7593..00000000 --- a/Pod/Classes/WPALAssetDataSource.h +++ /dev/null @@ -1,24 +0,0 @@ -@import Foundation; -@import AssetsLibrary; -#import "WPMediaCollectionDataSource.h" - -/** - An implementation of the WPDataSource protocol using the AssetsLibrary framework - */ -@interface WPALAssetDataSource : NSObject - -@end - -/** - An implementation of the WPMediaAsset protocol using the ALAsset class - */ -@interface ALAsset(WPMediaAsset) - -@end - -/** - An implementation of the WPMediaGroup protocol using the ALAssetsGroup class - */ -@interface ALAssetsGroup(WPMediaGroup) - -@end diff --git a/Pod/Classes/WPALAssetDataSource.m b/Pod/Classes/WPALAssetDataSource.m deleted file mode 100644 index 824f4553..00000000 --- a/Pod/Classes/WPALAssetDataSource.m +++ /dev/null @@ -1,429 +0,0 @@ -#import "WPALAssetDataSource.h" -#import "WPALAssetImageCacheManager.h" - -@interface WPALAssetDataSource () - -@property (nonatomic, strong) ALAssetsGroup *assetsGroup; -@property (nonatomic, strong) NSMutableArray *groups; -@property (nonatomic, assign) BOOL ignoreMediaNotifications; -@property (nonatomic, assign) WPMediaType filter; -@property (nonatomic, strong) ALAssetsFilter *assetsFilter; -@property (nonatomic, strong) NSMutableDictionary *observers; -@property (nonatomic, assign) BOOL refreshGroups; -@property (nonatomic, strong) NSMutableArray *extraAssets; - -@end - -@implementation WPALAssetDataSource - -- (instancetype)init -{ - self = [super init]; - if (!self) { - return nil; - } - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(handleLibraryNotification:) - name:ALAssetsLibraryChangedNotification - object:self.assetsLibrary]; - _groups = [[NSMutableArray alloc] init]; - _filter = WPMediaTypeAll; - _assetsFilter = [ALAssetsFilter allAssets]; - _observers = [[NSMutableDictionary alloc] init]; - _refreshGroups = YES; - _extraAssets = [NSMutableArray array]; - return self; -} - -- (void)dealloc -{ - [[NSNotificationCenter defaultCenter] removeObserver:self]; -} - -- (void)handleLibraryNotification:(NSNotification *)note -{ - if (![self shouldNotifyObservers:note]) { - return; - } - if (self.ignoreMediaNotifications) { - return; - } - __weak __typeof__(self) weakSelf = self; - dispatch_async(dispatch_get_main_queue(), ^{ - [weakSelf loadDataWithSuccess:^{ - [weakSelf.observers enumerateKeysAndObjectsUsingBlock:^(NSUUID *key, WPMediaChangesBlock block, BOOL *stop) { - block(); - }]; - } failure:nil]; - }); -} - -- (BOOL)shouldNotifyObservers:(NSNotification *)note -{ - if (!note.userInfo || - note.userInfo[ALAssetLibraryUpdatedAssetGroupsKey] || - [note.userInfo[ALAssetLibraryInsertedAssetGroupsKey] count] > 0 || - [note.userInfo[ALAssetLibraryDeletedAssetGroupsKey] count] > 0 - ) - { - self.refreshGroups = YES; - return YES; - } - - NSURL *currentGroupID = [self.assetsGroup valueForProperty:ALAssetsGroupPropertyURL]; - NSSet *groupsChanged = note.userInfo[ALAssetLibraryUpdatedAssetGroupsKey]; - NSSet *assetsChanged = note.userInfo[ALAssetLibraryUpdatedAssetsKey]; - if ( groupsChanged && [groupsChanged containsObject:currentGroupID] - && assetsChanged.count > 0 - ) { - return YES; - } - - return NO; -} - -- (ALAssetsLibrary *)assetsLibrary -{ - static dispatch_once_t onceToken; - static ALAssetsLibrary *_assetsLibrary; - dispatch_once(&onceToken, ^{ - _assetsLibrary = [[ALAssetsLibrary alloc] init]; - }); - return _assetsLibrary; -} - -- (void)loadDataWithSuccess:(WPMediaChangesBlock)successBlock - failure:(WPMediaFailureBlock)failureBlock -{ - ALAuthorizationStatus authorizationStatus = ALAssetsLibrary.authorizationStatus; - if (authorizationStatus == ALAuthorizationStatusDenied || - authorizationStatus == ALAuthorizationStatusRestricted) { - if (failureBlock) { - NSError *error = [NSError errorWithDomain:WPMediaPickerErrorDomain code:WPMediaErrorCodePermissionsFailed userInfo:nil]; - failureBlock(error); - } - return; - } - [self.extraAssets removeAllObjects]; - if (self.refreshGroups) { - [self loadGroupsWithSuccess:^{ - self.refreshGroups = NO; - [self loadAssetsWithSuccess:successBlock failure:failureBlock]; - } failure:failureBlock]; - } else { - [self loadAssetsWithSuccess:successBlock failure:failureBlock]; - } -} - -- (void)loadGroupsWithSuccess:(WPMediaChangesBlock)successBlock - failure:(WPMediaFailureBlock)failureBlock -{ - [self.groups removeAllObjects]; - [self.assetsLibrary enumerateGroupsWithTypes:ALAssetsGroupAll usingBlock:^(ALAssetsGroup *group, BOOL *stop) { - if(!group){ - if (successBlock) { - successBlock(); - } - return; - } - if ([[group valueForProperty:ALAssetsGroupPropertyType] intValue] == ALAssetsGroupSavedPhotos){ - if (!self.assetsGroup){ - self.assetsGroup = group; - } - [self.groups insertObject:group atIndex:0]; - } else { - [self.groups addObject:group]; - } - } failureBlock:^(NSError *error) { - NSLog(@"Error: %@", [error localizedDescription]); - NSError * filteredError = error; - if ([error.domain isEqualToString:ALAssetsLibraryErrorDomain] && - (error.code == ALAssetsLibraryAccessUserDeniedError || error.code == ALAssetsLibraryAccessGloballyDeniedError) - ) { - filteredError = [NSError errorWithDomain:WPMediaPickerErrorDomain code:WPMediaErrorCodePermissionsFailed userInfo:error.userInfo]; - } - if (failureBlock) { - failureBlock(filteredError); - } - }]; -} - -- (void)loadAssetsWithSuccess:(WPMediaChangesBlock)successBlock - failure:(WPMediaFailureBlock)failureBlock -{ - [self.assetsGroup setAssetsFilter:self.assetsFilter]; - - if (successBlock) { - successBlock(); - } -} - -#pragma mark - WPMediaCollectionDataSource - -- (NSInteger)numberOfGroups -{ - return self.groups.count; -} - -- (id)groupAtIndex:(NSInteger)index -{ - return self.groups[index]; -} - -- (id)selectedGroup -{ - return self.assetsGroup; -} - -- (void)setSelectedGroup:(id)group -{ - NSParameterAssert([group isKindOfClass:[ALAssetsGroup class]]); - [self.extraAssets removeAllObjects]; - self.assetsGroup = [group baseGroup]; -} - -- (NSInteger)indexOfSelectedGroup -{ - for (int i = 0; i < self.groups.count; i++) { - ALAssetsGroup *group = (ALAssetsGroup *)self.groups[i]; - NSURL *loopGroupURL = [group valueForProperty:ALAssetsGroupPropertyURL]; - if ([loopGroupURL isEqual:[self.assetsGroup valueForProperty:ALAssetsGroupPropertyURL]]) { - return i; - } - } - return NSNotFound; -} - -- (NSInteger)numberOfAssets -{ - return [self.assetsGroup numberOfAssets] + [self.extraAssets count]; -} - -- (id)mediaAtIndex:(NSInteger)index -{ - if ( index >= [self.assetsGroup numberOfAssets]){ - return self.extraAssets[index-self.assetsGroup.numberOfAssets]; - } - __block ALAsset *asset; - [self.assetsGroup enumerateAssetsAtIndexes:[NSIndexSet indexSetWithIndex:index] - options:0 usingBlock:^(ALAsset *result, NSUInteger index, BOOL *stop) { - if (result){ - asset = result; - } - }]; - return asset; -} - -- (id)mediaWithIdentifier:(NSString *)identifier -{ - NSParameterAssert(identifier); - __block ALAsset *assetResult = nil; - dispatch_semaphore_t sema = dispatch_semaphore_create(0); - [self.assetsLibrary assetForURL:[NSURL URLWithString:identifier] resultBlock:^(ALAsset *asset) { - assetResult = asset; - dispatch_semaphore_signal(sema); - } failureBlock:^(NSError *error) { - dispatch_semaphore_signal(sema); - }]; - dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER); - return assetResult; -} - -- (id)registerChangeObserverBlock:(WPMediaChangesBlock)callback -{ - NSUUID *blockKey = [NSUUID UUID]; - [self.observers setObject:[callback copy] forKey:blockKey]; - return blockKey; - -} - -- (void)unregisterChangeObserver:(id)blockKey -{ - [self.observers removeObjectForKey:blockKey]; -} - -- (void)addImage:(UIImage *)image - metadata:(NSDictionary *)metadata - completionBlock:(WPMediaAddedBlock)completionBlock -{ - self.ignoreMediaNotifications = YES; - [self.assetsLibrary writeImageToSavedPhotosAlbum:[image CGImage] - metadata:metadata - completionBlock:^(NSURL *assetURL, NSError *error) - { - [self addMediaFromAssetURL:assetURL error:error completionBlock:completionBlock]; - }]; -} - -- (void)addVideoFromURL:(NSURL *)url - completionBlock:(WPMediaAddedBlock)completionBlock -{ - self.ignoreMediaNotifications = YES; - [self.assetsLibrary writeVideoAtPathToSavedPhotosAlbum:url - completionBlock:^(NSURL *assetURL, NSError *error) - { - [self addMediaFromAssetURL:assetURL error:error completionBlock:completionBlock]; - }]; -} - --(void)addMediaFromAssetURL:(NSURL *)assetURL - error:(NSError *)error - completionBlock:(WPMediaAddedBlock)completionBlock -{ - if (error){ - self.ignoreMediaNotifications = NO; - if (completionBlock){ - completionBlock(nil, error); - } - return; - } - [self.assetsLibrary assetForURL:assetURL resultBlock:^(ALAsset *asset) { - if (![self.assetsGroup isEditable] && - [[self.assetsGroup valueForProperty:ALAssetsGroupPropertyType] intValue] != ALAssetsGroupSavedPhotos) { - dispatch_async(dispatch_get_main_queue(), ^{ - if (completionBlock) { - completionBlock(nil, [NSError errorWithDomain:ALAssetsLibraryErrorDomain code:ALAssetsLibraryUnknownError userInfo:nil]); - } - self.ignoreMediaNotifications = NO; - }); - return; - } - if ([self.assetsGroup isEditable]){ - [self.assetsGroup addAsset:asset]; - [self.extraAssets addObject:asset]; - } - dispatch_async(dispatch_get_main_queue(), ^{ - if (completionBlock) { - completionBlock(asset, nil); - } - self.ignoreMediaNotifications = NO; - }); - } failureBlock:^(NSError *error) { - dispatch_async(dispatch_get_main_queue(), ^{ - if (completionBlock) { - completionBlock(nil, error); - } - self.ignoreMediaNotifications = NO; - }); - }]; -} - -- (void) setMediaTypeFilter:(WPMediaType)filter -{ - self.filter = filter; - switch (self.filter) { - case WPMediaTypeAll: - self.assetsFilter = [ALAssetsFilter allAssets]; - break; - case WPMediaTypeImage: - self.assetsFilter = [ALAssetsFilter allPhotos]; - break; - case WPMediaTypeVideo: - self.assetsFilter = [ALAssetsFilter allVideos]; - break; - default: - self.assetsFilter = [ALAssetsFilter allAssets]; - break; - }} - -- (WPMediaType)mediaTypeFilter -{ - return self.filter; -} - -@end - -#pragma mark - WPALAssetMedia - -@implementation ALAsset(WPMediaAsset) - -- (WPMediaRequestID)imageWithSize:(CGSize)size completionHandler:(WPMediaImageBlock)completionHandler; -{ - CGFloat scale = [[UIScreen mainScreen] scale]; - return (WPMediaRequestID)[[WPALAssetImageCacheManager sharedInstance] requestImageForAsset:self - targetSize:size - scale:scale resultHandler:completionHandler]; -} - -- (void)cancelImageRequest:(WPMediaRequestID)requestID -{ - [[WPALAssetImageCacheManager sharedInstance] cancelImageRequest:requestID]; -} - -- (WPMediaType)assetType -{ - if ([self valueForProperty:ALAssetPropertyType] == ALAssetTypeVideo){ - return WPMediaTypeVideo; - } else if ([self valueForProperty:ALAssetPropertyType] == ALAssetTypePhoto) { - return WPMediaTypeImage; - } else if ([self valueForProperty:ALAssetPropertyType] == ALAssetTypeUnknown) { - return WPMediaTypeOther; - } - - return WPMediaTypeOther; -} - -- (NSTimeInterval)duration -{ - NSTimeInterval duration = 0; - if ([self valueForProperty:ALAssetPropertyType] == ALAssetTypeVideo) { - duration = [[self valueForProperty:ALAssetPropertyDuration] doubleValue]; - } - return duration; -} - -- (id)baseAsset -{ - return self; -} - -- (NSString *)identifier -{ - return [[self valueForProperty:ALAssetPropertyAssetURL] absoluteString]; -} - -- (NSDate *)date -{ - return [self valueForProperty:ALAssetPropertyDate]; -} - -@end - -#pragma mark - WPALAssetGroup - -@implementation ALAssetsGroup(WPALAssetGroup) - -- (NSString *)name -{ - return [self valueForProperty:ALAssetsGroupPropertyName]; -} - -- (WPMediaRequestID)imageWithSize:(CGSize)size completionHandler:(WPMediaImageBlock)completionHandler; -{ - UIImage *result = [UIImage imageWithCGImage:[self posterImage]]; - if (completionHandler){ - if (result) { - completionHandler(result, nil); - } else { - completionHandler(nil, nil); - } - } - return 0; -} - -- (void)cancelImageRequest:(WPMediaRequestID)requestID -{ - //This implementation doens't actually makes work async so nothing to cancel here. -} - -- (id)baseGroup -{ - return self; -} - -- (NSString *)identifier -{ - return [[self valueForProperty:ALAssetsGroupPropertyURL] absoluteString]; -} - -@end \ No newline at end of file diff --git a/Pod/Classes/WPALAssetImageCacheManager.h b/Pod/Classes/WPALAssetImageCacheManager.h deleted file mode 100644 index 36f0ea81..00000000 --- a/Pod/Classes/WPALAssetImageCacheManager.h +++ /dev/null @@ -1,16 +0,0 @@ -#import - -@import AssetsLibrary; - -@interface WPALAssetImageCacheManager : NSObject - -+ (instancetype)sharedInstance; - -- (NSUInteger)requestImageForAsset:(ALAsset *)asset - targetSize:(CGSize)targetSize - scale:(CGFloat)scale - resultHandler:(void (^)(UIImage *result, NSError *error))resultHandler; - -- (void)cancelImageRequest:(NSUInteger)requestID; - -@end diff --git a/Pod/Classes/WPALAssetImageCacheManager.m b/Pod/Classes/WPALAssetImageCacheManager.m deleted file mode 100644 index e7f4b861..00000000 --- a/Pod/Classes/WPALAssetImageCacheManager.m +++ /dev/null @@ -1,161 +0,0 @@ -#import "WPALAssetImageCacheManager.h" -#import "WPMediaCollectionDataSource.h" - -@import AVFoundation; -@import ImageIO; - -#pragma mark - WPAssetResizeOperation - -@interface WPAssetResizeOperation : NSOperation - -- (instancetype)initWithAsset:(ALAsset *)asset - targetSize:(CGSize)targetSize - scale:(CGFloat)scale - completionBlock:(WPMediaImageBlock)completionBlock; - -@property (nonatomic, strong) ALAsset *asset; -@property (nonatomic, assign) CGSize targetSize; -@property (nonatomic, assign) CGFloat scale; -@property (nonatomic, copy) WPMediaImageBlock block; - -@end - -@implementation WPAssetResizeOperation - -- (instancetype)initWithAsset:(ALAsset *)asset - targetSize:(CGSize)targetSize - scale:(CGFloat)scale - completionBlock:(WPMediaImageBlock)completionBlock -{ - self = [super init]; - if (self) { - _asset = asset; - _targetSize = targetSize; - _scale = scale; - _block = [completionBlock copy]; - } - return self; -} - -- (void)main -{ - CGSize realSize = CGSizeApplyAffineTransform(self.targetSize, CGAffineTransformMakeScale(self.scale, self.scale)); - UIImage *result; - if ([self.asset valueForProperty:ALAssetPropertyType] == ALAssetTypePhoto){ - result = [self resizedImageWithSize:realSize]; - } else { - result = [UIImage imageWithCGImage:self.asset.thumbnail]; - } - if (self.block){ - if (result) { - self.block(result, nil); - } else { - self.block(nil, nil); - } - } -} - --(UIImage *)resizedImageWithSize:(CGSize)targetSize { - ALAssetRepresentation *representation = [self.asset defaultRepresentation]; - size_t bufferSize = (size_t)representation.size; - uint8_t *buffer = malloc(bufferSize); - if ([representation getBytes:buffer fromOffset:0 length:bufferSize error:nil] == 0){ - free(buffer); - return nil; - } - NSData *data = [NSData dataWithBytesNoCopy:buffer length:bufferSize]; - CGImageSourceRef sourceRef = CGImageSourceCreateWithData((__bridge CFDataRef)data, NULL); - - NSDictionary *resizeOptions = @{ - (id)kCGImageSourceCreateThumbnailWithTransform : @YES, - (id)kCGImageSourceCreateThumbnailFromImageAlways : @YES, - (id)kCGImageSourceThumbnailMaxPixelSize : @(MAX(targetSize.width, targetSize.height)), - }; - - CGImageRef resizedImageRef = CGImageSourceCreateThumbnailAtIndex(sourceRef, 0, (__bridge CFDictionaryRef)resizeOptions); - UIImage *resizedImage = [UIImage imageWithCGImage:resizedImageRef scale:self.scale orientation:UIImageOrientationUp]; - CGImageRelease(resizedImageRef); - CFRelease(sourceRef); - return resizedImage; -} - -@end - -@interface WPALAssetImageCacheManager() - -@property (nonatomic, strong) NSOperationQueue *operationQueue; -@property (nonatomic, strong) NSCache *cache; -@property (nonatomic, strong) NSMutableDictionary *runningOperations; - -@end - -@implementation WPALAssetImageCacheManager - -+ (instancetype)sharedInstance { - static id _sharedInstance = nil; - static dispatch_once_t _onceToken; - dispatch_once(&_onceToken, ^{ - _sharedInstance = [[self alloc] init]; - }); - - return _sharedInstance; -} - -- (instancetype)init -{ - self = [super init]; - if (self) { - _operationQueue = [[NSOperationQueue alloc] init]; - _operationQueue.maxConcurrentOperationCount = 1; - _cache = [[NSCache alloc] init]; - _runningOperations = [NSMutableDictionary dictionary]; - } - return self; -} - -- (NSUInteger)requestImageForAsset:(ALAsset *)asset - targetSize:(CGSize)targetSize - scale:(CGFloat)scale - resultHandler:(void (^)(UIImage *result, NSError *error))resultHandler -{ - NSString *identifier = [[asset valueForProperty:ALAssetPropertyAssetURL] absoluteString]; - UIImage *result = [self.cache objectForKey:identifier]; - if (result) { - if (resultHandler) { - resultHandler(result, nil); - } - return 0; - } - NSNumber *operationKey = @(arc4random()); - while (self.runningOperations[operationKey] != nil) { - operationKey = @(arc4random()); - } - WPAssetResizeOperation *resizeOperation = [[WPAssetResizeOperation alloc] initWithAsset:asset - targetSize:targetSize - scale:scale - completionBlock:^(UIImage *result, NSError *error) { - if (result) { - [self.cache setObject:result forKey:identifier]; - } - if (resultHandler) { - resultHandler(result, error); - } - }]; - self.runningOperations[operationKey] = resizeOperation; - [resizeOperation setCompletionBlock:^{ - [self.runningOperations removeObjectForKey:operationKey]; - }]; - [self.operationQueue addOperation:resizeOperation]; - - return [operationKey unsignedIntegerValue]; -} - -- (void)cancelImageRequest:(NSUInteger)requestID -{ - NSOperation *operation = (NSOperation *)self.runningOperations[@(requestID)]; - if (operation) { - [operation cancel]; - } -} - -@end diff --git a/Pod/Classes/WPMediaCollectionViewController.m b/Pod/Classes/WPMediaCollectionViewController.m index 82611398..89ea3855 100644 --- a/Pod/Classes/WPMediaCollectionViewController.m +++ b/Pod/Classes/WPMediaCollectionViewController.m @@ -7,19 +7,12 @@ @import MobileCoreServices; @import AVFoundation; -typedef NS_ENUM(NSUInteger, WPMediaCollectionAlert){ - WPMediaCollectionAlertMediaLibraryPermissionsNeeded, - WPMediaCollectionAlertMediaCapturePermissionsNeeded, - WPMediaCollectionAlertOtherError -}; - @interface WPMediaCollectionViewController () < UIImagePickerControllerDelegate, UINavigationControllerDelegate, WPMediaGroupPickerViewControllerDelegate, - UIPopoverPresentationControllerDelegate, - UIAlertViewDelegate + UIPopoverPresentationControllerDelegate > @property (nonatomic, strong) UICollectionViewFlowLayout *layout; @@ -27,7 +20,6 @@ @interface WPMediaCollectionViewController () @property (nonatomic, strong) WPMediaCaptureCollectionViewCell *captureCell; @property (nonatomic, strong) UIButton *titleButton; @property (nonatomic, strong) UIRefreshControl *refreshControl; -@property (nonatomic, strong) UIPopoverController *popOverController; @property (nonatomic, assign) NSTimeInterval ignoreMediaTimestamp; @property (nonatomic, strong) NSObject *changesObserver; @property (nonatomic, strong) NSIndexPath *firstVisibleCell; @@ -76,19 +68,12 @@ - (void)viewDidLoad self.collectionView.bounces = YES; self.collectionView.alwaysBounceHorizontal = NO; self.collectionView.alwaysBounceVertical = YES; - // HACK: Fix for iOS 7 not respecting the appeareance background color - if (![[self class] isiOS8OrAbove]) { - UIColor * appearanceColor = [[UICollectionView appearanceWhenContainedIn:[WPMediaCollectionViewController class],nil] backgroundColor]; - if (!appearanceColor){ - appearanceColor = [[UICollectionView appearance] backgroundColor]; - } - self.collectionView.backgroundColor = appearanceColor; - } + // Register cell classes [self.collectionView registerClass:[WPMediaCollectionViewCell class] forCellWithReuseIdentifier:NSStringFromClass([WPMediaCollectionViewCell class])]; [self.collectionView registerClass:[WPMediaCaptureCollectionViewCell class] forCellWithReuseIdentifier:NSStringFromClass([WPMediaCaptureCollectionViewCell class])]; - [self setupLayoutForOrientation:self.interfaceOrientation]; + [self setupLayout]; //setup navigation items self.titleButton = [UIButton buttonWithType:UIButtonTypeSystem]; @@ -108,7 +93,7 @@ - (void)viewDidLoad [self refreshData]; } -- (void)setupLayoutForOrientation:(UIInterfaceOrientation)interfaceOrientation +- (void)setupLayout { CGFloat minWidth = MIN (self.view.frame.size.width, self.view.frame.size.height); // Configure collection view layout @@ -116,9 +101,6 @@ - (void)setupLayoutForOrientation:(UIInterfaceOrientation)interfaceOrientation CGFloat spaceBetweenPhotos = 1.0f; CGFloat leftRightInset = 0; CGFloat topBottomInset = 5; - if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) { - numberOfPhotosForLine = 5; - } CGFloat width = floorf((minWidth - (((numberOfPhotosForLine -1) * spaceBetweenPhotos)) + (2*leftRightInset)) / numberOfPhotosForLine); @@ -146,15 +128,13 @@ - (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceO animated:NO]; } -#pragma mark - Actions - -+ (BOOL)isiOS8OrAbove -{ - NSComparisonResult result = [[[UIDevice currentDevice] systemVersion] compare:@"8.0.0" options:NSNumericSearch]; - - return result == NSOrderedSame || result == NSOrderedDescending; +- (void)viewWillLayoutSubviews { + [super viewWillLayoutSubviews]; + [self setupLayout]; } +#pragma mark - Actions + - (void)pullToRefresh:(id)sender { [self refreshData]; @@ -166,21 +146,12 @@ - (void)changeGroup:(UIButton *)sender groupViewController.delegate = self; groupViewController.dataSource = self.dataSource; - if ([[self class] isiOS8OrAbove]) { - groupViewController.modalPresentationStyle = UIModalPresentationPopover; - UIPopoverPresentationController *ppc = groupViewController.popoverPresentationController; - ppc.delegate = self; - ppc.sourceView = sender; - ppc.sourceRect = [sender bounds]; - [self presentViewController:groupViewController animated:YES completion:nil]; - } else if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) { - self.popOverController = [[UIPopoverController alloc] initWithContentViewController:groupViewController]; - [self.popOverController presentPopoverFromRect:[sender bounds] inView:sender permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES]; - - } else { - UINavigationController *groupNavigationController = [[UINavigationController alloc] initWithRootViewController:groupViewController]; - [self presentViewController:groupNavigationController animated:YES completion:nil]; - } + groupViewController.modalPresentationStyle = UIModalPresentationPopover; + UIPopoverPresentationController *ppc = groupViewController.popoverPresentationController; + ppc.delegate = self; + ppc.sourceView = sender; + ppc.sourceRect = [sender bounds]; + [self presentViewController:groupViewController animated:YES completion:nil]; } - (UIModalPresentationStyle)adaptivePresentationStyleForPresentationController:(UIPresentationController *)controller @@ -292,42 +263,29 @@ - (void)showError:(NSError *)error { NSString *message = NSLocalizedString(@"There was a problem when trying to access your media. Please try again later.", @"Explaining to the user there was an generic error accesing media."); NSString *cancelText = NSLocalizedString(@"OK", ""); NSString *otherButtonTitle = nil; - NSInteger tag = WPMediaCollectionAlertOtherError; if (error.domain == WPMediaPickerErrorDomain && error.code == WPMediaErrorCodePermissionsFailed) { - if ([[self class] isiOS8OrAbove]) { - otherButtonTitle = NSLocalizedString(@"Open Settings", @"Go to the settings app"); + otherButtonTitle = NSLocalizedString(@"Open Settings", @"Go to the settings app"); + } + title = NSLocalizedString(@"Media Library", @"Title for alert when access to the media library is not granted by the user"); + message = NSLocalizedString(@"This app needs permission to access your device media library in order to add photos and/or video to your posts. Please change the privacy settings if you wish to allow this.", @"Explaining to the user why the app needs access to the device media library."); + + UIAlertController *alertController = [UIAlertController alertControllerWithTitle:title message:message preferredStyle:UIAlertControllerStyleAlert]; + UIAlertAction *okAction = [UIAlertAction actionWithTitle:cancelText style:UIAlertActionStyleCancel handler:^(UIAlertAction *action) { + if ([self.picker.delegate respondsToSelector:@selector(mediaPickerControllerDidCancel:)]) { + [self.picker.delegate mediaPickerControllerDidCancel:self.picker]; } - title = NSLocalizedString(@"Media Library", @"Title for alert when access to the media library is not granted by the user"); - message = NSLocalizedString(@"This app needs permission to access your device media library in order to add photos and/or video to your posts. Please change the privacy settings if you wish to allow this.", @"Explaining to the user why the app needs access to the device media library."); - tag = WPMediaCollectionAlertMediaLibraryPermissionsNeeded; - } - if ([[self class] isiOS8OrAbove]) { - UIAlertController *alertController = [UIAlertController alertControllerWithTitle:title message:message preferredStyle:UIAlertControllerStyleAlert]; - UIAlertAction *okAction = [UIAlertAction actionWithTitle:cancelText style:UIAlertActionStyleCancel handler:^(UIAlertAction *action) { - if ([self.picker.delegate respondsToSelector:@selector(mediaPickerControllerDidCancel:)]) { - [self.picker.delegate mediaPickerControllerDidCancel:self.picker]; - } + }]; + [alertController addAction:okAction]; + + if (otherButtonTitle) { + UIAlertAction *otherAction = [UIAlertAction actionWithTitle:otherButtonTitle style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) { + NSURL *settingsURL = [NSURL URLWithString:UIApplicationOpenSettingsURLString]; + [[UIApplication sharedApplication] openURL:settingsURL]; }]; - [alertController addAction:okAction]; - - if (otherButtonTitle) { - UIAlertAction *otherAction = [UIAlertAction actionWithTitle:otherButtonTitle style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) { - NSURL *settingsURL = [NSURL URLWithString:UIApplicationOpenSettingsURLString]; - [[UIApplication sharedApplication] openURL:settingsURL]; - }]; - [alertController addAction:otherAction]; - [self presentViewController:alertController animated:YES completion:nil]; - } - } else { - UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:title - message:message - delegate:self - cancelButtonTitle:cancelText - otherButtonTitles:otherButtonTitle, nil]; - alertView.tag = tag; - [alertView show]; + [alertController addAction:otherAction]; } + [self presentViewController:alertController animated:YES completion:nil]; } - (void)refreshSelection @@ -611,18 +569,22 @@ - (void)captureMedia - (void)showAlertAboutMediaCapturePermission { - NSString *otherButtonTitle = nil; - if ([[self class] isiOS8OrAbove]) { - otherButtonTitle = NSLocalizedString(@"Open Settings", @"Go to the settings app"); - } - - UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:NSLocalizedString(@"Media Capture", @"Title for alert when access to media capture is not granted") - message:NSLocalizedString(@"This app needs permission to access the Camera to capture new media, please change the privacy settings if you wish to allow this.", @"") - delegate:self - cancelButtonTitle:NSLocalizedString(@"OK", nil) - otherButtonTitles:otherButtonTitle, nil]; - alertView.tag = WPMediaCollectionAlertMediaCapturePermissionsNeeded; - [alertView show]; + NSString *title = NSLocalizedString(@"Media Capture", @"Title for alert when access to media capture is not granted"); + NSString *message =NSLocalizedString(@"This app needs permission to access the Camera to capture new media, please change the privacy settings if you wish to allow this.", @""); + UIAlertController *alertController = [UIAlertController alertControllerWithTitle:title message:message preferredStyle:UIAlertControllerStyleAlert]; + UIAlertAction *okAction = [UIAlertAction actionWithTitle:NSLocalizedString(@"OK", "Confirmation of action") style:UIAlertActionStyleCancel handler:nil]; + [alertController addAction:okAction]; + + NSString *otherButtonTitle = NSLocalizedString(@"Open Settings", @"Go to the settings app"); + UIAlertAction *otherAction = [UIAlertAction actionWithTitle:otherButtonTitle + style:UIAlertActionStyleDefault + handler:^(UIAlertAction *action) { + NSURL *settingsURL = [NSURL URLWithString:UIApplicationOpenSettingsURLString]; + [[UIApplication sharedApplication] openURL:settingsURL]; + }]; + [alertController addAction:otherAction]; + + [self presentViewController:alertController animated:YES completion:nil]; } - (void)processMediaCaptured:(NSDictionary *)info @@ -713,68 +675,20 @@ - (void)imagePickerControllerDidCancel:(UIImagePickerController *)picker - (void)mediaGroupPickerViewController:(WPMediaGroupPickerViewController *)picker didPickGroup:(id)group { if (group == [self.dataSource selectedGroup]){ - if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad - && ![[self class] isiOS8OrAbove]) { - [self.popOverController dismissPopoverAnimated:YES]; - } else { - [self dismissViewControllerAnimated:YES completion:nil]; - } + [self dismissViewControllerAnimated:YES completion:nil]; return; } self.refreshGroupFirstTime = YES; [self.dataSource setSelectedGroup:group]; [self refreshTitle]; - if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad - && ![[self class] isiOS8OrAbove]) { - [self.popOverController dismissPopoverAnimated:YES]; + [self dismissViewControllerAnimated:YES completion:^{ [self refreshData]; - } else { - [self dismissViewControllerAnimated:YES completion:^{ - [self refreshData]; - }]; - } - + }]; } - (void)mediaGroupPickerViewControllerDidCancel:(WPMediaGroupPickerViewController *)picker { - if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad - && ![[self class] isiOS8OrAbove]) { - [self.popOverController dismissPopoverAnimated:YES]; - } else { - [self dismissViewControllerAnimated:YES completion:nil]; - } + [self dismissViewControllerAnimated:YES completion:nil]; } -#pragma mark - UIAlertViewDelegate - -- (void)alertView:(UIAlertView *)alertView didDismissWithButtonIndex:(NSInteger)buttonIndex { - switch (alertView.tag) { - case WPMediaCollectionAlertMediaLibraryPermissionsNeeded: - { - if (alertView.cancelButtonIndex == buttonIndex){ - if ([self.picker.delegate respondsToSelector:@selector(mediaPickerControllerDidCancel:)]) { - [self.picker.delegate mediaPickerControllerDidCancel:self.picker]; - } - } else if (alertView.firstOtherButtonIndex == buttonIndex) { - if ([self.picker.delegate respondsToSelector:@selector(mediaPickerControllerDidCancel:)]) { - [self.picker.delegate mediaPickerControllerDidCancel:self.picker]; - } - NSURL *settingsURL = [NSURL URLWithString:UIApplicationOpenSettingsURLString]; - [[UIApplication sharedApplication] openURL:settingsURL]; - } - } break; - case WPMediaCollectionAlertMediaCapturePermissionsNeeded: - { - if (alertView.firstOtherButtonIndex == buttonIndex) { - NSURL *settingsURL = [NSURL URLWithString:UIApplicationOpenSettingsURLString]; - [[UIApplication sharedApplication] openURL:settingsURL]; - } - } break; - - - default: - break; - } -} @end diff --git a/Pod/Classes/WPMediaPicker.h b/Pod/Classes/WPMediaPicker.h index 845b9d80..e51c2912 100644 --- a/Pod/Classes/WPMediaPicker.h +++ b/Pod/Classes/WPMediaPicker.h @@ -5,6 +5,5 @@ #import "WPMediaCollectionDataSource.h" #import "WPMediaCollectionViewCell.h" #import "WPPHAssetDataSource.h" -#import "WPALAssetDataSource.h" #endif /* _WPMEDIAPICKER_ */ diff --git a/Pod/Classes/WPMediaPickerViewController.m b/Pod/Classes/WPMediaPickerViewController.m index 17f4ad70..cb8e9629 100644 --- a/Pod/Classes/WPMediaPickerViewController.m +++ b/Pod/Classes/WPMediaPickerViewController.m @@ -1,6 +1,5 @@ #import "WPMediaPickerViewController.h" #import "WPMediaCollectionViewController.h" -#import "WPALAssetDataSource.h" #import "WPPHAssetDataSource.h" @interface WPMediaPickerViewController () @@ -61,7 +60,7 @@ - (void)setupNavigationController static id assetSource = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ - assetSource = [[WPALAssetDataSource alloc] init]; + assetSource = [[WPPHAssetDataSource alloc] init]; }); return assetSource; } diff --git a/README.md b/README.md index fb0d8b3c..202a2b09 100644 --- a/README.md +++ b/README.md @@ -71,15 +71,6 @@ Just use the standard appearance methods from UIKIT. Here is an example how to c [[UIActivityIndicatorView appearanceWhenContainedIn:[WPMediaCollectionViewController class],nil] setColor:[UIColor grayColor]]; ```` -### How to use the Photos framework instead of the AssetLibrary framework - -Before you present the picker do the following: - -```` objective-c -self.customDataSource = [[WPPHAssetDataSource alloc] init]; -mediaPicker.dataSource = self.customDataSource; -```` - ### How to use a custom data source for the picker If you have a custom database of media and you want to display it using the WPMediaPicker you need to to implement the following protocols around your data: @@ -103,9 +94,9 @@ To run the example project, clone the repo, and run `pod install` from the Examp ## Requirements * ARC - * AssetsLibrary, MediaPlayer frameworks and optionally if you are in iOS 8 and above the Photos framework + * Photos, AVFoundation, ImageIO * XCode 6 - * iOS 7 + * iOS 8 or above ## Author diff --git a/WPMediaPicker.podspec b/WPMediaPicker.podspec index bd4add62..9b6c03d5 100644 --- a/WPMediaPicker.podspec +++ b/WPMediaPicker.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "WPMediaPicker" - s.version = "0.7.3" + s.version = "0.8.0" s.summary = "WPMediaPicker is an iOS controller that allows capture and picking of media assets." s.description = <<-DESC WPMediaPicker is an iOS controller that allows capture and picking of media assets. @@ -14,7 +14,7 @@ Pod::Spec.new do |s| s.author = { "WordPress" => "mobile@automattic.com" } s.source = { :git => "https://github.com/wordpress-mobile/MediaPicker-iOS.git", :tag => s.version.to_s } - s.platform = :ios, '7.0' + s.platform = :ios, '8.0' s.requires_arc = true s.source_files = 'Pod/Classes' @@ -23,6 +23,5 @@ Pod::Spec.new do |s| } s.public_header_files = 'Pod/Classes/**/*.h' - s.frameworks = 'UIKit', 'AssetsLibrary', 'AVFoundation', 'ImageIO' - s.weak_framework = 'Photos' + s.frameworks = 'UIKit', 'Photos', 'AVFoundation', 'ImageIO' end