Tusker/GMImagePicker/GMImagePickerController.m

389 lines
16 KiB
Objective-C

//
// GMImagePickerController.m
// GMPhotoPicker
//
// Created by Guillermo Muntaner Perelló on 19/09/14.
// Copyright (c) 2014 Guillermo Muntaner Perelló. All rights reserved.
//
#import <MobileCoreServices/MobileCoreServices.h>
#import "GMImagePickerController.h"
#import "GMAlbumsViewController.h"
#import <Photos/Photos.h>
@interface GMImagePickerController () <UINavigationControllerDelegate, UIImagePickerControllerDelegate, UIAlertViewDelegate>
@end
@implementation GMImagePickerController
- (id)init
{
if (self = [super init]) {
_selectedAssets = [[NSMutableArray alloc] init];
// Default values:
_displaySelectionInfoToolbar = YES;
_displayAlbumsNumberOfAssets = YES;
_autoDisableDoneButton = YES;
_allowsMultipleSelection = YES;
_confirmSingleSelection = NO;
_showCameraButton = NO;
// Grid configuration:
_colsInPortrait = 3;
_colsInLandscape = 5;
_minimumInteritemSpacing = 2.0;
// Sample of how to select the collections you want to display:
_customSmartCollections = @[@(PHAssetCollectionSubtypeSmartAlbumFavorites),
@(PHAssetCollectionSubtypeSmartAlbumRecentlyAdded),
@(PHAssetCollectionSubtypeSmartAlbumVideos),
@(PHAssetCollectionSubtypeSmartAlbumSlomoVideos),
@(PHAssetCollectionSubtypeSmartAlbumTimelapses),
@(PHAssetCollectionSubtypeSmartAlbumBursts),
@(PHAssetCollectionSubtypeSmartAlbumPanoramas)];
// If you don't want to show smart collections, just put _customSmartCollections to nil;
//_customSmartCollections=nil;
// Which media types will display
_mediaTypes = @[@(PHAssetMediaTypeAudio),
@(PHAssetMediaTypeVideo),
@(PHAssetMediaTypeImage)];
self.preferredContentSize = kPopoverContentSize;
// UI Customisation
_pickerBackgroundColor = [UIColor whiteColor];
_pickerTextColor = [UIColor darkTextColor];
_pickerFontName = @"HelveticaNeue";
_pickerBoldFontName = @"HelveticaNeue-Bold";
_pickerFontNormalSize = 14.0f;
_pickerFontHeaderSize = 17.0f;
_navigationBarBackgroundColor = [UIColor whiteColor];
_navigationBarTextColor = [UIColor darkTextColor];
_navigationBarTintColor = [UIColor darkTextColor];
_toolbarBarTintColor = [UIColor whiteColor];
_toolbarTextColor = [UIColor darkTextColor];
_toolbarTintColor = [UIColor darkTextColor];
_pickerStatusBarStyle = UIStatusBarStyleDefault;
[self setupNavigationController];
}
return self;
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
// Ensure nav and toolbar customisations are set. Defaults are in place, but the user may have changed them
self.view.backgroundColor = _pickerBackgroundColor;
_navigationController.toolbar.translucent = YES;
_navigationController.toolbar.barTintColor = _toolbarBarTintColor;
_navigationController.toolbar.tintColor = _toolbarTintColor;
_navigationController.navigationBar.backgroundColor = _navigationBarBackgroundColor;
_navigationController.navigationBar.tintColor = _navigationBarTintColor;
NSDictionary *attributes;
if (_useCustomFontForNavigationBar) {
attributes = @{NSForegroundColorAttributeName : _navigationBarTextColor,
NSFontAttributeName : [UIFont fontWithName:_pickerBoldFontName size:_pickerFontHeaderSize]};
} else {
attributes = @{NSForegroundColorAttributeName : _navigationBarTextColor};
}
_navigationController.navigationBar.titleTextAttributes = attributes;
[self updateToolbar];
}
- (UIStatusBarStyle)preferredStatusBarStyle {
return _pickerStatusBarStyle;
}
#pragma mark - Setup Navigation Controller
- (void)setupNavigationController
{
GMAlbumsViewController *albumsViewController = [[GMAlbumsViewController alloc] init];
_navigationController = [[UINavigationController alloc] initWithRootViewController:albumsViewController];
_navigationController.delegate = self;
[_navigationController.navigationBar setTranslucent:NO];
[_navigationController willMoveToParentViewController:self];
[_navigationController.view setFrame:self.view.frame];
[self.view addSubview:_navigationController.view];
[self addConstraintsToChildViewControllersView:_navigationController.view];
[self addChildViewController:_navigationController];
[_navigationController didMoveToParentViewController:self];
}
- (void)addConstraintsToChildViewControllersView:(UIView *)view {
view.translatesAutoresizingMaskIntoConstraints = NO;
NSArray * hConstraints = [NSLayoutConstraint constraintsWithVisualFormat:@"H:|-0-[view]-0-|" options:0 metrics:0 views:NSDictionaryOfVariableBindings(view)];
NSLayoutConstraint * topConstraint = [NSLayoutConstraint constraintWithItem:view attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:self.topLayoutGuide attribute:NSLayoutAttributeBottom multiplier:1 constant:0];
NSLayoutConstraint * bottomConstraint = [NSLayoutConstraint constraintWithItem:view attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual toItem:view.superview attribute:NSLayoutAttributeBottom multiplier:1 constant:0];
[view.superview addConstraints:@[topConstraint,bottomConstraint]];
[view.superview addConstraints:hConstraints];
}
#pragma mark - UIAlertViewDelegate
-(void)alertView:(UIAlertView *)alertView didDismissWithButtonIndex:(NSInteger)buttonIndex
{
if (buttonIndex == 1) {
// Only if OK was pressed do we want to completge the selection
[self finishPickingAssets:self];
}
}
#pragma mark - Select / Deselect Asset
- (void)selectAsset:(PHAsset *)asset
{
[self.selectedAssets insertObject:asset atIndex:self.selectedAssets.count];
[self updateDoneButton];
if (!self.allowsMultipleSelection) {
if (self.confirmSingleSelection) {
NSString *message = self.confirmSingleSelectionPrompt ? self.confirmSingleSelectionPrompt : [NSString stringWithFormat:NSLocalizedStringFromTableInBundle(@"picker.confirm.message", @"GMImagePicker", [NSBundle bundleForClass:GMImagePickerController.class], @"Do you want to select the image you tapped on?")];
[[[UIAlertView alloc] initWithTitle:[NSString stringWithFormat:NSLocalizedStringFromTableInBundle(@"picker.confirm.title", @"GMImagePicker", [NSBundle bundleForClass:GMImagePickerController.class], @"Are You Sure?")]
message:message
delegate:self
cancelButtonTitle:[NSString stringWithFormat:NSLocalizedStringFromTableInBundle(@"picker.action.no", @"GMImagePicker", [NSBundle bundleForClass:GMImagePickerController.class], @"No")]
otherButtonTitles:[NSString stringWithFormat:NSLocalizedStringFromTableInBundle(@"picker.action.yes", @"GMImagePicker", [NSBundle bundleForClass:GMImagePickerController.class], @"Yes")], nil] show];
} else {
[self finishPickingAssets:self];
}
} else if (self.displaySelectionInfoToolbar || self.showCameraButton) {
[self updateToolbar];
}
}
- (void)deselectAsset:(PHAsset *)asset
{
[self.selectedAssets removeObjectAtIndex:[self.selectedAssets indexOfObject:asset]];
if (self.selectedAssets.count == 0) {
[self updateDoneButton];
}
if (self.displaySelectionInfoToolbar || self.showCameraButton) {
[self updateToolbar];
}
}
- (void)updateDoneButton
{
if (!self.allowsMultipleSelection) {
return;
}
UINavigationController *nav = (UINavigationController *)self.childViewControllers[0];
for (UIViewController *viewController in nav.viewControllers) {
viewController.navigationItem.rightBarButtonItem.enabled = (self.autoDisableDoneButton ? self.selectedAssets.count > 0 : TRUE);
}
}
- (void)updateToolbar
{
if (!self.allowsMultipleSelection && !self.showCameraButton) {
return;
}
UINavigationController *nav = (UINavigationController *)self.childViewControllers[0];
for (UIViewController *viewController in nav.viewControllers) {
NSUInteger index = 1;
if (_showCameraButton) {
index++;
}
[[viewController.toolbarItems objectAtIndex:index] setTitleTextAttributes:[self toolbarTitleTextAttributes] forState:UIControlStateNormal];
[[viewController.toolbarItems objectAtIndex:index] setTitleTextAttributes:[self toolbarTitleTextAttributes] forState:UIControlStateDisabled];
[[viewController.toolbarItems objectAtIndex:index] setTitle:[self toolbarTitle]];
[viewController.navigationController setToolbarHidden:(self.selectedAssets.count == 0 && !self.showCameraButton) animated:YES];
}
}
#pragma mark - User finish Actions
- (void)dismiss:(id)sender
{
if ([self.delegate respondsToSelector:@selector(assetsPickerControllerDidCancel:)]) {
[self.delegate assetsPickerControllerDidCancel:self];
}
[self.presentingViewController dismissViewControllerAnimated:YES completion:nil];
}
- (void)finishPickingAssets:(id)sender
{
if ([self.delegate respondsToSelector:@selector(assetsPickerController:didFinishPickingAssets:)]) {
[self.delegate assetsPickerController:self didFinishPickingAssets:self.selectedAssets];
}
}
#pragma mark - Toolbar Title
- (NSPredicate *)predicateOfAssetType:(PHAssetMediaType)type
{
return [NSPredicate predicateWithBlock:^BOOL(PHAsset *asset, NSDictionary *bindings) {
return (asset.mediaType == type);
}];
}
- (NSString *)toolbarTitle
{
if (self.selectedAssets.count == 0) {
return nil;
}
NSPredicate *photoPredicate = [self predicateOfAssetType:PHAssetMediaTypeImage];
NSPredicate *videoPredicate = [self predicateOfAssetType:PHAssetMediaTypeVideo];
NSInteger nImages = [self.selectedAssets filteredArrayUsingPredicate:photoPredicate].count;
NSInteger nVideos = [self.selectedAssets filteredArrayUsingPredicate:videoPredicate].count;
if (nImages > 0 && nVideos > 0) {
return [NSString stringWithFormat:NSLocalizedStringFromTableInBundle(@"picker.selection.multiple-items", @"GMImagePicker", [NSBundle bundleForClass:GMImagePickerController.class], @"%@ Items Selected" ), @(nImages + nVideos)];
} else if (nImages > 1) {
return [NSString stringWithFormat:NSLocalizedStringFromTableInBundle(@"picker.selection.multiple-photos", @"GMImagePicker", [NSBundle bundleForClass:GMImagePickerController.class], @"%@ Photos Selected"), @(nImages)];
} else if (nImages == 1) {
return NSLocalizedStringFromTableInBundle(@"picker.selection.single-photo", @"GMImagePicker", [NSBundle bundleForClass:GMImagePickerController.class], @"1 Photo Selected" );
} else if (nVideos > 1) {
return [NSString stringWithFormat:NSLocalizedStringFromTableInBundle(@"picker.selection.multiple-videos", @"GMImagePicker", [NSBundle bundleForClass:GMImagePickerController.class], @"%@ Videos Selected"), @(nVideos)];
} else if (nVideos == 1) {
return NSLocalizedStringFromTableInBundle(@"picker.selection.single-video", @"GMImagePicker", [NSBundle bundleForClass:GMImagePickerController.class], @"1 Video Selected");
} else {
return nil;
}
}
#pragma mark - Toolbar Items
- (void)cameraButtonPressed:(UIBarButtonItem *)button
{
if (![UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]) {
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"No Camera!"
message:@"Sorry, this device does not have a camera."
delegate:nil
cancelButtonTitle:@"OK"
otherButtonTitles:nil];
[alert show];
return;
}
// This allows the selection of the image taken to be better seen if the user is not already in that VC
if (self.autoSelectCameraImages && [self.navigationController.topViewController isKindOfClass:[GMAlbumsViewController class]]) {
[((GMAlbumsViewController *)self.navigationController.topViewController) selectAllAlbumsCell];
}
UIImagePickerController *picker = [[UIImagePickerController alloc] init];
picker.sourceType = UIImagePickerControllerSourceTypeCamera;
picker.mediaTypes = @[(NSString *)kUTTypeImage];
picker.allowsEditing = self.allowsEditingCameraImages;
picker.delegate = self;
picker.modalPresentationStyle = UIModalPresentationPopover;
UIPopoverPresentationController *popPC = picker.popoverPresentationController;
popPC.permittedArrowDirections = UIPopoverArrowDirectionAny;
popPC.barButtonItem = button;
[self showViewController:picker sender:button];
}
- (NSDictionary *)toolbarTitleTextAttributes {
return @{NSForegroundColorAttributeName : _toolbarTextColor,
NSFontAttributeName : [UIFont fontWithName:_pickerFontName size:_pickerFontHeaderSize]};
}
- (UIBarButtonItem *)titleButtonItem
{
UIBarButtonItem *title = [[UIBarButtonItem alloc] initWithTitle:self.toolbarTitle
style:UIBarButtonItemStylePlain
target:nil
action:nil];
NSDictionary *attributes = [self toolbarTitleTextAttributes];
[title setTitleTextAttributes:attributes forState:UIControlStateNormal];
[title setTitleTextAttributes:attributes forState:UIControlStateDisabled];
[title setEnabled:NO];
return title;
}
- (UIBarButtonItem *)spaceButtonItem
{
return [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:nil action:nil];
}
- (UIBarButtonItem *)cameraButtonItem
{
return [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemCamera target:self action:@selector(cameraButtonPressed:)];
}
- (NSArray *)toolbarItems
{
UIBarButtonItem *camera = [self cameraButtonItem];
UIBarButtonItem *title = [self titleButtonItem];
UIBarButtonItem *space = [self spaceButtonItem];
NSMutableArray *items = [[NSMutableArray alloc] init];
if (_showCameraButton) {
[items addObject:camera];
}
[items addObject:space];
[items addObject:title];
[items addObject:space];
return [NSArray arrayWithArray:items];
}
#pragma mark - Camera Delegate
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary<NSString *,id> *)info
{
[picker.presentingViewController dismissViewControllerAnimated:YES completion:nil];
NSString *mediaType = info[UIImagePickerControllerMediaType];
if ([mediaType isEqualToString:(NSString *)kUTTypeImage]) {
UIImage *image = info[UIImagePickerControllerEditedImage] ? : info[UIImagePickerControllerOriginalImage];
UIImageWriteToSavedPhotosAlbum(image,
self,
@selector(image:finishedSavingWithError:contextInfo:),
nil);
}
}
-(void)imagePickerControllerDidCancel:(UIImagePickerController *)picker
{
[picker.presentingViewController dismissViewControllerAnimated:YES completion:nil];
}
-(void)image:(UIImage *)image finishedSavingWithError:(NSError *)error contextInfo:(void *)contextInfo
{
if (error) {
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Image Not Saved"
message:@"Sorry, unable to save the new image!"
delegate:nil
cancelButtonTitle:@"OK"
otherButtonTitles:nil];
[alert show];
}
// Note: The image view will auto refresh as the photo's are being observed in the other VCs
}
@end