Repository: d0ping/DBAttachmentPickerController Branch: develop Commit: 355f3f35897f Files: 73 Total size: 272.7 KB Directory structure: gitextract_ir399rag/ ├── DBAttachmentPickerController.podspec ├── Example/ │ ├── Classes/ │ │ ├── NSDateFormatter+DBLibrary.h │ │ ├── NSDateFormatter+DBLibrary.m │ │ ├── ViewController.h │ │ └── ViewController.m │ ├── DBAttachmentPickerControllerExample/ │ │ ├── AppDelegate.h │ │ ├── AppDelegate.m │ │ ├── Base.lproj/ │ │ │ ├── LaunchScreen.storyboard │ │ │ └── Main.storyboard │ │ ├── Common/ │ │ │ ├── DBAssetImageView.h │ │ │ └── DBAssetImageView.m │ │ ├── DBAttachmentPickerControllerExample.entitlements │ │ ├── Info.plist │ │ ├── main.m │ │ └── ru.lproj/ │ │ └── LaunchScreen.strings │ ├── DBAttachmentPickerControllerExample.xcodeproj/ │ │ ├── project.pbxproj │ │ ├── project.xcworkspace/ │ │ │ └── contents.xcworkspacedata │ │ └── xcuserdata/ │ │ └── denisbogatyrev.xcuserdatad/ │ │ └── xcschemes/ │ │ └── DBAttachmentPickerControllerExample.xcscheme │ └── Resources/ │ └── Assets.xcassets/ │ ├── AppIcon.appiconset/ │ │ └── Contents.json │ ├── AttachFileIcon.imageset/ │ │ └── Contents.json │ └── Contents.json ├── LICENSE ├── README.md └── Source/ ├── Categories/ │ ├── NSBundle+DBLibrary.h │ ├── NSBundle+DBLibrary.m │ ├── NSIndexSet+DBLibrary.h │ ├── NSIndexSet+DBLibrary.m │ ├── UIImage+DBAssetIcons.h │ └── UIImage+DBAssetIcons.m ├── Cells/ │ ├── DBAssetGroupCell.h │ ├── DBAssetGroupCell.m │ ├── DBAssetGroupCell.xib │ ├── DBThumbnailPhotoCell.h │ ├── DBThumbnailPhotoCell.m │ └── DBThumbnailPhotoCell.xib ├── Common/ │ ├── DBAssetImageView.h │ └── DBAssetImageView.m ├── DBAssetPickerController/ │ ├── DBAssetGroupsViewController.h │ ├── DBAssetGroupsViewController.m │ ├── DBAssetGroupsViewController.xib │ ├── DBAssetItemsViewController.h │ ├── DBAssetItemsViewController.m │ ├── DBAssetItemsViewController.xib │ ├── DBAssetPickerController.h │ └── DBAssetPickerController.m ├── DBAttachmentAlertController/ │ ├── DBAttachmentAlertController.h │ └── DBAttachmentAlertController.m ├── DBAttachmentPickerController.h ├── DBAttachmentPickerController.m ├── Localization/ │ ├── de.lproj/ │ │ ├── DBAttachmentPickerController.strings │ │ └── DBAttachmentPickerController.stringsdict │ ├── en.lproj/ │ │ ├── DBAttachmentPickerController.strings │ │ └── DBAttachmentPickerController.stringsdict │ ├── es.lproj/ │ │ ├── DBAttachmentPickerController.strings │ │ └── DBAttachmentPickerController.stringsdict │ ├── fr.lproj/ │ │ ├── DBAttachmentPickerController.strings │ │ └── DBAttachmentPickerController.stringsdict │ ├── ja.lproj/ │ │ ├── DBAttachmentPickerController.strings │ │ └── DBAttachmentPickerController.stringsdict │ ├── pl-PL.lproj/ │ │ ├── DBAttachmentPickerController.strings │ │ └── DBAttachmentPickerController.stringsdict │ ├── pt-BR.lproj/ │ │ ├── DBAttachmentPickerController.strings │ │ └── DBAttachmentPickerController.stringsdict │ ├── ru.lproj/ │ │ ├── DBAttachmentPickerController.strings │ │ └── DBAttachmentPickerController.stringsdict │ ├── uk.lproj/ │ │ ├── DBAttachmentPickerController.strings │ │ └── DBAttachmentPickerController.stringsdict │ ├── zh-Hans.lproj/ │ │ ├── DBAttachmentPickerController.strings │ │ └── DBAttachmentPickerController.stringsdict │ └── zh-Hant.lproj/ │ ├── DBAttachmentPickerController.strings │ └── DBAttachmentPickerController.stringsdict └── Models/ ├── DBAttachment.h └── DBAttachment.m ================================================ FILE CONTENTS ================================================ ================================================ FILE: DBAttachmentPickerController.podspec ================================================ Pod::Spec.new do |s| s.name = 'DBAttachmentPickerController' s.version = '1.1.4' s.authors = { 'Denis Bogatyrev' => 'denis.bogatyrev@gmail.com' } s.summary = 'This powerful component allows to select different types of files from different sources on your device' s.homepage = 'https://github.com/d0ping/DBAttachmentPickerController' s.license = { :type => 'MIT' } s.requires_arc = true s.platform = :ios, '8.0' s.source = { :git => 'https://github.com/d0ping/DBAttachmentPickerController.git', :tag => "#{s.version}" } s.source_files = 'Source/**/*.{h,m}' s.resources = 'Source/**/*.{xib}' s.resource_bundle = { 'DBAttachmentPickerController' => ['Source/Localization/*.lproj'] } s.public_header_files = 'Source/**/*.h' end ================================================ FILE: Example/Classes/NSDateFormatter+DBLibrary.h ================================================ // // NSDateFormatter+DBLibrary.h // DBAttachmentPickerControllerExample // // Created by Denis Bogatyrev on 22.03.16. // Copyright © 2016 Denis Bogatyrev. All rights reserved. // #import @interface NSDateFormatter (DBLibrary) + (NSDateFormatter *)localizedDateTimeFormatter; @end ================================================ FILE: Example/Classes/NSDateFormatter+DBLibrary.m ================================================ // // NSDateFormatter+DBLibrary.m // DBAttachmentPickerControllerExample // // Created by Denis Bogatyrev on 22.03.16. // Copyright © 2016 Denis Bogatyrev. All rights reserved. // #import "NSDateFormatter+DBLibrary.h" @implementation NSDateFormatter (DBLibrary) + (NSDateFormatter *)localizedDateTimeFormatter { static dispatch_once_t onceToken; static NSDateFormatter *dateFormatter; dispatch_once(&onceToken, ^{ dateFormatter = [NSDateFormatter new]; [dateFormatter setDateStyle:NSDateFormatterMediumStyle]; [dateFormatter setTimeStyle:NSDateFormatterMediumStyle]; [dateFormatter setLocale:[NSLocale currentLocale]]; }); return dateFormatter; } @end ================================================ FILE: Example/Classes/ViewController.h ================================================ // // ViewController.h // DBAttachmentPickerControllerExample // // Created by Denis Bogatyrev on 18.03.16. // Copyright © 2016 Denis Bogatyrev. All rights reserved. // #import @interface ViewController : UITableViewController @end ================================================ FILE: Example/Classes/ViewController.m ================================================ // // ViewController.m // DBAttachmentPickerControllerExample // // Created by Denis Bogatyrev on 18.03.16. // Copyright © 2016 Denis Bogatyrev. All rights reserved. // #import "ViewController.h" #import "DBAttachmentPickerController.h" #import "DBAttachment.h" #import "NSDateFormatter+DBLibrary.h" static NSString *const kAttachmentCellIdentifier = @"AttachmentCellID"; @interface AttachmentCell : UITableViewCell @property (weak, nonatomic) IBOutlet UIImageView *thumbnailImageView; @property (weak, nonatomic) IBOutlet UILabel *titleLabel; @property (weak, nonatomic) IBOutlet UILabel *dateLabel; @property (weak, nonatomic) IBOutlet UILabel *sizeLabel; @end @implementation AttachmentCell @end @interface ViewController () @property (strong, nonatomic) NSMutableArray *attachmentArray; @property (strong, nonatomic) DBAttachmentPickerController *pickerController; @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; self.attachmentArray = [[NSMutableArray alloc] initWithCapacity:100]; } #pragma mark - - (IBAction)addAttachmentButtonDidSelect:(UIBarButtonItem *)sender { UIView *senderView = [sender valueForKey:@"view"]; __weak typeof(self) weakSelf = self; DBAttachmentPickerController *attachmentPickerController = [DBAttachmentPickerController attachmentPickerControllerFinishPickingBlock:^(NSArray * _Nonnull attachmentArray) { NSMutableArray *indexPathArray = [NSMutableArray arrayWithCapacity:attachmentArray.count]; NSUInteger currentIndex = weakSelf.attachmentArray.count; for (NSUInteger i = 0; i < attachmentArray.count; i++) { NSIndexPath *indexPath = [NSIndexPath indexPathForRow:currentIndex+i inSection:0]; [indexPathArray addObject:indexPath]; } [weakSelf.attachmentArray addObjectsFromArray:attachmentArray]; [weakSelf.tableView insertRowsAtIndexPaths:indexPathArray withRowAnimation:UITableViewRowAnimationAutomatic]; } cancelBlock:nil]; attachmentPickerController.mediaType = DBAttachmentMediaTypeImage | DBAttachmentMediaTypeVideo; attachmentPickerController.capturedVideoQulity = UIImagePickerControllerQualityTypeHigh; attachmentPickerController.senderView = senderView; attachmentPickerController.allowsMultipleSelection = YES; attachmentPickerController.allowsSelectionFromOtherApps = YES; [attachmentPickerController presentOnViewController:self]; } #pragma mark - UITableView DataSource && Delegate - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { return self.attachmentArray.count; } - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { AttachmentCell *cell = [tableView dequeueReusableCellWithIdentifier:kAttachmentCellIdentifier]; if (cell == nil) { cell = [[AttachmentCell alloc] init]; } [self configureCell:cell atIndexPath:indexPath]; return cell; } - (void)configureCell:(AttachmentCell *)cell atIndexPath:(NSIndexPath *)indexPath { DBAttachment *attachment = self.attachmentArray[indexPath.row]; cell.titleLabel.text = attachment.fileName; cell.sizeLabel.text = attachment.fileSizeStr; cell.dateLabel.text = [[NSDateFormatter localizedDateTimeFormatter] stringFromDate:attachment.creationDate]; CGFloat scale = [UIScreen mainScreen].scale; CGSize scaledThumbnailSize = CGSizeMake( 80.f * scale, 80.f * scale ); [attachment loadThumbnailImageWithTargetSize:scaledThumbnailSize completion:^(UIImage *resultImage) { cell.thumbnailImageView.image = resultImage; }]; } - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { [tableView deselectRowAtIndexPath:indexPath animated:YES]; } @end ================================================ FILE: Example/DBAttachmentPickerControllerExample/AppDelegate.h ================================================ // // AppDelegate.h // DBAttachmentPickerControllerExample // // Created by Denis Bogatyrev on 18.03.16. // Copyright © 2016 Denis Bogatyrev. All rights reserved. // #import @interface AppDelegate : UIResponder @property (strong, nonatomic) UIWindow *window; @end ================================================ FILE: Example/DBAttachmentPickerControllerExample/AppDelegate.m ================================================ // // AppDelegate.m // DBAttachmentPickerControllerExample // // Created by Denis Bogatyrev on 18.03.16. // Copyright © 2016 Denis Bogatyrev. All rights reserved. // #import "AppDelegate.h" @interface AppDelegate () @end @implementation AppDelegate - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { // Override point for customization after application launch. return YES; } - (void)applicationWillResignActive:(UIApplication *)application { // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. } - (void)applicationDidEnterBackground:(UIApplication *)application { // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. } - (void)applicationWillEnterForeground:(UIApplication *)application { // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. } - (void)applicationDidBecomeActive:(UIApplication *)application { // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. } - (void)applicationWillTerminate:(UIApplication *)application { // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. } @end ================================================ FILE: Example/DBAttachmentPickerControllerExample/Base.lproj/LaunchScreen.storyboard ================================================ ================================================ FILE: Example/DBAttachmentPickerControllerExample/Base.lproj/Main.storyboard ================================================ ================================================ FILE: Example/DBAttachmentPickerControllerExample/Common/DBAssetImageView.h ================================================ // // DBAssetImageView.h // DBAttachmentPickerControllerExample // // Created by Denis Bogatyrev on 30.03.16. // Copyright © 2016 Denis Bogatyrev. All rights reserved. // #import #import @interface DBAssetImageView : UIImageView - (void)configureWithAssetMediaType:(PHAssetMediaType)mediaType subtype:(PHAssetMediaSubtype)mediaSubtype; - (void)configureCollectionSubtype:(PHAssetCollectionSubtype)collectionSubtype; @end ================================================ FILE: Example/DBAttachmentPickerControllerExample/Common/DBAssetImageView.m ================================================ // // DBAssetImageView.m // DBAttachmentPickerControllerExample // // Created by Denis Bogatyrev on 30.03.16. // Copyright © 2016 Denis Bogatyrev. All rights reserved. // @import Photos; #import "DBAssetImageView.h" #import "UIImage+DBAssetIcons.h" static const CGFloat kDefaultGradientHeight = 24.f; static const CGFloat kDefaultMediaTypeIconOffset = 4.f; static const CGSize kDefaultMediaTypeIconSize = {16.f, 16.f}; @interface DBAssetImageView () @property (strong, nonatomic) CAGradientLayer *gradient; @property (strong, nonatomic) UIImageView *mediaTypeImageView; @end @implementation DBAssetImageView - (void)awakeFromNib { self.mediaTypeImageView = [[UIImageView alloc] init]; [self addSubview:self.mediaTypeImageView]; self.gradient = [CAGradientLayer layer]; self.gradient.colors = [NSArray arrayWithObjects:(id)[[UIColor clearColor] CGColor], (id)[[[UIColor blackColor] colorWithAlphaComponent:.75f] CGColor], nil]; } - (void)configureWithAssetMediaType:(PHAssetMediaType)mediaType subtype:(PHAssetMediaSubtype)mediaSubtype { UIImage *iconImage = nil; if (mediaSubtype & PHAssetMediaSubtypeVideoHighFrameRate) { iconImage =[UIImage imageOfVideoHighFrameRateIcon]; } else if (mediaSubtype & PHAssetMediaSubtypeVideoTimelapse) { iconImage =[UIImage imageOfVideoTimelapseIcon]; } else if (mediaSubtype == PHAssetMediaSubtypePhotoPanorama) { iconImage =[UIImage imageOfSmartAlbumPanoramasIcon]; } else if (mediaType == PHAssetMediaTypeVideo) { iconImage =[UIImage imageOfVideoIcon]; } self.mediaTypeImageView.image = iconImage; if (iconImage) { [self.layer insertSublayer:self.gradient atIndex:0]; } else { [self.gradient removeFromSuperlayer]; } } - (void)configureCollectionSubtype:(PHAssetCollectionSubtype)collectionSubtype { UIImage *iconImage = nil; switch (collectionSubtype) { case PHAssetCollectionSubtypeSmartAlbumVideos: iconImage =[UIImage imageOfSmartAlbumVideosIcon]; break; case PHAssetCollectionSubtypeSmartAlbumPanoramas: iconImage =[UIImage imageOfSmartAlbumPanoramasIcon]; break; case PHAssetCollectionSubtypeSmartAlbumFavorites: iconImage =[UIImage imageOfSmartAlbumFavoritesIcon]; break; case PHAssetCollectionSubtypeSmartAlbumSlomoVideos: iconImage =[UIImage imageOfSmartAlbumSlomoVideosIcon]; break; case PHAssetCollectionSubtypeSmartAlbumTimelapses: iconImage =[UIImage imageOfSmartAlbumTimelapsesIcon]; break; case PHAssetCollectionSubtypeSmartAlbumSelfPortraits: iconImage =[UIImage imageOfSmartAlbumSelfPortraitsIcon]; break; case PHAssetCollectionSubtypeSmartAlbumBursts: iconImage =[UIImage imageOfSmartAlbumBurstsIcon]; break; case PHAssetCollectionSubtypeSmartAlbumScreenshots: iconImage =[UIImage imageOfSmartAlbumScreenshotsIcon]; break; default: break; } self.mediaTypeImageView.image = iconImage; if (iconImage) { [self.layer insertSublayer:self.gradient atIndex:0]; } else { [self.gradient removeFromSuperlayer]; } } - (void)layoutSubviews { CGSize iconSize = kDefaultMediaTypeIconSize; self.mediaTypeImageView.frame = CGRectMake(kDefaultMediaTypeIconOffset, CGRectGetHeight(self.bounds) - iconSize.height - kDefaultMediaTypeIconOffset, iconSize.width, iconSize.height); self.gradient.frame = CGRectMake(.0f, CGRectGetHeight(self.bounds) - kDefaultGradientHeight, CGRectGetWidth(self.bounds), kDefaultGradientHeight); } @end ================================================ FILE: Example/DBAttachmentPickerControllerExample/DBAttachmentPickerControllerExample.entitlements ================================================ com.apple.developer.icloud-container-identifiers iCloud.$(CFBundleIdentifier) com.apple.developer.icloud-services CloudDocuments com.apple.developer.ubiquity-container-identifiers iCloud.$(CFBundleIdentifier) ================================================ FILE: Example/DBAttachmentPickerControllerExample/Info.plist ================================================ CFBundleDevelopmentRegion en CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIdentifier $(PRODUCT_BUNDLE_IDENTIFIER) CFBundleInfoDictionaryVersion 6.0 CFBundleName $(PRODUCT_NAME) CFBundlePackageType APPL CFBundleShortVersionString 1.1.4 CFBundleSignature ???? CFBundleVersion 1 LSRequiresIPhoneOS UILaunchStoryboardName LaunchScreen UIMainStoryboardFile Main UIRequiredDeviceCapabilities armv7 UISupportedInterfaceOrientations UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeRight UIInterfaceOrientationLandscapeLeft UISupportedInterfaceOrientations~ipad UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight ================================================ FILE: Example/DBAttachmentPickerControllerExample/main.m ================================================ // // main.m // DBAttachmentPickerControllerExample // // Created by Denis Bogatyrev on 18.03.16. // Copyright © 2016 Denis Bogatyrev. All rights reserved. // #import #import "AppDelegate.h" int main(int argc, char * argv[]) { @autoreleasepool { return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); } } ================================================ FILE: Example/DBAttachmentPickerControllerExample/ru.lproj/LaunchScreen.strings ================================================ ================================================ FILE: Example/DBAttachmentPickerControllerExample.xcodeproj/project.pbxproj ================================================ // !$*UTF8*$! { archiveVersion = 1; classes = { }; objectVersion = 46; objects = { /* Begin PBXBuildFile section */ A401D4F51D537AEC006B2909 /* DBAttachmentPickerController.strings in Resources */ = {isa = PBXBuildFile; fileRef = A401D4F71D537AEC006B2909 /* DBAttachmentPickerController.strings */; }; A401D4FE1D5545F5006B2909 /* DBAttachmentPickerController.stringsdict in Resources */ = {isa = PBXBuildFile; fileRef = A401D5001D5545F5006B2909 /* DBAttachmentPickerController.stringsdict */; }; A41C4BD71D557B9A002FEA0B /* NSBundle+DBLibrary.m in Sources */ = {isa = PBXBuildFile; fileRef = A41C4BD61D557B9A002FEA0B /* NSBundle+DBLibrary.m */; }; C3134EB41C9C30990023B9E6 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = C3134EB31C9C30990023B9E6 /* main.m */; }; C3134EB71C9C30990023B9E6 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = C3134EB61C9C30990023B9E6 /* AppDelegate.m */; }; C3134EBD1C9C30990023B9E6 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = C3134EBB1C9C30990023B9E6 /* Main.storyboard */; }; C3134EC21C9C30990023B9E6 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = C3134EC01C9C30990023B9E6 /* LaunchScreen.storyboard */; }; C3134EE81C9C323C0023B9E6 /* NSIndexSet+DBLibrary.m in Sources */ = {isa = PBXBuildFile; fileRef = C3134ECD1C9C323C0023B9E6 /* NSIndexSet+DBLibrary.m */; }; C3134EE91C9C323C0023B9E6 /* UIImage+DBAssetIcons.m in Sources */ = {isa = PBXBuildFile; fileRef = C3134ECF1C9C323C0023B9E6 /* UIImage+DBAssetIcons.m */; }; C3134EEA1C9C323C0023B9E6 /* DBAssetGroupCell.m in Sources */ = {isa = PBXBuildFile; fileRef = C3134ED21C9C323C0023B9E6 /* DBAssetGroupCell.m */; }; C3134EEB1C9C323C0023B9E6 /* DBAssetGroupCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = C3134ED31C9C323C0023B9E6 /* DBAssetGroupCell.xib */; }; C3134EEC1C9C323C0023B9E6 /* DBThumbnailPhotoCell.m in Sources */ = {isa = PBXBuildFile; fileRef = C3134ED51C9C323C0023B9E6 /* DBThumbnailPhotoCell.m */; }; C3134EED1C9C323C0023B9E6 /* DBThumbnailPhotoCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = C3134ED61C9C323C0023B9E6 /* DBThumbnailPhotoCell.xib */; }; C3134EF41C9C323C0023B9E6 /* DBAttachmentPickerController.m in Sources */ = {isa = PBXBuildFile; fileRef = C3134EE41C9C323C0023B9E6 /* DBAttachmentPickerController.m */; }; C3134EF51C9C323C0023B9E6 /* DBAttachment.m in Sources */ = {isa = PBXBuildFile; fileRef = C3134EE71C9C323C0023B9E6 /* DBAttachment.m */; }; C3134EF91C9C33CA0023B9E6 /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = C3134EF81C9C33CA0023B9E6 /* ViewController.m */; }; C3134EFC1C9C34340023B9E6 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = C3134EFB1C9C34340023B9E6 /* Assets.xcassets */; }; C3849AB51C9C3D8C00D12EEF /* DBAssetGroupsViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = C3849AAB1C9C3D8C00D12EEF /* DBAssetGroupsViewController.m */; }; C3849AB61C9C3D8C00D12EEF /* DBAssetGroupsViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = C3849AAC1C9C3D8C00D12EEF /* DBAssetGroupsViewController.xib */; }; C3849AB71C9C3D8C00D12EEF /* DBAssetItemsViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = C3849AAE1C9C3D8C00D12EEF /* DBAssetItemsViewController.m */; }; C3849AB81C9C3D8C00D12EEF /* DBAssetItemsViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = C3849AAF1C9C3D8C00D12EEF /* DBAssetItemsViewController.xib */; }; C3849AB91C9C3D8C00D12EEF /* DBAssetPickerController.m in Sources */ = {isa = PBXBuildFile; fileRef = C3849AB11C9C3D8C00D12EEF /* DBAssetPickerController.m */; }; C3849ABA1C9C3D8C00D12EEF /* DBAttachmentAlertController.m in Sources */ = {isa = PBXBuildFile; fileRef = C3849AB41C9C3D8C00D12EEF /* DBAttachmentAlertController.m */; }; C3A004DE1CCDE66C00945C1B /* DBAssetImageView.m in Sources */ = {isa = PBXBuildFile; fileRef = C3A004DD1CCDE66C00945C1B /* DBAssetImageView.m */; }; C3A949421CA188B80061EE61 /* CoreLocation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C3A949411CA188B80061EE61 /* CoreLocation.framework */; }; C3A949451CA18D440061EE61 /* NSDateFormatter+DBLibrary.m in Sources */ = {isa = PBXBuildFile; fileRef = C3A949441CA18D440061EE61 /* NSDateFormatter+DBLibrary.m */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ A401D4F41D537A13006B2909 /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/LaunchScreen.strings; sourceTree = ""; }; A401D4F81D537AEF006B2909 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/DBAttachmentPickerController.strings; sourceTree = ""; }; A401D4F91D537AF0006B2909 /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/DBAttachmentPickerController.strings; sourceTree = ""; }; A401D4FF1D5545F5006B2909 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = en; path = en.lproj/DBAttachmentPickerController.stringsdict; sourceTree = ""; }; A401D5011D5545F7006B2909 /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = ru; path = ru.lproj/DBAttachmentPickerController.stringsdict; sourceTree = ""; }; A401D5021D554922006B2909 /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/DBAttachmentPickerController.strings"; sourceTree = ""; }; A401D5041D554AD3006B2909 /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = "zh-Hans"; path = "zh-Hans.lproj/DBAttachmentPickerController.stringsdict"; sourceTree = ""; }; A401D5051D554BD0006B2909 /* zh-Hant */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hant"; path = "zh-Hant.lproj/DBAttachmentPickerController.strings"; sourceTree = ""; }; A401D5061D554BD0006B2909 /* zh-Hant */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = "zh-Hant"; path = "zh-Hant.lproj/DBAttachmentPickerController.stringsdict"; sourceTree = ""; }; A401D5071D554D71006B2909 /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/DBAttachmentPickerController.strings; sourceTree = ""; }; A401D5081D554D71006B2909 /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = es; path = es.lproj/DBAttachmentPickerController.stringsdict; sourceTree = ""; }; A401D5091D554FCD006B2909 /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/DBAttachmentPickerController.strings; sourceTree = ""; }; A401D50B1D555228006B2909 /* ja */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ja; path = ja.lproj/DBAttachmentPickerController.strings; sourceTree = ""; }; A401D50C1D555228006B2909 /* ja */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = ja; path = ja.lproj/DBAttachmentPickerController.stringsdict; sourceTree = ""; }; A401D50D1D5553CA006B2909 /* uk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = uk; path = uk.lproj/DBAttachmentPickerController.strings; sourceTree = ""; }; A401D50E1D5553ED006B2909 /* uk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = uk; path = uk.lproj/DBAttachmentPickerController.stringsdict; sourceTree = ""; }; A401D50F1D55560A006B2909 /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/DBAttachmentPickerController.strings; sourceTree = ""; }; A401D5101D55560A006B2909 /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = fr; path = fr.lproj/DBAttachmentPickerController.stringsdict; sourceTree = ""; }; A41C4BD31D557B1A002FEA0B /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = de; path = de.lproj/DBAttachmentPickerController.stringsdict; sourceTree = ""; }; A41C4BD51D557B9A002FEA0B /* NSBundle+DBLibrary.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSBundle+DBLibrary.h"; sourceTree = ""; }; A41C4BD61D557B9A002FEA0B /* NSBundle+DBLibrary.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSBundle+DBLibrary.m"; sourceTree = ""; }; A43A510B1D76A68B00065358 /* pt-BR */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-BR"; path = "pt-BR.lproj/DBAttachmentPickerController.strings"; sourceTree = ""; }; A43A510C1D76A68B00065358 /* pt-BR */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = "pt-BR"; path = "pt-BR.lproj/DBAttachmentPickerController.stringsdict"; sourceTree = ""; }; C3134EAF1C9C30990023B9E6 /* DBAttachmentPickerControllerExample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = DBAttachmentPickerControllerExample.app; sourceTree = BUILT_PRODUCTS_DIR; }; C3134EB31C9C30990023B9E6 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; C3134EB51C9C30990023B9E6 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; C3134EB61C9C30990023B9E6 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; C3134EBC1C9C30990023B9E6 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; C3134EC11C9C30990023B9E6 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; C3134EC31C9C30990023B9E6 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; C3134EC91C9C30D10023B9E6 /* DBAttachmentPickerControllerExample.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = DBAttachmentPickerControllerExample.entitlements; sourceTree = ""; }; C3134ECC1C9C323C0023B9E6 /* NSIndexSet+DBLibrary.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSIndexSet+DBLibrary.h"; sourceTree = ""; }; C3134ECD1C9C323C0023B9E6 /* NSIndexSet+DBLibrary.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSIndexSet+DBLibrary.m"; sourceTree = ""; }; C3134ECE1C9C323C0023B9E6 /* UIImage+DBAssetIcons.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIImage+DBAssetIcons.h"; sourceTree = ""; }; C3134ECF1C9C323C0023B9E6 /* UIImage+DBAssetIcons.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIImage+DBAssetIcons.m"; sourceTree = ""; }; C3134ED11C9C323C0023B9E6 /* DBAssetGroupCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DBAssetGroupCell.h; sourceTree = ""; }; C3134ED21C9C323C0023B9E6 /* DBAssetGroupCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DBAssetGroupCell.m; sourceTree = ""; }; C3134ED31C9C323C0023B9E6 /* DBAssetGroupCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = DBAssetGroupCell.xib; sourceTree = ""; }; C3134ED41C9C323C0023B9E6 /* DBThumbnailPhotoCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DBThumbnailPhotoCell.h; sourceTree = ""; }; C3134ED51C9C323C0023B9E6 /* DBThumbnailPhotoCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DBThumbnailPhotoCell.m; sourceTree = ""; }; C3134ED61C9C323C0023B9E6 /* DBThumbnailPhotoCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = DBThumbnailPhotoCell.xib; sourceTree = ""; }; C3134EE31C9C323C0023B9E6 /* DBAttachmentPickerController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DBAttachmentPickerController.h; path = ../../Source/DBAttachmentPickerController.h; sourceTree = ""; }; C3134EE41C9C323C0023B9E6 /* DBAttachmentPickerController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = DBAttachmentPickerController.m; path = ../../Source/DBAttachmentPickerController.m; sourceTree = ""; }; C3134EE61C9C323C0023B9E6 /* DBAttachment.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DBAttachment.h; sourceTree = ""; }; C3134EE71C9C323C0023B9E6 /* DBAttachment.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DBAttachment.m; sourceTree = ""; }; C3134EF71C9C33CA0023B9E6 /* ViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; }; C3134EF81C9C33CA0023B9E6 /* ViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; }; C3134EFB1C9C34340023B9E6 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; C3849AAA1C9C3D8C00D12EEF /* DBAssetGroupsViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DBAssetGroupsViewController.h; sourceTree = ""; }; C3849AAB1C9C3D8C00D12EEF /* DBAssetGroupsViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DBAssetGroupsViewController.m; sourceTree = ""; }; C3849AAC1C9C3D8C00D12EEF /* DBAssetGroupsViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = DBAssetGroupsViewController.xib; sourceTree = ""; }; C3849AAD1C9C3D8C00D12EEF /* DBAssetItemsViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DBAssetItemsViewController.h; sourceTree = ""; }; C3849AAE1C9C3D8C00D12EEF /* DBAssetItemsViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DBAssetItemsViewController.m; sourceTree = ""; }; C3849AAF1C9C3D8C00D12EEF /* DBAssetItemsViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = DBAssetItemsViewController.xib; sourceTree = ""; }; C3849AB01C9C3D8C00D12EEF /* DBAssetPickerController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DBAssetPickerController.h; sourceTree = ""; }; C3849AB11C9C3D8C00D12EEF /* DBAssetPickerController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DBAssetPickerController.m; sourceTree = ""; }; C3849AB31C9C3D8C00D12EEF /* DBAttachmentAlertController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DBAttachmentAlertController.h; sourceTree = ""; }; C3849AB41C9C3D8C00D12EEF /* DBAttachmentAlertController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DBAttachmentAlertController.m; sourceTree = ""; }; C3A004DC1CCDE66C00945C1B /* DBAssetImageView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DBAssetImageView.h; sourceTree = ""; }; C3A004DD1CCDE66C00945C1B /* DBAssetImageView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DBAssetImageView.m; sourceTree = ""; }; C3A949411CA188B80061EE61 /* CoreLocation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreLocation.framework; path = System/Library/Frameworks/CoreLocation.framework; sourceTree = SDKROOT; }; C3A949431CA18D440061EE61 /* NSDateFormatter+DBLibrary.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSDateFormatter+DBLibrary.h"; sourceTree = ""; }; C3A949441CA18D440061EE61 /* NSDateFormatter+DBLibrary.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSDateFormatter+DBLibrary.m"; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ C3134EAC1C9C30990023B9E6 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( C3A949421CA188B80061EE61 /* CoreLocation.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ A401D4F11D5378F2006B2909 /* Localization */ = { isa = PBXGroup; children = ( A401D4F71D537AEC006B2909 /* DBAttachmentPickerController.strings */, A401D5001D5545F5006B2909 /* DBAttachmentPickerController.stringsdict */, ); name = Localization; path = ../../Source/Localization; sourceTree = ""; }; C3134EA61C9C30990023B9E6 = { isa = PBXGroup; children = ( C3A949411CA188B80061EE61 /* CoreLocation.framework */, C3134EB11C9C30990023B9E6 /* DBAttachmentPickerControllerExample */, C3134EF61C9C33CA0023B9E6 /* Classes */, C3134EFA1C9C34340023B9E6 /* Resources */, C3134EB01C9C30990023B9E6 /* Products */, ); sourceTree = ""; }; C3134EB01C9C30990023B9E6 /* Products */ = { isa = PBXGroup; children = ( C3134EAF1C9C30990023B9E6 /* DBAttachmentPickerControllerExample.app */, ); name = Products; sourceTree = ""; }; C3134EB11C9C30990023B9E6 /* DBAttachmentPickerControllerExample */ = { isa = PBXGroup; children = ( C3134EB51C9C30990023B9E6 /* AppDelegate.h */, C3134EB61C9C30990023B9E6 /* AppDelegate.m */, C3134EC01C9C30990023B9E6 /* LaunchScreen.storyboard */, C3134EB21C9C30990023B9E6 /* Supporting Files */, ); path = DBAttachmentPickerControllerExample; sourceTree = ""; }; C3134EB21C9C30990023B9E6 /* Supporting Files */ = { isa = PBXGroup; children = ( C3134EC91C9C30D10023B9E6 /* DBAttachmentPickerControllerExample.entitlements */, C3134EC31C9C30990023B9E6 /* Info.plist */, C3134EB31C9C30990023B9E6 /* main.m */, ); name = "Supporting Files"; sourceTree = ""; }; C3134ECA1C9C321C0023B9E6 /* DBAttachmentPickerController */ = { isa = PBXGroup; children = ( C3134EE31C9C323C0023B9E6 /* DBAttachmentPickerController.h */, C3134EE41C9C323C0023B9E6 /* DBAttachmentPickerController.m */, C3134ECB1C9C323C0023B9E6 /* Categories */, C3A004DB1CCDE66C00945C1B /* Common */, C3134EE51C9C323C0023B9E6 /* Models */, C3134ED01C9C323C0023B9E6 /* Cells */, C3849AB21C9C3D8C00D12EEF /* DBAttachmentAlertController */, C3849AA91C9C3D8C00D12EEF /* DBAssetPickerController */, A401D4F11D5378F2006B2909 /* Localization */, ); name = DBAttachmentPickerController; path = ../DBAttachmentPickerControllerExample; sourceTree = ""; }; C3134ECB1C9C323C0023B9E6 /* Categories */ = { isa = PBXGroup; children = ( C3134ECC1C9C323C0023B9E6 /* NSIndexSet+DBLibrary.h */, C3134ECD1C9C323C0023B9E6 /* NSIndexSet+DBLibrary.m */, C3134ECE1C9C323C0023B9E6 /* UIImage+DBAssetIcons.h */, C3134ECF1C9C323C0023B9E6 /* UIImage+DBAssetIcons.m */, A41C4BD51D557B9A002FEA0B /* NSBundle+DBLibrary.h */, A41C4BD61D557B9A002FEA0B /* NSBundle+DBLibrary.m */, ); name = Categories; path = ../../Source/Categories; sourceTree = ""; }; C3134ED01C9C323C0023B9E6 /* Cells */ = { isa = PBXGroup; children = ( C3134ED11C9C323C0023B9E6 /* DBAssetGroupCell.h */, C3134ED21C9C323C0023B9E6 /* DBAssetGroupCell.m */, C3134ED31C9C323C0023B9E6 /* DBAssetGroupCell.xib */, C3134ED41C9C323C0023B9E6 /* DBThumbnailPhotoCell.h */, C3134ED51C9C323C0023B9E6 /* DBThumbnailPhotoCell.m */, C3134ED61C9C323C0023B9E6 /* DBThumbnailPhotoCell.xib */, ); name = Cells; path = ../../Source/Cells; sourceTree = ""; }; C3134EE51C9C323C0023B9E6 /* Models */ = { isa = PBXGroup; children = ( C3134EE61C9C323C0023B9E6 /* DBAttachment.h */, C3134EE71C9C323C0023B9E6 /* DBAttachment.m */, ); name = Models; path = ../../Source/Models; sourceTree = ""; }; C3134EF61C9C33CA0023B9E6 /* Classes */ = { isa = PBXGroup; children = ( C3134ECA1C9C321C0023B9E6 /* DBAttachmentPickerController */, C3134EF71C9C33CA0023B9E6 /* ViewController.h */, C3134EF81C9C33CA0023B9E6 /* ViewController.m */, C3134EBB1C9C30990023B9E6 /* Main.storyboard */, C3A949431CA18D440061EE61 /* NSDateFormatter+DBLibrary.h */, C3A949441CA18D440061EE61 /* NSDateFormatter+DBLibrary.m */, ); path = Classes; sourceTree = ""; }; C3134EFA1C9C34340023B9E6 /* Resources */ = { isa = PBXGroup; children = ( C3134EFB1C9C34340023B9E6 /* Assets.xcassets */, ); path = Resources; sourceTree = ""; }; C3849AA91C9C3D8C00D12EEF /* DBAssetPickerController */ = { isa = PBXGroup; children = ( C3849AB01C9C3D8C00D12EEF /* DBAssetPickerController.h */, C3849AB11C9C3D8C00D12EEF /* DBAssetPickerController.m */, C3849AAA1C9C3D8C00D12EEF /* DBAssetGroupsViewController.h */, C3849AAB1C9C3D8C00D12EEF /* DBAssetGroupsViewController.m */, C3849AAC1C9C3D8C00D12EEF /* DBAssetGroupsViewController.xib */, C3849AAD1C9C3D8C00D12EEF /* DBAssetItemsViewController.h */, C3849AAE1C9C3D8C00D12EEF /* DBAssetItemsViewController.m */, C3849AAF1C9C3D8C00D12EEF /* DBAssetItemsViewController.xib */, ); name = DBAssetPickerController; path = ../../Source/DBAssetPickerController; sourceTree = ""; }; C3849AB21C9C3D8C00D12EEF /* DBAttachmentAlertController */ = { isa = PBXGroup; children = ( C3849AB31C9C3D8C00D12EEF /* DBAttachmentAlertController.h */, C3849AB41C9C3D8C00D12EEF /* DBAttachmentAlertController.m */, ); name = DBAttachmentAlertController; path = ../../Source/DBAttachmentAlertController; sourceTree = ""; }; C3A004DB1CCDE66C00945C1B /* Common */ = { isa = PBXGroup; children = ( C3A004DC1CCDE66C00945C1B /* DBAssetImageView.h */, C3A004DD1CCDE66C00945C1B /* DBAssetImageView.m */, ); path = Common; sourceTree = ""; }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ C3134EAE1C9C30990023B9E6 /* DBAttachmentPickerControllerExample */ = { isa = PBXNativeTarget; buildConfigurationList = C3134EC61C9C30990023B9E6 /* Build configuration list for PBXNativeTarget "DBAttachmentPickerControllerExample" */; buildPhases = ( C3134EAB1C9C30990023B9E6 /* Sources */, C3134EAC1C9C30990023B9E6 /* Frameworks */, C3134EAD1C9C30990023B9E6 /* Resources */, ); buildRules = ( ); dependencies = ( ); name = DBAttachmentPickerControllerExample; productName = DBAttachmentPickerControllerExample; productReference = C3134EAF1C9C30990023B9E6 /* DBAttachmentPickerControllerExample.app */; productType = "com.apple.product-type.application"; }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ C3134EA71C9C30990023B9E6 /* Project object */ = { isa = PBXProject; attributes = { LastUpgradeCheck = 0720; ORGANIZATIONNAME = "Denis Bogatyrev"; TargetAttributes = { C3134EAE1C9C30990023B9E6 = { CreatedOnToolsVersion = 7.2.1; SystemCapabilities = { com.apple.iCloud = { enabled = 1; }; }; }; }; }; buildConfigurationList = C3134EAA1C9C30990023B9E6 /* Build configuration list for PBXProject "DBAttachmentPickerControllerExample" */; compatibilityVersion = "Xcode 3.2"; developmentRegion = English; hasScannedForEncodings = 0; knownRegions = ( en, Base, ru, "zh-Hans", "zh-Hant", es, de, ja, uk, fr, "pt-BR", ); mainGroup = C3134EA61C9C30990023B9E6; productRefGroup = C3134EB01C9C30990023B9E6 /* Products */; projectDirPath = ""; projectRoot = ""; targets = ( C3134EAE1C9C30990023B9E6 /* DBAttachmentPickerControllerExample */, ); }; /* End PBXProject section */ /* Begin PBXResourcesBuildPhase section */ C3134EAD1C9C30990023B9E6 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( C3849AB81C9C3D8C00D12EEF /* DBAssetItemsViewController.xib in Resources */, C3134EC21C9C30990023B9E6 /* LaunchScreen.storyboard in Resources */, C3849AB61C9C3D8C00D12EEF /* DBAssetGroupsViewController.xib in Resources */, C3134EEB1C9C323C0023B9E6 /* DBAssetGroupCell.xib in Resources */, A401D4FE1D5545F5006B2909 /* DBAttachmentPickerController.stringsdict in Resources */, C3134EED1C9C323C0023B9E6 /* DBThumbnailPhotoCell.xib in Resources */, A401D4F51D537AEC006B2909 /* DBAttachmentPickerController.strings in Resources */, C3134EFC1C9C34340023B9E6 /* Assets.xcassets in Resources */, C3134EBD1C9C30990023B9E6 /* Main.storyboard in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXResourcesBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ C3134EAB1C9C30990023B9E6 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( A41C4BD71D557B9A002FEA0B /* NSBundle+DBLibrary.m in Sources */, C3134EF91C9C33CA0023B9E6 /* ViewController.m in Sources */, C3A004DE1CCDE66C00945C1B /* DBAssetImageView.m in Sources */, C3A949451CA18D440061EE61 /* NSDateFormatter+DBLibrary.m in Sources */, C3134EF41C9C323C0023B9E6 /* DBAttachmentPickerController.m in Sources */, C3134EB71C9C30990023B9E6 /* AppDelegate.m in Sources */, C3134EE81C9C323C0023B9E6 /* NSIndexSet+DBLibrary.m in Sources */, C3134EEC1C9C323C0023B9E6 /* DBThumbnailPhotoCell.m in Sources */, C3134EB41C9C30990023B9E6 /* main.m in Sources */, C3134EEA1C9C323C0023B9E6 /* DBAssetGroupCell.m in Sources */, C3134EE91C9C323C0023B9E6 /* UIImage+DBAssetIcons.m in Sources */, C3849AB91C9C3D8C00D12EEF /* DBAssetPickerController.m in Sources */, C3849ABA1C9C3D8C00D12EEF /* DBAttachmentAlertController.m in Sources */, C3134EF51C9C323C0023B9E6 /* DBAttachment.m in Sources */, C3849AB51C9C3D8C00D12EEF /* DBAssetGroupsViewController.m in Sources */, C3849AB71C9C3D8C00D12EEF /* DBAssetItemsViewController.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXSourcesBuildPhase section */ /* Begin PBXVariantGroup section */ A401D4F71D537AEC006B2909 /* DBAttachmentPickerController.strings */ = { isa = PBXVariantGroup; children = ( A401D4F81D537AEF006B2909 /* en */, A401D4F91D537AF0006B2909 /* ru */, A401D5021D554922006B2909 /* zh-Hans */, A401D5051D554BD0006B2909 /* zh-Hant */, A401D5071D554D71006B2909 /* es */, A401D5091D554FCD006B2909 /* de */, A401D50B1D555228006B2909 /* ja */, A401D50D1D5553CA006B2909 /* uk */, A401D50F1D55560A006B2909 /* fr */, A43A510B1D76A68B00065358 /* pt-BR */, ); name = DBAttachmentPickerController.strings; sourceTree = ""; }; A401D5001D5545F5006B2909 /* DBAttachmentPickerController.stringsdict */ = { isa = PBXVariantGroup; children = ( A401D4FF1D5545F5006B2909 /* en */, A401D5011D5545F7006B2909 /* ru */, A401D5041D554AD3006B2909 /* zh-Hans */, A401D5061D554BD0006B2909 /* zh-Hant */, A401D5081D554D71006B2909 /* es */, A401D50C1D555228006B2909 /* ja */, A401D50E1D5553ED006B2909 /* uk */, A401D5101D55560A006B2909 /* fr */, A41C4BD31D557B1A002FEA0B /* de */, A43A510C1D76A68B00065358 /* pt-BR */, ); name = DBAttachmentPickerController.stringsdict; sourceTree = ""; }; C3134EBB1C9C30990023B9E6 /* Main.storyboard */ = { isa = PBXVariantGroup; children = ( C3134EBC1C9C30990023B9E6 /* Base */, ); name = Main.storyboard; path = ../DBAttachmentPickerControllerExample; sourceTree = ""; }; C3134EC01C9C30990023B9E6 /* LaunchScreen.storyboard */ = { isa = PBXVariantGroup; children = ( C3134EC11C9C30990023B9E6 /* Base */, A401D4F41D537A13006B2909 /* ru */, ); name = LaunchScreen.storyboard; sourceTree = ""; }; /* End PBXVariantGroup section */ /* Begin XCBuildConfiguration section */ C3134EC41C9C30990023B9E6 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = dwarf; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_DYNAMIC_NO_PIC = NO; GCC_NO_COMMON_BLOCKS = YES; GCC_OPTIMIZATION_LEVEL = 0; GCC_PREPROCESSOR_DEFINITIONS = ( "DEBUG=1", "$(inherited)", ); GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 9.2; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; TARGETED_DEVICE_FAMILY = "1,2"; }; name = Debug; }; C3134EC51C9C30990023B9E6 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_NO_COMMON_BLOCKS = YES; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 9.2; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; TARGETED_DEVICE_FAMILY = "1,2"; VALIDATE_PRODUCT = YES; }; name = Release; }; C3134EC71C9C30990023B9E6 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CODE_SIGN_ENTITLEMENTS = DBAttachmentPickerControllerExample/DBAttachmentPickerControllerExample.entitlements; CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; INFOPLIST_FILE = DBAttachmentPickerControllerExample/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 8.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = DB.DBAttachmentPickerControllerExample; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE = ""; }; name = Debug; }; C3134EC81C9C30990023B9E6 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CODE_SIGN_ENTITLEMENTS = DBAttachmentPickerControllerExample/DBAttachmentPickerControllerExample.entitlements; CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; INFOPLIST_FILE = DBAttachmentPickerControllerExample/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 8.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = DB.DBAttachmentPickerControllerExample; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE = ""; }; name = Release; }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ C3134EAA1C9C30990023B9E6 /* Build configuration list for PBXProject "DBAttachmentPickerControllerExample" */ = { isa = XCConfigurationList; buildConfigurations = ( C3134EC41C9C30990023B9E6 /* Debug */, C3134EC51C9C30990023B9E6 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; C3134EC61C9C30990023B9E6 /* Build configuration list for PBXNativeTarget "DBAttachmentPickerControllerExample" */ = { isa = XCConfigurationList; buildConfigurations = ( C3134EC71C9C30990023B9E6 /* Debug */, C3134EC81C9C30990023B9E6 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; /* End XCConfigurationList section */ }; rootObject = C3134EA71C9C30990023B9E6 /* Project object */; } ================================================ FILE: Example/DBAttachmentPickerControllerExample.xcodeproj/project.xcworkspace/contents.xcworkspacedata ================================================ ================================================ FILE: Example/DBAttachmentPickerControllerExample.xcodeproj/xcuserdata/denisbogatyrev.xcuserdatad/xcschemes/DBAttachmentPickerControllerExample.xcscheme ================================================ ================================================ FILE: Example/Resources/Assets.xcassets/AppIcon.appiconset/Contents.json ================================================ { "images" : [ { "idiom" : "iphone", "size" : "29x29", "scale" : "2x" }, { "idiom" : "iphone", "size" : "29x29", "scale" : "3x" }, { "idiom" : "iphone", "size" : "40x40", "scale" : "2x" }, { "idiom" : "iphone", "size" : "40x40", "scale" : "3x" }, { "idiom" : "iphone", "size" : "60x60", "scale" : "2x" }, { "idiom" : "iphone", "size" : "60x60", "scale" : "3x" }, { "idiom" : "ipad", "size" : "29x29", "scale" : "1x" }, { "idiom" : "ipad", "size" : "29x29", "scale" : "2x" }, { "idiom" : "ipad", "size" : "40x40", "scale" : "1x" }, { "idiom" : "ipad", "size" : "40x40", "scale" : "2x" }, { "idiom" : "ipad", "size" : "76x76", "scale" : "1x" }, { "idiom" : "ipad", "size" : "76x76", "scale" : "2x" }, { "idiom" : "ipad", "size" : "83.5x83.5", "scale" : "2x" } ], "info" : { "version" : 1, "author" : "xcode" } } ================================================ FILE: Example/Resources/Assets.xcassets/AttachFileIcon.imageset/Contents.json ================================================ { "images" : [ { "idiom" : "universal", "filename" : "AttachFileIcon.png", "scale" : "1x" }, { "idiom" : "universal", "filename" : "AttachFileIcon@2x.png", "scale" : "2x" }, { "idiom" : "universal", "filename" : "AttachFileIcon@3x.png", "scale" : "3x" } ], "info" : { "version" : 1, "author" : "xcode" } } ================================================ FILE: Example/Resources/Assets.xcassets/Contents.json ================================================ { "info" : { "version" : 1, "author" : "xcode" } } ================================================ FILE: LICENSE ================================================ The MIT License (MIT) Copyright (c) 2016 Denis Bogatyrev Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: README.md ================================================ # DBAttachmentPickerController [![Version](https://img.shields.io/cocoapods/v/DBAttachmentPickerController.svg?style=flat)](http://cocoadocs.org/docsets/DBAttachmentPickerController) [![License](https://img.shields.io/cocoapods/l/DBAttachmentPickerController.svg?style=flat)](http://cocoadocs.org/docsets/DBAttachmentPickerController) [![Platform](https://img.shields.io/cocoapods/p/DBAttachmentPickerController.svg?style=flat)](http://cocoadocs.org/docsets/DBAttachmentPickerController) ![Language](https://img.shields.io/badge/Language-%20Objective%20C%20-blue.svg) This powerful component allows to select different types of files from different sources on your device. ![iCloud Documents Capability](Screenshots/Screenshot.jpg) ## Preview ![iCloud Documents Capability](Screenshots/ezgif.com-gif-maker.gif) ## Requirements - iOS 8.0+ ## Adding to your project ### Cocoapods To add DBAttachmentPickerController via [CocoaPods](http://cocoapods.org/) into your project: 1. Add a pod entry for DBAttachmentPickerController to your Podfile `pod 'DBAttachmentPickerController', '~> 1.1.0'` 2. Install the pod by running `pod install` ### Source Files To add DBAttachmentPickerController manually into your project: 1. Download the latest code, using `git clone` 2. Open your project in Xcode, then drag and drop entire contents of the `Source` folder into your project (Make sure to select Copy items when asked if you extracted the code archive outside of your project) ## Usage To use DBAttachmentPickerController in your project you should perform the following steps: 1. Initialize Attachment picker controller (see the [Constructors section](#constructors)) 2. Specify additional options if nedded (see the [Property list section](#property-list)) 3. Present Attachment picker controller (see the [Presentation section](#presentation)) ```objc - (void)addAttachment { // (1) DBAttachmentPickerController *attachmentPickerController = [DBAttachmentPickerController attachmentPickerControllerFinishPickingBlock:^(NSArray * _Nonnull attachmentArray) {...} cancelBlock:^{...}]; // (2) attachmentPickerController.mediaType = DBAttachmentMediaTypeImage | DBAttachmentMediaTypeVideo; attachmentPickerController.capturedVideoQulity = UIImagePickerControllerQualityTypeHigh; attachmentPickerController.allowsMultipleSelection = YES; attachmentPickerController.allowsSelectionFromOtherApps = YES; // (3) [attachmentPickerController presentOnViewController:self]; } ``` ### Constructors To initialize the attachment picker controller you have to call one of the following methods: - `+attachmentPickerControllerFinishPickingBlock:cancelBlock:` - Creates and returns an attachment picker controller. As a result in the finishPickingBlock will be returned array of DBAttachment objects. [DBAttachment](#dbattachment) class provides more opportunities to process selected files but required additional processing to get result; - `+imagePickerControllerFinishPickingBlock:cancelBlock:` - Creates and returns an attachment picker controller with constant media type (image). Other media types will be ignored. As a result in the finishPickingBlock will be returned array of UIImage objects; - `+videoPickerControllerFinishPickingBlock:cancelBlock:` - Creates and returns an attachment picker controller with constant media type (video). Other media types will be ignored. As a result in the finishPickingBlock will be returned array of different objects depending on the source. Required additional processing to get result. ### Property list You can change additional Attachment Picker Controller properties. Full properties list is shown below: - `UIView *senderView` - Used to provide opportunity to correctly calculate position popover view when app works on iPad. You can specify UIButton, UITableViewCell, etc. instance to which the user touched. ATTENTION: The parameter must contain only UIView subclass instance or nil; - `DBAttachmentMediaType mediaType` - It's determine the types of attachments that can be picked. Default is DBAttachmentMediaTypeMaskAll; - `UIImagePickerControllerQualityType capturedVideoQulity` - Used to determine the quality of the captured video from camera. Default is UIImagePickerControllerQualityTypeMedium; - `BOOL allowsSelectionFromOtherApps` - Used to add Other Apps button. ATTENTION: To correctly work this option you must select iCloud Documents capability on project settings. To view detail information, see [Usage Document Picker section](#usage-document-picker-(other-apps-button)). Default is NO. - `BOOL allowsMultipleSelection` - Used to allow multiple selection where it possible. Default is NO. ### Presentation After creation Attachment Picker Controller you should set additional options if needed (see the [Property list section](#property-list)) and present it. - `-presentOnViewController:` - Present attachment picker controller on specify UIViewController. ## Usage Document Picker (Other apps button) To usage Document Picker you must: - set YES value to `AllowsSelectionFromOtherApps` property - turn on the iCloud Documents capabilities in Xcode (see image later) ## DBAttachment The class contain metadata about selected item. You can use following properties to get metadata of file: - `NSString *fileName` - The name of the file. Can be empty; - `NSDate *creationDate` - Creation date of the file. Can be nil; - `NSUInteger fileSize` - Size of the file in byte. Available only for existing files. ATTENTION: If you want get file size for PHAsset or something like that, you should calculate it after getting file data; - `NSString *fileSizeStr` - Formatted string of file size. Can be empty. To get thumbnail image you should call folowing method: `-loadThumbnailImageWithTargetSize:completion:`. Or you can get original image through method `-loadOriginalImageWithCompletion:`. Also you can get original file data if call appropriate method `-originalFileResource`. ## Version history ### 1.1.0 - Added localization. Available following languages: English (Default), Russian, Spanish, German, French, Ukrainian, Chinese (Simplified and Traditional) and Japanese. - Added description for methods and properties on DBAttachment class. ## Contact Denis Bogatyrev (maintainer) - https://github.com/d0ping - denis.bogatyrev@gmail.com ## License DBAttachmentPickerController - Copyright (c) 2016 Denis Bogatyrev Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: Source/Categories/NSBundle+DBLibrary.h ================================================ // // NSBundle+DBLibrary.h // DBAttachmentPickerControllerExample // // Created by Denis Bogatyrev on 06.08.16. // // The MIT License (MIT) // Copyright (c) 2016 Denis Bogatyrev. // // Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), // to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, // and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS // IN THE SOFTWARE. // #import @interface NSBundle (DBLibrary) #undef NSLocalizedString #define NSLocalizedString(key, comment) \ [NSBundle localizedStringForKey:key value:nil table:@"DBAttachmentPickerController" backupBundle:[NSBundle dbAttachmentPickerResourceBundle]] + (instancetype)dbAttachmentPickerBundle; + (instancetype)dbAttachmentPickerResourceBundle; + (NSString *)localizedStringForKey:(NSString *)key value:(NSString *)value table:(NSString *)tableName backupBundle:(NSBundle *)bundle; @end ================================================ FILE: Source/Categories/NSBundle+DBLibrary.m ================================================ // // NSBundle+DBLibrary.m // DBAttachmentPickerControllerExample // // Created by Denis Bogatyrev on 06.08.16. // // The MIT License (MIT) // Copyright (c) 2016 Denis Bogatyrev. // // Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), // to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, // and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS // IN THE SOFTWARE. // #import "NSBundle+DBLibrary.h" #import "DBAttachmentPickerController.h" @implementation NSBundle (DBLibrary) NSString * const kLocalizedStringNotFound = @"kLocalizedStringNotFound"; + (instancetype)dbAttachmentPickerBundle { return [NSBundle bundleForClass:[DBAttachmentPickerController class]]; } + (instancetype)dbAttachmentPickerResourceBundle { NSBundle *bundle = [NSBundle mainBundle]; NSString *bundlePath = [[NSBundle mainBundle] pathForResource:@"DBAttachmentPickerController" ofType:@"bundle"]; if (!bundlePath) { bundlePath = [[NSBundle bundleForClass:[DBAttachmentPickerController class]] pathForResource:@"DBAttachmentPickerController" ofType:@"bundle"]; } if (bundlePath) { bundle = [NSBundle bundleWithPath:bundlePath]; } return bundle; } + (NSString *)localizedStringForKey:(NSString *)key value:(NSString *)value table:(NSString *)tableName backupBundle:(NSBundle *)bundle { // First try main bundle NSString * string = [[NSBundle mainBundle] localizedStringForKey:key value:kLocalizedStringNotFound table:tableName]; // Then try the backup bundle if ([string isEqualToString:kLocalizedStringNotFound]) { string = [bundle localizedStringForKey:key value:kLocalizedStringNotFound table:tableName]; } // Still not found? if ([string isEqualToString:kLocalizedStringNotFound]) { NSLog(@"No localized string for '%@' in '%@'", key, tableName); string = value.length > 0 ? value : key; } return string; } @end ================================================ FILE: Source/Categories/NSIndexSet+DBLibrary.h ================================================ // // NSIndexSet+DBLibrary.h // DBAttachmentPickerController // // Created by Denis Bogatyrev on 15.03.16. // // The MIT License (MIT) // Copyright (c) 2016 Denis Bogatyrev. // // Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), // to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, // and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS // IN THE SOFTWARE. // #import @interface NSIndexSet (DBLibrary) - (NSArray *)indexPathsFromIndexesWithSection:(NSUInteger)section; @end ================================================ FILE: Source/Categories/NSIndexSet+DBLibrary.m ================================================ // // NSIndexSet+DBLibrary.m // DBAttachmentPickerController // // Created by Denis Bogatyrev on 15.03.16. // // The MIT License (MIT) // Copyright (c) 2016 Denis Bogatyrev. // // Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), // to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, // and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS // IN THE SOFTWARE. // #import "NSIndexSet+DBLibrary.h" #import @implementation NSIndexSet (DBLibrary) - (NSArray *)indexPathsFromIndexesWithSection:(NSUInteger)section { NSMutableArray *indexPaths = [NSMutableArray arrayWithCapacity:self.count]; [self enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL *stop) { [indexPaths addObject:[NSIndexPath indexPathForItem:idx inSection:section]]; }]; return indexPaths; } @end ================================================ FILE: Source/Categories/UIImage+DBAssetIcons.h ================================================ // // UIImage+DBAssetIcons.h // DBAttachmentPickerController // // Created by Denis Bogatyrev on 14.03.16. // // The MIT License (MIT) // Copyright (c) 2016 Denis Bogatyrev. // // Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), // to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, // and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS // IN THE SOFTWARE. // @import UIKit; @import Photos; @interface UIImage (DBAssetIcons) + (UIImage*)imageOfSmartAlbumBurstsIcon; + (UIImage*)imageOfSmartAlbumSlomoVideosIcon; + (UIImage*)imageOfSmartAlbumTimelapsesIcon; + (UIImage*)imageOfSmartAlbumVideosIcon; + (UIImage*)imageOfSmartAlbumPanoramasIcon; + (UIImage*)imageOfSmartAlbumSelfPortraitsIcon; + (UIImage*)imageOfSmartAlbumFavoritesIcon; + (UIImage*)imageOfSmartAlbumScreenshotsIcon; + (UIImage*)imageOfVideoTimelapseIcon; + (UIImage*)imageOfVideoHighFrameRateIcon; + (UIImage*)imageOfVideoIcon; + (UIImage*)imageOfSelectorOnIconWithTintColor: (UIColor*)tintColor; + (UIImage*)imageOfSelectorOffIcon; + (UIImage*)imageOfFileIconWithExtensionText: (NSString*)text; + (UIImage *)placeholderImageWithSize:(CGSize)size; @end ================================================ FILE: Source/Categories/UIImage+DBAssetIcons.m ================================================ // // UIImage+DBAssetIcons.m // DBAttachmentPickerController // // Created by Denis Bogatyrev on 14.03.16. // // The MIT License (MIT) // Copyright (c) 2016 Denis Bogatyrev. // // Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), // to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, // and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS // IN THE SOFTWARE. // #import "UIImage+DBAssetIcons.h" @implementation UIImage (DBAssetIcons) #pragma mark Cache static UIImage* _imageOfSmartAlbumBurstsIcon = nil; static UIImage* _imageOfSmartAlbumSlomoVideosIcon = nil; static UIImage* _imageOfSmartAlbumTimelapsesIcon = nil; static UIImage* _imageOfSmartAlbumVideosIcon = nil; static UIImage* _imageOfSmartAlbumPanoramasIcon = nil; static UIImage* _imageOfSmartAlbumSelfPortraitsIcon = nil; static UIImage* _imageOfSmartAlbumFavoritesIcon = nil; static UIImage* _imageOfSmartAlbumScreenshotsIcon = nil; static UIImage* _imageOfVideoTimelapseIcon = nil; static UIImage* _imageOfVideoHighFrameRateIcon = nil; static UIImage* _imageOfVideoIcon = nil; static UIImage* _imageOfSelectorOffIcon = nil; #pragma mark Drawing Methods + (void)drawSmartAlbumBurstsIcon { //// Color Declarations UIColor* color = [UIColor colorWithRed: 1 green: 1 blue: 1 alpha: 1]; //// Group { //// Rectangle Drawing UIBezierPath* rectanglePath = [UIBezierPath bezierPathWithRoundedRect: CGRectMake(0, 11, 48, 35) cornerRadius: 1]; [color setFill]; [rectanglePath fill]; //// Rectangle 2 Drawing UIBezierPath* rectangle2Path = [UIBezierPath bezierPathWithRoundedRect: CGRectMake(4, 7, 41, 3) byRoundingCorners: UIRectCornerTopLeft | UIRectCornerTopRight cornerRadii: CGSizeMake(1, 1)]; [rectangle2Path closePath]; [color setFill]; [rectangle2Path fill]; //// Rectangle 3 Drawing UIBezierPath* rectangle3Path = [UIBezierPath bezierPathWithRoundedRect: CGRectMake(7, 4, 35, 2) byRoundingCorners: UIRectCornerTopLeft | UIRectCornerTopRight cornerRadii: CGSizeMake(1, 1)]; [rectangle3Path closePath]; [color setFill]; [rectangle3Path fill]; } } + (void)drawSmartAlbumSlomoVideosIcon { //// General Declarations CGContextRef context = UIGraphicsGetCurrentContext(); //// Color Declarations UIColor* color = [UIColor colorWithRed: 1 green: 1 blue: 1 alpha: 1]; //// short15 Drawing CGContextSaveGState(context); CGContextTranslateCTM(context, 24, 24); CGContextRotateCTM(context, 15 * M_PI / 180); UIBezierPath* short15Path = [UIBezierPath bezierPathWithRoundedRect: CGRectMake(-1, -22, 2, 2) cornerRadius: 1]; [color setFill]; [short15Path fill]; CGContextRestoreGState(context); //// short45 Drawing CGContextSaveGState(context); CGContextTranslateCTM(context, 24, 24); CGContextRotateCTM(context, 45 * M_PI / 180); UIBezierPath* short45Path = [UIBezierPath bezierPathWithRoundedRect: CGRectMake(-1, -22, 2, 2) cornerRadius: 1]; [color setFill]; [short45Path fill]; CGContextRestoreGState(context); //// short75 Drawing CGContextSaveGState(context); CGContextTranslateCTM(context, 24, 24); CGContextRotateCTM(context, 75 * M_PI / 180); UIBezierPath* short75Path = [UIBezierPath bezierPathWithRoundedRect: CGRectMake(-1, -22, 2, 2) cornerRadius: 1]; [color setFill]; [short75Path fill]; CGContextRestoreGState(context); //// short105 Drawing CGContextSaveGState(context); CGContextTranslateCTM(context, 24, 24); CGContextRotateCTM(context, 105 * M_PI / 180); UIBezierPath* short105Path = [UIBezierPath bezierPathWithRoundedRect: CGRectMake(-1, -22, 2, 2) cornerRadius: 1]; [color setFill]; [short105Path fill]; CGContextRestoreGState(context); //// short135 Drawing CGContextSaveGState(context); CGContextTranslateCTM(context, 24, 24); CGContextRotateCTM(context, 135 * M_PI / 180); UIBezierPath* short135Path = [UIBezierPath bezierPathWithRoundedRect: CGRectMake(-1, -22, 2, 2) cornerRadius: 1]; [color setFill]; [short135Path fill]; CGContextRestoreGState(context); //// short165 Drawing CGContextSaveGState(context); CGContextTranslateCTM(context, 24, 24); CGContextRotateCTM(context, 165 * M_PI / 180); UIBezierPath* short165Path = [UIBezierPath bezierPathWithRoundedRect: CGRectMake(-1, -22, 2, 2) cornerRadius: 1]; [color setFill]; [short165Path fill]; CGContextRestoreGState(context); //// short195 Drawing CGContextSaveGState(context); CGContextTranslateCTM(context, 24, 24); CGContextRotateCTM(context, -165 * M_PI / 180); UIBezierPath* short195Path = [UIBezierPath bezierPathWithRoundedRect: CGRectMake(-1, -22, 2, 2) cornerRadius: 1]; [color setFill]; [short195Path fill]; CGContextRestoreGState(context); //// short225 Drawing CGContextSaveGState(context); CGContextTranslateCTM(context, 24, 24); CGContextRotateCTM(context, -135 * M_PI / 180); UIBezierPath* short225Path = [UIBezierPath bezierPathWithRoundedRect: CGRectMake(-1, -22, 2, 2) cornerRadius: 1]; [color setFill]; [short225Path fill]; CGContextRestoreGState(context); //// short255 Drawing CGContextSaveGState(context); CGContextTranslateCTM(context, 24, 24); CGContextRotateCTM(context, -105 * M_PI / 180); UIBezierPath* short255Path = [UIBezierPath bezierPathWithRoundedRect: CGRectMake(-1, -22, 2, 2) cornerRadius: 1]; [color setFill]; [short255Path fill]; CGContextRestoreGState(context); //// short285 Drawing CGContextSaveGState(context); CGContextTranslateCTM(context, 24, 24); CGContextRotateCTM(context, -75 * M_PI / 180); UIBezierPath* short285Path = [UIBezierPath bezierPathWithRoundedRect: CGRectMake(-1, -22, 2, 2) cornerRadius: 1]; [color setFill]; [short285Path fill]; CGContextRestoreGState(context); //// short315 Drawing CGContextSaveGState(context); CGContextTranslateCTM(context, 24, 24); CGContextRotateCTM(context, -45 * M_PI / 180); UIBezierPath* short315Path = [UIBezierPath bezierPathWithRoundedRect: CGRectMake(-1, -22, 2, 2) cornerRadius: 1]; [color setFill]; [short315Path fill]; CGContextRestoreGState(context); //// short345 Drawing CGContextSaveGState(context); CGContextTranslateCTM(context, 24, 24); CGContextRotateCTM(context, -15 * M_PI / 180); UIBezierPath* short345Path = [UIBezierPath bezierPathWithRoundedRect: CGRectMake(-1, -22, 2, 2) cornerRadius: 1]; [color setFill]; [short345Path fill]; CGContextRestoreGState(context); //// long0 Drawing CGContextSaveGState(context); CGContextTranslateCTM(context, 24, 24); UIBezierPath* long0Path = [UIBezierPath bezierPathWithRoundedRect: CGRectMake(-1.5, -22, 3, 8) cornerRadius: 1]; [color setFill]; [long0Path fill]; CGContextRestoreGState(context); //// long30 Drawing CGContextSaveGState(context); CGContextTranslateCTM(context, 24, 24); CGContextRotateCTM(context, 30 * M_PI / 180); UIBezierPath* long30Path = [UIBezierPath bezierPathWithRoundedRect: CGRectMake(-1.5, -22, 3, 8) cornerRadius: 1]; [color setFill]; [long30Path fill]; CGContextRestoreGState(context); //// long60 Drawing CGContextSaveGState(context); CGContextTranslateCTM(context, 24, 24); CGContextRotateCTM(context, 60 * M_PI / 180); UIBezierPath* long60Path = [UIBezierPath bezierPathWithRoundedRect: CGRectMake(-1.5, -22, 3, 8) cornerRadius: 1]; [color setFill]; [long60Path fill]; CGContextRestoreGState(context); //// long90 Drawing CGContextSaveGState(context); CGContextTranslateCTM(context, 24, 24); CGContextRotateCTM(context, 90 * M_PI / 180); UIBezierPath* long90Path = [UIBezierPath bezierPathWithRoundedRect: CGRectMake(-1.5, -22, 3, 8) cornerRadius: 1]; [color setFill]; [long90Path fill]; CGContextRestoreGState(context); //// long120 Drawing CGContextSaveGState(context); CGContextTranslateCTM(context, 24, 24); CGContextRotateCTM(context, 120 * M_PI / 180); UIBezierPath* long120Path = [UIBezierPath bezierPathWithRoundedRect: CGRectMake(-1.5, -22, 3, 8) cornerRadius: 1]; [color setFill]; [long120Path fill]; CGContextRestoreGState(context); //// long150 Drawing CGContextSaveGState(context); CGContextTranslateCTM(context, 24, 24); CGContextRotateCTM(context, 150 * M_PI / 180); UIBezierPath* long150Path = [UIBezierPath bezierPathWithRoundedRect: CGRectMake(-1.5, -22, 3, 8) cornerRadius: 1]; [color setFill]; [long150Path fill]; CGContextRestoreGState(context); //// long180 Drawing CGContextSaveGState(context); CGContextTranslateCTM(context, 24, 24); CGContextRotateCTM(context, -180 * M_PI / 180); UIBezierPath* long180Path = [UIBezierPath bezierPathWithRoundedRect: CGRectMake(-1.5, -22, 3, 8) cornerRadius: 1]; [color setFill]; [long180Path fill]; CGContextRestoreGState(context); //// long210 Drawing CGContextSaveGState(context); CGContextTranslateCTM(context, 24, 24); CGContextRotateCTM(context, -150 * M_PI / 180); UIBezierPath* long210Path = [UIBezierPath bezierPathWithRoundedRect: CGRectMake(-1.5, -22, 3, 8) cornerRadius: 1]; [color setFill]; [long210Path fill]; CGContextRestoreGState(context); //// long240 Drawing CGContextSaveGState(context); CGContextTranslateCTM(context, 24, 24); CGContextRotateCTM(context, -120 * M_PI / 180); UIBezierPath* long240Path = [UIBezierPath bezierPathWithRoundedRect: CGRectMake(-1.5, -22, 3, 8) cornerRadius: 1]; [color setFill]; [long240Path fill]; CGContextRestoreGState(context); //// long270 Drawing CGContextSaveGState(context); CGContextTranslateCTM(context, 24, 24); CGContextRotateCTM(context, -90 * M_PI / 180); UIBezierPath* long270Path = [UIBezierPath bezierPathWithRoundedRect: CGRectMake(-1.5, -22, 3, 8) cornerRadius: 1]; [color setFill]; [long270Path fill]; CGContextRestoreGState(context); //// long300 Drawing CGContextSaveGState(context); CGContextTranslateCTM(context, 24, 24); CGContextRotateCTM(context, -60 * M_PI / 180); UIBezierPath* long300Path = [UIBezierPath bezierPathWithRoundedRect: CGRectMake(-1.5, -22, 3, 8) cornerRadius: 1]; [color setFill]; [long300Path fill]; CGContextRestoreGState(context); //// long330 Drawing CGContextSaveGState(context); CGContextTranslateCTM(context, 24, 24); CGContextRotateCTM(context, -30 * M_PI / 180); UIBezierPath* long330Path = [UIBezierPath bezierPathWithRoundedRect: CGRectMake(-1.5, -22, 3, 8) cornerRadius: 1]; [color setFill]; [long330Path fill]; CGContextRestoreGState(context); } + (void)drawSmartAlbumTimelapsesIcon { //// General Declarations CGContextRef context = UIGraphicsGetCurrentContext(); //// Color Declarations UIColor* color = [UIColor colorWithRed: 1 green: 1 blue: 1 alpha: 1]; //// Group { //// Rectangle Drawing CGContextSaveGState(context); CGContextTranslateCTM(context, 24, 24); UIBezierPath* rectanglePath = [UIBezierPath bezierPathWithRoundedRect: CGRectMake(-1.5, -22.5, 3, 6) cornerRadius: 1]; [color setFill]; [rectanglePath fill]; CGContextRestoreGState(context); //// Rectangle 2 Drawing CGContextSaveGState(context); CGContextTranslateCTM(context, 24, 24); CGContextRotateCTM(context, 30 * M_PI / 180); UIBezierPath* rectangle2Path = [UIBezierPath bezierPathWithRoundedRect: CGRectMake(-1.5, -22.5, 3, 6) cornerRadius: 1]; [color setFill]; [rectangle2Path fill]; CGContextRestoreGState(context); //// Rectangle 3 Drawing CGContextSaveGState(context); CGContextTranslateCTM(context, 24, 24); CGContextRotateCTM(context, 60 * M_PI / 180); UIBezierPath* rectangle3Path = [UIBezierPath bezierPathWithRoundedRect: CGRectMake(-1.5, -22.5, 3, 6) cornerRadius: 1]; [color setFill]; [rectangle3Path fill]; CGContextRestoreGState(context); //// Rectangle 4 Drawing CGContextSaveGState(context); CGContextTranslateCTM(context, 24, 24); CGContextRotateCTM(context, 90 * M_PI / 180); UIBezierPath* rectangle4Path = [UIBezierPath bezierPathWithRoundedRect: CGRectMake(-1.5, -22.5, 3, 6) cornerRadius: 1]; [color setFill]; [rectangle4Path fill]; CGContextRestoreGState(context); //// Rectangle 5 Drawing CGContextSaveGState(context); CGContextTranslateCTM(context, 24, 24); CGContextRotateCTM(context, 120 * M_PI / 180); UIBezierPath* rectangle5Path = [UIBezierPath bezierPathWithRoundedRect: CGRectMake(-1.5, -22.5, 3, 6) cornerRadius: 1]; [color setFill]; [rectangle5Path fill]; CGContextRestoreGState(context); //// Rectangle 6 Drawing CGContextSaveGState(context); CGContextTranslateCTM(context, 24, 24); CGContextRotateCTM(context, 150 * M_PI / 180); UIBezierPath* rectangle6Path = [UIBezierPath bezierPathWithRoundedRect: CGRectMake(-1.5, -22.5, 3, 6) cornerRadius: 1]; [color setFill]; [rectangle6Path fill]; CGContextRestoreGState(context); //// Rectangle 7 Drawing CGContextSaveGState(context); CGContextTranslateCTM(context, 24, 24); CGContextRotateCTM(context, -180 * M_PI / 180); UIBezierPath* rectangle7Path = [UIBezierPath bezierPathWithRoundedRect: CGRectMake(-1.5, -22.5, 3, 6) cornerRadius: 1]; [color setFill]; [rectangle7Path fill]; CGContextRestoreGState(context); //// Rectangle 8 Drawing CGContextSaveGState(context); CGContextTranslateCTM(context, 24, 24); CGContextRotateCTM(context, -150 * M_PI / 180); UIBezierPath* rectangle8Path = [UIBezierPath bezierPathWithRoundedRect: CGRectMake(-1.5, -22.5, 3, 6) cornerRadius: 1]; [color setFill]; [rectangle8Path fill]; CGContextRestoreGState(context); //// Rectangle 9 Drawing CGContextSaveGState(context); CGContextTranslateCTM(context, 24, 24); CGContextRotateCTM(context, -120 * M_PI / 180); UIBezierPath* rectangle9Path = [UIBezierPath bezierPathWithRoundedRect: CGRectMake(-1.5, -22.5, 3, 6) cornerRadius: 1]; [color setFill]; [rectangle9Path fill]; CGContextRestoreGState(context); //// Rectangle 10 Drawing CGContextSaveGState(context); CGContextTranslateCTM(context, 24, 24); CGContextRotateCTM(context, -90 * M_PI / 180); UIBezierPath* rectangle10Path = [UIBezierPath bezierPathWithRoundedRect: CGRectMake(-1.5, -22.5, 3, 6) cornerRadius: 1]; [color setFill]; [rectangle10Path fill]; CGContextRestoreGState(context); //// Rectangle 11 Drawing CGContextSaveGState(context); CGContextTranslateCTM(context, 24, 24); CGContextRotateCTM(context, -60 * M_PI / 180); UIBezierPath* rectangle11Path = [UIBezierPath bezierPathWithRoundedRect: CGRectMake(-1.5, -22.5, 3, 6) cornerRadius: 1]; [color setFill]; [rectangle11Path fill]; CGContextRestoreGState(context); //// Rectangle 12 Drawing CGContextSaveGState(context); CGContextTranslateCTM(context, 24, 24); CGContextRotateCTM(context, -30 * M_PI / 180); UIBezierPath* rectangle12Path = [UIBezierPath bezierPathWithRoundedRect: CGRectMake(-1.5, -22.5, 3, 6) cornerRadius: 1]; [color setFill]; [rectangle12Path fill]; CGContextRestoreGState(context); } } + (void)drawSmartAlbumVideosIcon { //// Color Declarations UIColor* color = [UIColor colorWithRed: 1 green: 1 blue: 1 alpha: 1]; //// Group { //// Rectangle Drawing UIBezierPath* rectanglePath = [UIBezierPath bezierPathWithRoundedRect: CGRectMake(0, 11, 34, 27) cornerRadius: 5]; [color setFill]; [rectanglePath fill]; //// Bezier Drawing UIBezierPath* bezierPath = UIBezierPath.bezierPath; [bezierPath moveToPoint: CGPointMake(36, 21.38)]; [bezierPath addLineToPoint: CGPointMake(48, 11)]; [bezierPath addLineToPoint: CGPointMake(48, 38)]; [bezierPath addLineToPoint: CGPointMake(36, 27.62)]; [bezierPath addLineToPoint: CGPointMake(36, 21.38)]; [bezierPath closePath]; [color setFill]; [bezierPath fill]; } } + (void)drawSmartAlbumPanoramasIcon { //// Color Declarations UIColor* color = [UIColor colorWithRed: 1 green: 1 blue: 1 alpha: 1]; //// Rectangle Drawing UIBezierPath* rectanglePath = UIBezierPath.bezierPath; [rectanglePath moveToPoint: CGPointMake(2, 38)]; [rectanglePath addLineToPoint: CGPointMake(7, 35)]; [rectanglePath addLineToPoint: CGPointMake(13, 33)]; [rectanglePath addLineToPoint: CGPointMake(20, 32)]; [rectanglePath addLineToPoint: CGPointMake(28, 32)]; [rectanglePath addLineToPoint: CGPointMake(35, 33)]; [rectanglePath addLineToPoint: CGPointMake(41, 35)]; [rectanglePath addLineToPoint: CGPointMake(46, 38)]; [rectanglePath addLineToPoint: CGPointMake(46, 11)]; [rectanglePath addLineToPoint: CGPointMake(41, 14)]; [rectanglePath addLineToPoint: CGPointMake(35, 16)]; [rectanglePath addLineToPoint: CGPointMake(28, 17)]; [rectanglePath addLineToPoint: CGPointMake(20, 17)]; [rectanglePath addLineToPoint: CGPointMake(13, 16)]; [rectanglePath addLineToPoint: CGPointMake(7, 14)]; [rectanglePath addLineToPoint: CGPointMake(2, 11)]; [rectanglePath addLineToPoint: CGPointMake(2, 38)]; [rectanglePath closePath]; [color setFill]; [rectanglePath fill]; } + (void)drawSmartAlbumSelfPortraitsIcon { //// Color Declarations UIColor* color = [UIColor colorWithRed: 1 green: 1 blue: 1 alpha: 1]; //// Rectangle 2 Drawing UIBezierPath* rectangle2Path = [UIBezierPath bezierPathWithRect: CGRectMake(6, 12, 4, 2)]; [color setFill]; [rectangle2Path fill]; //// Bezier 2 Drawing UIBezierPath* bezier2Path = UIBezierPath.bezierPath; [bezier2Path moveToPoint: CGPointMake(24, 17.5)]; [bezier2Path addCurveToPoint: CGPointMake(14.8, 22) controlPoint1: CGPointMake(20.23, 17.5) controlPoint2: CGPointMake(16.5, 18.87)]; [bezier2Path addCurveToPoint: CGPointMake(13.5, 28) controlPoint1: CGPointMake(14.02, 23.44) controlPoint2: CGPointMake(13.5, 25.79)]; [bezier2Path addCurveToPoint: CGPointMake(23, 37.5) controlPoint1: CGPointMake(13.5, 35) controlPoint2: CGPointMake(19, 37.5)]; [bezier2Path addLineToPoint: CGPointMake(25.5, 37.5)]; [bezier2Path addCurveToPoint: CGPointMake(25.5, 35.5) controlPoint1: CGPointMake(25.5, 37.43) controlPoint2: CGPointMake(25.5, 35.5)]; [bezier2Path addLineToPoint: CGPointMake(23, 35.5)]; [bezier2Path addCurveToPoint: CGPointMake(15.5, 28) controlPoint1: CGPointMake(17.5, 35.5) controlPoint2: CGPointMake(15.5, 31.5)]; [bezier2Path addCurveToPoint: CGPointMake(16.38, 23.58) controlPoint1: CGPointMake(15.5, 26.39) controlPoint2: CGPointMake(15.76, 24.88)]; [bezier2Path addCurveToPoint: CGPointMake(24, 19.5) controlPoint1: CGPointMake(17.69, 20.84) controlPoint2: CGPointMake(19.93, 19.5)]; [bezier2Path addCurveToPoint: CGPointMake(31.5, 27) controlPoint1: CGPointMake(30, 19.5) controlPoint2: CGPointMake(31.5, 23)]; [bezier2Path addCurveToPoint: CGPointMake(31.5, 27.5) controlPoint1: CGPointMake(31.5, 28) controlPoint2: CGPointMake(31.5, 27.5)]; [bezier2Path addLineToPoint: CGPointMake(28, 27.5)]; [bezier2Path addLineToPoint: CGPointMake(32.5, 35)]; [bezier2Path addLineToPoint: CGPointMake(37, 27.5)]; [bezier2Path addLineToPoint: CGPointMake(33.5, 27.5)]; [bezier2Path addCurveToPoint: CGPointMake(33.5, 27) controlPoint1: CGPointMake(33.5, 27.5) controlPoint2: CGPointMake(33.5, 29)]; [bezier2Path addCurveToPoint: CGPointMake(24, 17.5) controlPoint1: CGPointMake(33.5, 21.5) controlPoint2: CGPointMake(29.5, 17.5)]; [bezier2Path closePath]; [bezier2Path moveToPoint: CGPointMake(29, 10)]; [bezier2Path addLineToPoint: CGPointMake(34, 15)]; [bezier2Path addLineToPoint: CGPointMake(38.89, 15)]; [bezier2Path addCurveToPoint: CGPointMake(42.32, 15.26) controlPoint1: CGPointMake(40.65, 15) controlPoint2: CGPointMake(41.53, 15)]; [bezier2Path addLineToPoint: CGPointMake(42.47, 15.3)]; [bezier2Path addCurveToPoint: CGPointMake(44.7, 17.53) controlPoint1: CGPointMake(43.51, 15.68) controlPoint2: CGPointMake(44.32, 16.49)]; [bezier2Path addCurveToPoint: CGPointMake(45, 21.11) controlPoint1: CGPointMake(45, 18.47) controlPoint2: CGPointMake(45, 19.35)]; [bezier2Path addLineToPoint: CGPointMake(45, 36.89)]; [bezier2Path addCurveToPoint: CGPointMake(44.74, 40.32) controlPoint1: CGPointMake(45, 38.65) controlPoint2: CGPointMake(45, 39.53)]; [bezier2Path addLineToPoint: CGPointMake(44.7, 40.47)]; [bezier2Path addCurveToPoint: CGPointMake(42.47, 42.7) controlPoint1: CGPointMake(44.32, 41.51) controlPoint2: CGPointMake(43.51, 42.32)]; [bezier2Path addCurveToPoint: CGPointMake(38.89, 43) controlPoint1: CGPointMake(41.53, 43) controlPoint2: CGPointMake(40.65, 43)]; [bezier2Path addLineToPoint: CGPointMake(8.11, 43)]; [bezier2Path addCurveToPoint: CGPointMake(4.68, 42.74) controlPoint1: CGPointMake(6.35, 43) controlPoint2: CGPointMake(5.47, 43)]; [bezier2Path addLineToPoint: CGPointMake(4.53, 42.7)]; [bezier2Path addCurveToPoint: CGPointMake(2.3, 40.47) controlPoint1: CGPointMake(3.49, 42.32) controlPoint2: CGPointMake(2.68, 41.51)]; [bezier2Path addCurveToPoint: CGPointMake(2, 36.89) controlPoint1: CGPointMake(2, 39.53) controlPoint2: CGPointMake(2, 38.65)]; [bezier2Path addLineToPoint: CGPointMake(2, 21.11)]; [bezier2Path addCurveToPoint: CGPointMake(2.26, 17.68) controlPoint1: CGPointMake(2, 19.35) controlPoint2: CGPointMake(2, 18.47)]; [bezier2Path addLineToPoint: CGPointMake(2.3, 17.53)]; [bezier2Path addCurveToPoint: CGPointMake(4.53, 15.3) controlPoint1: CGPointMake(2.68, 16.49) controlPoint2: CGPointMake(3.49, 15.68)]; [bezier2Path addCurveToPoint: CGPointMake(8.11, 15) controlPoint1: CGPointMake(5.47, 15) controlPoint2: CGPointMake(6.35, 15)]; [bezier2Path addLineToPoint: CGPointMake(13, 15)]; [bezier2Path addLineToPoint: CGPointMake(18, 10)]; [bezier2Path addLineToPoint: CGPointMake(29, 10)]; [bezier2Path closePath]; [color setFill]; [bezier2Path fill]; } + (void)drawSmartAlbumFavoritesIcon { //// Color Declarations UIColor* color = [UIColor colorWithRed: 1 green: 1 blue: 1 alpha: 1]; //// Bezier Drawing UIBezierPath* bezierPath = UIBezierPath.bezierPath; [bezierPath moveToPoint: CGPointMake(24, 45)]; [bezierPath addLineToPoint: CGPointMake(3.5, 24)]; [bezierPath addCurveToPoint: CGPointMake(1.5, 16) controlPoint1: CGPointMake(2, 22) controlPoint2: CGPointMake(1.5, 21)]; [bezierPath addCurveToPoint: CGPointMake(12.5, 5) controlPoint1: CGPointMake(1.5, 11) controlPoint2: CGPointMake(4.5, 5)]; [bezierPath addCurveToPoint: CGPointMake(24, 12) controlPoint1: CGPointMake(20.5, 5) controlPoint2: CGPointMake(24, 12)]; [bezierPath addCurveToPoint: CGPointMake(35.5, 5) controlPoint1: CGPointMake(24, 12) controlPoint2: CGPointMake(26.5, 5)]; [bezierPath addCurveToPoint: CGPointMake(46.5, 16) controlPoint1: CGPointMake(44.5, 5) controlPoint2: CGPointMake(46.5, 10)]; [bezierPath addCurveToPoint: CGPointMake(44.5, 24) controlPoint1: CGPointMake(46.5, 21.5) controlPoint2: CGPointMake(44.5, 24)]; [bezierPath addLineToPoint: CGPointMake(24, 45)]; [bezierPath closePath]; [color setFill]; [bezierPath fill]; } + (void)drawSmartAlbumScreenshotsIcon { //// Color Declarations UIColor* color = [UIColor colorWithRed: 1 green: 1 blue: 1 alpha: 1]; //// Bezier Drawing UIBezierPath* bezierPath = UIBezierPath.bezierPath; [bezierPath moveToPoint: CGPointMake(25.47, 4.5)]; [bezierPath addLineToPoint: CGPointMake(21.75, 4.5)]; [bezierPath addCurveToPoint: CGPointMake(21.31, 4.54) controlPoint1: CGPointMake(21.5, 4.5) controlPoint2: CGPointMake(21.41, 4.51)]; [bezierPath addCurveToPoint: CGPointMake(21, 4.97) controlPoint1: CGPointMake(21.12, 4.6) controlPoint2: CGPointMake(21, 4.78)]; [bezierPath addCurveToPoint: CGPointMake(21.31, 5.46) controlPoint1: CGPointMake(21, 5.22) controlPoint2: CGPointMake(21.12, 5.4)]; [bezierPath addCurveToPoint: CGPointMake(22.53, 5.5) controlPoint1: CGPointMake(21.42, 5.5) controlPoint2: CGPointMake(21.53, 5.5)]; [bezierPath addLineToPoint: CGPointMake(26.25, 5.5)]; [bezierPath addCurveToPoint: CGPointMake(26.69, 5.46) controlPoint1: CGPointMake(26.47, 5.5) controlPoint2: CGPointMake(26.58, 5.5)]; [bezierPath addCurveToPoint: CGPointMake(27, 5.02) controlPoint1: CGPointMake(26.88, 5.4) controlPoint2: CGPointMake(27, 5.22)]; [bezierPath addCurveToPoint: CGPointMake(26.69, 4.54) controlPoint1: CGPointMake(27, 4.78) controlPoint2: CGPointMake(26.88, 4.6)]; [bezierPath addCurveToPoint: CGPointMake(25.47, 4.5) controlPoint1: CGPointMake(26.58, 4.5) controlPoint2: CGPointMake(26.47, 4.5)]; [bezierPath closePath]; [bezierPath moveToPoint: CGPointMake(31.97, 7.5)]; [bezierPath addLineToPoint: CGPointMake(16.03, 7.5)]; [bezierPath addCurveToPoint: CGPointMake(15.13, 7.57) controlPoint1: CGPointMake(15.59, 7.5) controlPoint2: CGPointMake(15.37, 7.5)]; [bezierPath addCurveToPoint: CGPointMake(14.57, 8.13) controlPoint1: CGPointMake(14.87, 7.67) controlPoint2: CGPointMake(14.67, 7.87)]; [bezierPath addCurveToPoint: CGPointMake(14.5, 9.03) controlPoint1: CGPointMake(14.5, 8.37) controlPoint2: CGPointMake(14.5, 8.59)]; [bezierPath addLineToPoint: CGPointMake(14.5, 36.97)]; [bezierPath addCurveToPoint: CGPointMake(14.57, 37.87) controlPoint1: CGPointMake(14.5, 37.41) controlPoint2: CGPointMake(14.5, 37.63)]; [bezierPath addCurveToPoint: CGPointMake(15.13, 38.43) controlPoint1: CGPointMake(14.67, 38.13) controlPoint2: CGPointMake(14.87, 38.33)]; [bezierPath addCurveToPoint: CGPointMake(16.03, 38.5) controlPoint1: CGPointMake(15.37, 38.5) controlPoint2: CGPointMake(15.59, 38.5)]; [bezierPath addLineToPoint: CGPointMake(31.97, 38.5)]; [bezierPath addCurveToPoint: CGPointMake(32.87, 38.43) controlPoint1: CGPointMake(32.41, 38.5) controlPoint2: CGPointMake(32.63, 38.5)]; [bezierPath addCurveToPoint: CGPointMake(33.43, 37.87) controlPoint1: CGPointMake(33.13, 38.33) controlPoint2: CGPointMake(33.33, 38.13)]; [bezierPath addCurveToPoint: CGPointMake(33.5, 36.97) controlPoint1: CGPointMake(33.5, 37.63) controlPoint2: CGPointMake(33.5, 37.41)]; [bezierPath addLineToPoint: CGPointMake(33.5, 9.03)]; [bezierPath addCurveToPoint: CGPointMake(33.43, 8.13) controlPoint1: CGPointMake(33.5, 8.59) controlPoint2: CGPointMake(33.5, 8.37)]; [bezierPath addCurveToPoint: CGPointMake(32.87, 7.57) controlPoint1: CGPointMake(33.33, 7.87) controlPoint2: CGPointMake(33.13, 7.67)]; [bezierPath addCurveToPoint: CGPointMake(31.97, 7.5) controlPoint1: CGPointMake(32.63, 7.5) controlPoint2: CGPointMake(32.41, 7.5)]; [bezierPath closePath]; [bezierPath moveToPoint: CGPointMake(24, 40.5)]; [bezierPath addCurveToPoint: CGPointMake(22, 42.5) controlPoint1: CGPointMake(22.9, 40.5) controlPoint2: CGPointMake(22, 41.4)]; [bezierPath addCurveToPoint: CGPointMake(24, 44.5) controlPoint1: CGPointMake(22, 43.6) controlPoint2: CGPointMake(22.9, 44.5)]; [bezierPath addCurveToPoint: CGPointMake(26, 42.5) controlPoint1: CGPointMake(25.1, 44.5) controlPoint2: CGPointMake(26, 43.6)]; [bezierPath addCurveToPoint: CGPointMake(24, 40.5) controlPoint1: CGPointMake(26, 41.4) controlPoint2: CGPointMake(25.1, 40.5)]; [bezierPath closePath]; [bezierPath moveToPoint: CGPointMake(31.48, 1.89)]; [bezierPath addLineToPoint: CGPointMake(31.71, 1.95)]; [bezierPath addCurveToPoint: CGPointMake(35.05, 5.29) controlPoint1: CGPointMake(33.26, 2.51) controlPoint2: CGPointMake(34.49, 3.74)]; [bezierPath addCurveToPoint: CGPointMake(35.5, 10.67) controlPoint1: CGPointMake(35.5, 6.71) controlPoint2: CGPointMake(35.5, 8.03)]; [bezierPath addLineToPoint: CGPointMake(35.5, 37.33)]; [bezierPath addCurveToPoint: CGPointMake(35.11, 42.48) controlPoint1: CGPointMake(35.5, 39.97) controlPoint2: CGPointMake(35.5, 41.29)]; [bezierPath addLineToPoint: CGPointMake(35.05, 42.71)]; [bezierPath addCurveToPoint: CGPointMake(31.71, 46.05) controlPoint1: CGPointMake(34.49, 44.26) controlPoint2: CGPointMake(33.26, 45.49)]; [bezierPath addCurveToPoint: CGPointMake(26.33, 46.5) controlPoint1: CGPointMake(30.29, 46.5) controlPoint2: CGPointMake(28.97, 46.5)]; [bezierPath addLineToPoint: CGPointMake(21.67, 46.5)]; [bezierPath addCurveToPoint: CGPointMake(16.52, 46.11) controlPoint1: CGPointMake(19.03, 46.5) controlPoint2: CGPointMake(17.71, 46.5)]; [bezierPath addLineToPoint: CGPointMake(16.29, 46.05)]; [bezierPath addCurveToPoint: CGPointMake(12.95, 42.71) controlPoint1: CGPointMake(14.74, 45.49) controlPoint2: CGPointMake(13.51, 44.26)]; [bezierPath addCurveToPoint: CGPointMake(12.5, 37.33) controlPoint1: CGPointMake(12.5, 41.29) controlPoint2: CGPointMake(12.5, 39.97)]; [bezierPath addLineToPoint: CGPointMake(12.5, 10.67)]; [bezierPath addCurveToPoint: CGPointMake(12.76, 6) controlPoint1: CGPointMake(12.5, 8.38) controlPoint2: CGPointMake(12.5, 7.08)]; [bezierPath addCurveToPoint: CGPointMake(12.89, 5.52) controlPoint1: CGPointMake(12.8, 5.84) controlPoint2: CGPointMake(12.84, 5.68)]; [bezierPath addLineToPoint: CGPointMake(12.95, 5.29)]; [bezierPath addCurveToPoint: CGPointMake(14.51, 3.01) controlPoint1: CGPointMake(13.27, 4.4) controlPoint2: CGPointMake(13.81, 3.62)]; [bezierPath addCurveToPoint: CGPointMake(16.29, 1.95) controlPoint1: CGPointMake(15.02, 2.55) controlPoint2: CGPointMake(15.63, 2.19)]; [bezierPath addCurveToPoint: CGPointMake(21.67, 1.5) controlPoint1: CGPointMake(17.71, 1.5) controlPoint2: CGPointMake(19.03, 1.5)]; [bezierPath addLineToPoint: CGPointMake(26.33, 1.5)]; [bezierPath addCurveToPoint: CGPointMake(31.48, 1.89) controlPoint1: CGPointMake(28.97, 1.5) controlPoint2: CGPointMake(30.29, 1.5)]; [bezierPath closePath]; [color setFill]; [bezierPath fill]; } + (void)drawVideoTimelapseIcon { //// General Declarations CGContextRef context = UIGraphicsGetCurrentContext(); //// Color Declarations UIColor* color = [UIColor colorWithRed: 1 green: 1 blue: 1 alpha: 1]; //// Rectangle Drawing CGContextSaveGState(context); CGContextTranslateCTM(context, 24, 24); UIBezierPath* rectanglePath = [UIBezierPath bezierPathWithRoundedRect: CGRectMake(-1, -19, 2, 6) cornerRadius: 1]; [color setFill]; [rectanglePath fill]; CGContextRestoreGState(context); //// Rectangle 2 Drawing CGContextSaveGState(context); CGContextTranslateCTM(context, 24, 24); CGContextRotateCTM(context, -15 * M_PI / 180); UIBezierPath* rectangle2Path = [UIBezierPath bezierPathWithRoundedRect: CGRectMake(-1, -19, 2, 6) cornerRadius: 1]; [color setFill]; [rectangle2Path fill]; CGContextRestoreGState(context); //// Rectangle 3 Drawing CGContextSaveGState(context); CGContextTranslateCTM(context, 24, 24); CGContextRotateCTM(context, -30 * M_PI / 180); UIBezierPath* rectangle3Path = [UIBezierPath bezierPathWithRoundedRect: CGRectMake(-1, -19, 2, 6) cornerRadius: 1]; [color setFill]; [rectangle3Path fill]; CGContextRestoreGState(context); //// Rectangle 4 Drawing CGContextSaveGState(context); CGContextTranslateCTM(context, 24, 24); CGContextRotateCTM(context, -45 * M_PI / 180); UIBezierPath* rectangle4Path = [UIBezierPath bezierPathWithRoundedRect: CGRectMake(-1, -19, 2, 6) cornerRadius: 1]; [color setFill]; [rectangle4Path fill]; CGContextRestoreGState(context); //// Rectangle 5 Drawing CGContextSaveGState(context); CGContextTranslateCTM(context, 24, 24); CGContextRotateCTM(context, -60 * M_PI / 180); UIBezierPath* rectangle5Path = [UIBezierPath bezierPathWithRoundedRect: CGRectMake(-1, -19, 2, 6) cornerRadius: 1]; [color setFill]; [rectangle5Path fill]; CGContextRestoreGState(context); //// Rectangle 6 Drawing CGContextSaveGState(context); CGContextTranslateCTM(context, 24, 24); CGContextRotateCTM(context, -75 * M_PI / 180); UIBezierPath* rectangle6Path = [UIBezierPath bezierPathWithRoundedRect: CGRectMake(-1, -19, 2, 6) cornerRadius: 1]; [color setFill]; [rectangle6Path fill]; CGContextRestoreGState(context); //// Rectangle 7 Drawing CGContextSaveGState(context); CGContextTranslateCTM(context, 24, 24); CGContextRotateCTM(context, -90 * M_PI / 180); UIBezierPath* rectangle7Path = [UIBezierPath bezierPathWithRoundedRect: CGRectMake(-1, -19, 2, 6) cornerRadius: 1]; [color setFill]; [rectangle7Path fill]; CGContextRestoreGState(context); //// Rectangle 8 Drawing CGContextSaveGState(context); CGContextTranslateCTM(context, 24, 24); CGContextRotateCTM(context, -105 * M_PI / 180); UIBezierPath* rectangle8Path = [UIBezierPath bezierPathWithRoundedRect: CGRectMake(-1, -19, 2, 6) cornerRadius: 1]; [color setFill]; [rectangle8Path fill]; CGContextRestoreGState(context); //// Rectangle 9 Drawing CGContextSaveGState(context); CGContextTranslateCTM(context, 24, 24); CGContextRotateCTM(context, -120 * M_PI / 180); UIBezierPath* rectangle9Path = [UIBezierPath bezierPathWithRoundedRect: CGRectMake(-1, -19, 2, 6) cornerRadius: 1]; [color setFill]; [rectangle9Path fill]; CGContextRestoreGState(context); //// Rectangle 10 Drawing CGContextSaveGState(context); CGContextTranslateCTM(context, 24, 24); CGContextRotateCTM(context, -135 * M_PI / 180); UIBezierPath* rectangle10Path = [UIBezierPath bezierPathWithRoundedRect: CGRectMake(-1, -19, 2, 6) cornerRadius: 1]; [color setFill]; [rectangle10Path fill]; CGContextRestoreGState(context); //// Rectangle 11 Drawing CGContextSaveGState(context); CGContextTranslateCTM(context, 24, 24); CGContextRotateCTM(context, -150 * M_PI / 180); UIBezierPath* rectangle11Path = [UIBezierPath bezierPathWithRoundedRect: CGRectMake(-1, -19, 2, 6) cornerRadius: 1]; [color setFill]; [rectangle11Path fill]; CGContextRestoreGState(context); //// Rectangle 12 Drawing CGContextSaveGState(context); CGContextTranslateCTM(context, 24, 24); CGContextRotateCTM(context, -165 * M_PI / 180); UIBezierPath* rectangle12Path = [UIBezierPath bezierPathWithRoundedRect: CGRectMake(-1, -19, 2, 6) cornerRadius: 1]; [color setFill]; [rectangle12Path fill]; CGContextRestoreGState(context); //// Rectangle 13 Drawing CGContextSaveGState(context); CGContextTranslateCTM(context, 24, 24); CGContextRotateCTM(context, -180 * M_PI / 180); UIBezierPath* rectangle13Path = [UIBezierPath bezierPathWithRoundedRect: CGRectMake(-1, -19, 2, 6) cornerRadius: 1]; [color setFill]; [rectangle13Path fill]; CGContextRestoreGState(context); //// Rectangle 14 Drawing CGContextSaveGState(context); CGContextTranslateCTM(context, 24, 24); CGContextRotateCTM(context, -195 * M_PI / 180); UIBezierPath* rectangle14Path = [UIBezierPath bezierPathWithRoundedRect: CGRectMake(-1, -19, 2, 6) cornerRadius: 1]; [color setFill]; [rectangle14Path fill]; CGContextRestoreGState(context); //// Rectangle 15 Drawing CGContextSaveGState(context); CGContextTranslateCTM(context, 24, 24); CGContextRotateCTM(context, -210 * M_PI / 180); UIBezierPath* rectangle15Path = [UIBezierPath bezierPathWithRoundedRect: CGRectMake(-1, -19, 2, 6) cornerRadius: 1]; [color setFill]; [rectangle15Path fill]; CGContextRestoreGState(context); //// Rectangle 16 Drawing CGContextSaveGState(context); CGContextTranslateCTM(context, 24, 24); CGContextRotateCTM(context, -225 * M_PI / 180); UIBezierPath* rectangle16Path = [UIBezierPath bezierPathWithRoundedRect: CGRectMake(-1, -19, 2, 6) cornerRadius: 1]; [color setFill]; [rectangle16Path fill]; CGContextRestoreGState(context); //// Rectangle 17 Drawing CGContextSaveGState(context); CGContextTranslateCTM(context, 24, 24); CGContextRotateCTM(context, -240 * M_PI / 180); UIBezierPath* rectangle17Path = [UIBezierPath bezierPathWithRoundedRect: CGRectMake(-1, -19, 2, 6) cornerRadius: 1]; [color setFill]; [rectangle17Path fill]; CGContextRestoreGState(context); //// Rectangle 18 Drawing CGContextSaveGState(context); CGContextTranslateCTM(context, 24, 24); CGContextRotateCTM(context, -255 * M_PI / 180); UIBezierPath* rectangle18Path = [UIBezierPath bezierPathWithRoundedRect: CGRectMake(-1, -19, 2, 6) cornerRadius: 1]; [color setFill]; [rectangle18Path fill]; CGContextRestoreGState(context); //// Rectangle 19 Drawing CGContextSaveGState(context); CGContextTranslateCTM(context, 24, 24); CGContextRotateCTM(context, -270 * M_PI / 180); UIBezierPath* rectangle19Path = [UIBezierPath bezierPathWithRoundedRect: CGRectMake(-1, -19, 2, 6) cornerRadius: 1]; [color setFill]; [rectangle19Path fill]; CGContextRestoreGState(context); //// Rectangle 20 Drawing CGContextSaveGState(context); CGContextTranslateCTM(context, 24, 24); CGContextRotateCTM(context, -285 * M_PI / 180); UIBezierPath* rectangle20Path = [UIBezierPath bezierPathWithRoundedRect: CGRectMake(-1, -19, 2, 6) cornerRadius: 1]; [color setFill]; [rectangle20Path fill]; CGContextRestoreGState(context); //// Rectangle 21 Drawing CGContextSaveGState(context); CGContextTranslateCTM(context, 24, 24); CGContextRotateCTM(context, -300 * M_PI / 180); UIBezierPath* rectangle21Path = [UIBezierPath bezierPathWithRoundedRect: CGRectMake(-1, -19, 2, 6) cornerRadius: 1]; [color setFill]; [rectangle21Path fill]; CGContextRestoreGState(context); //// Rectangle 22 Drawing CGContextSaveGState(context); CGContextTranslateCTM(context, 24, 24); CGContextRotateCTM(context, -315 * M_PI / 180); UIBezierPath* rectangle22Path = [UIBezierPath bezierPathWithRoundedRect: CGRectMake(-1, -19, 2, 6) cornerRadius: 1]; [color setFill]; [rectangle22Path fill]; CGContextRestoreGState(context); //// Rectangle 23 Drawing CGContextSaveGState(context); CGContextTranslateCTM(context, 24, 24); CGContextRotateCTM(context, -330 * M_PI / 180); UIBezierPath* rectangle23Path = [UIBezierPath bezierPathWithRoundedRect: CGRectMake(-1, -19, 2, 6) cornerRadius: 1]; [color setFill]; [rectangle23Path fill]; CGContextRestoreGState(context); //// Rectangle 24 Drawing CGContextSaveGState(context); CGContextTranslateCTM(context, 24, 24); CGContextRotateCTM(context, -345 * M_PI / 180); UIBezierPath* rectangle24Path = [UIBezierPath bezierPathWithRoundedRect: CGRectMake(-1, -19, 2, 6) cornerRadius: 1]; [color setFill]; [rectangle24Path fill]; CGContextRestoreGState(context); } + (void)drawVideoHighFrameRateIcon { //// General Declarations CGContextRef context = UIGraphicsGetCurrentContext(); //// Color Declarations UIColor* color = [UIColor colorWithRed: 1 green: 1 blue: 1 alpha: 1]; //// Rectangle Drawing CGContextSaveGState(context); CGContextTranslateCTM(context, 24, 24); UIBezierPath* rectanglePath = [UIBezierPath bezierPathWithRoundedRect: CGRectMake(-1.5, -19.5, 3, 8) cornerRadius: 1]; [color setFill]; [rectanglePath fill]; CGContextRestoreGState(context); //// Rectangle 2 Drawing CGContextSaveGState(context); CGContextTranslateCTM(context, 24, 24); CGContextRotateCTM(context, -90 * M_PI / 180); UIBezierPath* rectangle2Path = [UIBezierPath bezierPathWithRoundedRect: CGRectMake(-1.5, -19.5, 3, 8) cornerRadius: 1]; [color setFill]; [rectangle2Path fill]; CGContextRestoreGState(context); //// Rectangle 3 Drawing CGContextSaveGState(context); CGContextTranslateCTM(context, 24, 24); CGContextRotateCTM(context, -180 * M_PI / 180); UIBezierPath* rectangle3Path = [UIBezierPath bezierPathWithRoundedRect: CGRectMake(-1.5, -19.5, 3, 8) cornerRadius: 1]; [color setFill]; [rectangle3Path fill]; CGContextRestoreGState(context); //// Rectangle 4 Drawing CGContextSaveGState(context); CGContextTranslateCTM(context, 24, 24); CGContextRotateCTM(context, -270 * M_PI / 180); UIBezierPath* rectangle4Path = [UIBezierPath bezierPathWithRoundedRect: CGRectMake(-1.5, -19.5, 3, 8) cornerRadius: 1]; [color setFill]; [rectangle4Path fill]; CGContextRestoreGState(context); //// Rectangle 5 Drawing CGContextSaveGState(context); CGContextTranslateCTM(context, 24, 24); CGContextRotateCTM(context, -45 * M_PI / 180); UIBezierPath* rectangle5Path = [UIBezierPath bezierPathWithRoundedRect: CGRectMake(-1.5, -19.5, 3, 11) cornerRadius: 1]; [color setFill]; [rectangle5Path fill]; CGContextRestoreGState(context); //// Rectangle 6 Drawing CGContextSaveGState(context); CGContextTranslateCTM(context, 24, 24); CGContextRotateCTM(context, -135 * M_PI / 180); UIBezierPath* rectangle6Path = [UIBezierPath bezierPathWithRoundedRect: CGRectMake(-1.5, -19.5, 3, 11) cornerRadius: 1]; [color setFill]; [rectangle6Path fill]; CGContextRestoreGState(context); //// Rectangle 7 Drawing CGContextSaveGState(context); CGContextTranslateCTM(context, 24, 24); CGContextRotateCTM(context, -225 * M_PI / 180); UIBezierPath* rectangle7Path = [UIBezierPath bezierPathWithRoundedRect: CGRectMake(-1.5, -19.5, 3, 11) cornerRadius: 1]; [color setFill]; [rectangle7Path fill]; CGContextRestoreGState(context); //// Rectangle 8 Drawing CGContextSaveGState(context); CGContextTranslateCTM(context, 24, 24); CGContextRotateCTM(context, 45 * M_PI / 180); UIBezierPath* rectangle8Path = [UIBezierPath bezierPathWithRoundedRect: CGRectMake(-1.5, -19.5, 3, 11) cornerRadius: 1]; [color setFill]; [rectangle8Path fill]; CGContextRestoreGState(context); //// Rectangle 9 Drawing CGContextSaveGState(context); CGContextTranslateCTM(context, 24, 24); CGContextRotateCTM(context, -22.5 * M_PI / 180); UIBezierPath* rectangle9Path = [UIBezierPath bezierPathWithRoundedRect: CGRectMake(-1.5, -19.5, 3, 5) cornerRadius: 1]; [color setFill]; [rectangle9Path fill]; CGContextRestoreGState(context); //// Rectangle 10 Drawing CGContextSaveGState(context); CGContextTranslateCTM(context, 24, 24); CGContextRotateCTM(context, -112.5 * M_PI / 180); UIBezierPath* rectangle10Path = [UIBezierPath bezierPathWithRoundedRect: CGRectMake(-1.5, -19.5, 3, 5) cornerRadius: 1]; [color setFill]; [rectangle10Path fill]; CGContextRestoreGState(context); //// Rectangle 11 Drawing CGContextSaveGState(context); CGContextTranslateCTM(context, 24, 24); CGContextRotateCTM(context, -202.5 * M_PI / 180); UIBezierPath* rectangle11Path = [UIBezierPath bezierPathWithRoundedRect: CGRectMake(-1.5, -19.5, 3, 5) cornerRadius: 1]; [color setFill]; [rectangle11Path fill]; CGContextRestoreGState(context); //// Rectangle 12 Drawing CGContextSaveGState(context); CGContextTranslateCTM(context, 24, 24); CGContextRotateCTM(context, 67.5 * M_PI / 180); UIBezierPath* rectangle12Path = [UIBezierPath bezierPathWithRoundedRect: CGRectMake(-1.5, -19.5, 3, 5) cornerRadius: 1]; [color setFill]; [rectangle12Path fill]; CGContextRestoreGState(context); //// Rectangle 13 Drawing CGContextSaveGState(context); CGContextTranslateCTM(context, 24, 24); CGContextRotateCTM(context, -67.5 * M_PI / 180); UIBezierPath* rectangle13Path = [UIBezierPath bezierPathWithRoundedRect: CGRectMake(-1.5, -19.5, 3, 5) cornerRadius: 1]; [color setFill]; [rectangle13Path fill]; CGContextRestoreGState(context); //// Rectangle 14 Drawing CGContextSaveGState(context); CGContextTranslateCTM(context, 24, 24); CGContextRotateCTM(context, -157.5 * M_PI / 180); UIBezierPath* rectangle14Path = [UIBezierPath bezierPathWithRoundedRect: CGRectMake(-1.5, -19.5, 3, 5) cornerRadius: 1]; [color setFill]; [rectangle14Path fill]; CGContextRestoreGState(context); //// Rectangle 15 Drawing CGContextSaveGState(context); CGContextTranslateCTM(context, 24, 24); CGContextRotateCTM(context, 112.5 * M_PI / 180); UIBezierPath* rectangle15Path = [UIBezierPath bezierPathWithRoundedRect: CGRectMake(-1.5, -19.5, 3, 5) cornerRadius: 1]; [color setFill]; [rectangle15Path fill]; CGContextRestoreGState(context); //// Rectangle 16 Drawing CGContextSaveGState(context); CGContextTranslateCTM(context, 24, 24); CGContextRotateCTM(context, 22.5 * M_PI / 180); UIBezierPath* rectangle16Path = [UIBezierPath bezierPathWithRoundedRect: CGRectMake(-1.5, -19.5, 3, 5) cornerRadius: 1]; [color setFill]; [rectangle16Path fill]; CGContextRestoreGState(context); } + (void)drawVideoIcon { //// Color Declarations UIColor* color = [UIColor colorWithRed: 1 green: 1 blue: 1 alpha: 1]; //// Rectangle Drawing UIBezierPath* rectanglePath = [UIBezierPath bezierPathWithRoundedRect: CGRectMake(3, 11.5, 27, 24) cornerRadius: 5]; [color setFill]; [rectanglePath fill]; //// Bezier Drawing UIBezierPath* bezierPath = UIBezierPath.bezierPath; [bezierPath moveToPoint: CGPointMake(33, 22.5)]; [bezierPath addLineToPoint: CGPointMake(45, 12)]; [bezierPath addLineToPoint: CGPointMake(45, 35)]; [bezierPath addLineToPoint: CGPointMake(33, 24.5)]; [bezierPath addLineToPoint: CGPointMake(33, 22.5)]; [bezierPath closePath]; [color setFill]; [bezierPath fill]; } #pragma mark Generated Images + (UIImage*)imageOfSmartAlbumBurstsIcon { if (_imageOfSmartAlbumBurstsIcon) return _imageOfSmartAlbumBurstsIcon; UIGraphicsBeginImageContextWithOptions(CGSizeMake(48, 48), NO, 0.0f); [self drawSmartAlbumBurstsIcon]; _imageOfSmartAlbumBurstsIcon = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); return _imageOfSmartAlbumBurstsIcon; } + (UIImage*)imageOfSmartAlbumSlomoVideosIcon { if (_imageOfSmartAlbumSlomoVideosIcon) return _imageOfSmartAlbumSlomoVideosIcon; UIGraphicsBeginImageContextWithOptions(CGSizeMake(48, 48), NO, 0.0f); [self drawSmartAlbumSlomoVideosIcon]; _imageOfSmartAlbumSlomoVideosIcon = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); return _imageOfSmartAlbumSlomoVideosIcon; } + (UIImage*)imageOfSmartAlbumTimelapsesIcon { if (_imageOfSmartAlbumTimelapsesIcon) return _imageOfSmartAlbumTimelapsesIcon; UIGraphicsBeginImageContextWithOptions(CGSizeMake(48, 48), NO, 0.0f); [self drawSmartAlbumTimelapsesIcon]; _imageOfSmartAlbumTimelapsesIcon = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); return _imageOfSmartAlbumTimelapsesIcon; } + (UIImage*)imageOfSmartAlbumVideosIcon { if (_imageOfSmartAlbumVideosIcon) return _imageOfSmartAlbumVideosIcon; UIGraphicsBeginImageContextWithOptions(CGSizeMake(48, 48), NO, 0.0f); [self drawSmartAlbumVideosIcon]; _imageOfSmartAlbumVideosIcon = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); return _imageOfSmartAlbumVideosIcon; } + (UIImage*)imageOfSmartAlbumPanoramasIcon { if (_imageOfSmartAlbumPanoramasIcon) return _imageOfSmartAlbumPanoramasIcon; UIGraphicsBeginImageContextWithOptions(CGSizeMake(48, 48), NO, 0.0f); [self drawSmartAlbumPanoramasIcon]; _imageOfSmartAlbumPanoramasIcon = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); return _imageOfSmartAlbumPanoramasIcon; } + (UIImage*)imageOfSmartAlbumSelfPortraitsIcon { if (_imageOfSmartAlbumSelfPortraitsIcon) return _imageOfSmartAlbumSelfPortraitsIcon; UIGraphicsBeginImageContextWithOptions(CGSizeMake(48, 48), NO, 0.0f); [self drawSmartAlbumSelfPortraitsIcon]; _imageOfSmartAlbumSelfPortraitsIcon = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); return _imageOfSmartAlbumSelfPortraitsIcon; } + (UIImage*)imageOfSmartAlbumFavoritesIcon { if (_imageOfSmartAlbumFavoritesIcon) return _imageOfSmartAlbumFavoritesIcon; UIGraphicsBeginImageContextWithOptions(CGSizeMake(48, 48), NO, 0.0f); [self drawSmartAlbumFavoritesIcon]; _imageOfSmartAlbumFavoritesIcon = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); return _imageOfSmartAlbumFavoritesIcon; } + (UIImage*)imageOfSmartAlbumScreenshotsIcon { if (_imageOfSmartAlbumScreenshotsIcon) return _imageOfSmartAlbumScreenshotsIcon; UIGraphicsBeginImageContextWithOptions(CGSizeMake(48, 48), NO, 0.0f); [self drawSmartAlbumScreenshotsIcon]; _imageOfSmartAlbumScreenshotsIcon = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); return _imageOfSmartAlbumScreenshotsIcon; } + (UIImage*)imageOfVideoTimelapseIcon { if (_imageOfVideoTimelapseIcon) return _imageOfVideoTimelapseIcon; UIGraphicsBeginImageContextWithOptions(CGSizeMake(48, 48), NO, 0.0f); [self drawVideoTimelapseIcon]; _imageOfVideoTimelapseIcon = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); return _imageOfVideoTimelapseIcon; } + (UIImage*)imageOfVideoHighFrameRateIcon { if (_imageOfVideoHighFrameRateIcon) return _imageOfVideoHighFrameRateIcon; UIGraphicsBeginImageContextWithOptions(CGSizeMake(48, 48), NO, 0.0f); [self drawVideoHighFrameRateIcon]; _imageOfVideoHighFrameRateIcon = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); return _imageOfVideoHighFrameRateIcon; } + (UIImage*)imageOfVideoIcon { if (_imageOfVideoIcon) return _imageOfVideoIcon; UIGraphicsBeginImageContextWithOptions(CGSizeMake(48, 48), NO, 0.0f); [self drawVideoIcon]; _imageOfVideoIcon = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); return _imageOfVideoIcon; } #pragma mark - Selector + (void)drawSelectorOnIconWithTintColor: (UIColor*)tintColor { //// General Declarations CGContextRef context = UIGraphicsGetCurrentContext(); //// Color Declarations UIColor* color = [UIColor colorWithRed: 1 green: 1 blue: 1 alpha: 1]; UIColor* shadowColor = [UIColor colorWithRed: 0 green: 0 blue: 0 alpha: 0.498]; //// Shadow Declarations NSShadow* shadow = [[NSShadow alloc] init]; [shadow setShadowColor: shadowColor]; [shadow setShadowOffset: CGSizeMake(0.1, -0.1)]; [shadow setShadowBlurRadius: 5]; //// Oval Drawing UIBezierPath* ovalPath = [UIBezierPath bezierPathWithOvalInRect: CGRectMake(2.5, 2.5, 73, 73)]; CGContextSaveGState(context); CGContextSetShadowWithColor(context, shadow.shadowOffset, shadow.shadowBlurRadius, [shadow.shadowColor CGColor]); [color setFill]; [ovalPath fill]; CGContextRestoreGState(context); //// Bezier 2 Drawing UIBezierPath* bezier2Path = UIBezierPath.bezierPath; [bezier2Path moveToPoint: CGPointMake(54, 25)]; [bezier2Path addLineToPoint: CGPointMake(31.5, 47.5)]; [bezier2Path addLineToPoint: CGPointMake(22.5, 38.5)]; [bezier2Path addLineToPoint: CGPointMake(19.5, 41.5)]; [bezier2Path addLineToPoint: CGPointMake(31.5, 53.5)]; [bezier2Path addLineToPoint: CGPointMake(57, 28)]; [bezier2Path addLineToPoint: CGPointMake(54, 25)]; [bezier2Path closePath]; [bezier2Path moveToPoint: CGPointMake(72.5, 39)]; [bezier2Path addCurveToPoint: CGPointMake(39, 72.5) controlPoint1: CGPointMake(72.5, 57.5) controlPoint2: CGPointMake(57.5, 72.5)]; [bezier2Path addCurveToPoint: CGPointMake(5.5, 39) controlPoint1: CGPointMake(20.5, 72.5) controlPoint2: CGPointMake(5.5, 57.5)]; [bezier2Path addCurveToPoint: CGPointMake(20.74, 10.91) controlPoint1: CGPointMake(5.5, 27.23) controlPoint2: CGPointMake(11.56, 16.89)]; [bezier2Path addCurveToPoint: CGPointMake(39, 5.5) controlPoint1: CGPointMake(25.99, 7.49) controlPoint2: CGPointMake(32.26, 5.5)]; [bezier2Path addCurveToPoint: CGPointMake(72.5, 39) controlPoint1: CGPointMake(57.5, 5.5) controlPoint2: CGPointMake(72.5, 20.5)]; [bezier2Path closePath]; [tintColor setFill]; [bezier2Path fill]; } + (void)drawSelectorOffIcon { //// General Declarations CGContextRef context = UIGraphicsGetCurrentContext(); //// Color Declarations UIColor* color = [UIColor colorWithRed: 1 green: 1 blue: 1 alpha: 1]; UIColor* shadowColor = [UIColor colorWithRed: 0 green: 0 blue: 0 alpha: 0.498]; //// Shadow Declarations NSShadow* shadow = [[NSShadow alloc] init]; [shadow setShadowColor: shadowColor]; [shadow setShadowOffset: CGSizeMake(0.1, -0.1)]; [shadow setShadowBlurRadius: 5]; //// Bezier Drawing UIBezierPath* bezierPath = UIBezierPath.bezierPath; [bezierPath moveToPoint: CGPointMake(39, 5.5)]; [bezierPath addCurveToPoint: CGPointMake(20.74, 10.91) controlPoint1: CGPointMake(32.26, 5.5) controlPoint2: CGPointMake(25.99, 7.49)]; [bezierPath addCurveToPoint: CGPointMake(5.5, 39) controlPoint1: CGPointMake(11.56, 16.89) controlPoint2: CGPointMake(5.5, 27.23)]; [bezierPath addCurveToPoint: CGPointMake(39, 72.5) controlPoint1: CGPointMake(5.5, 57.5) controlPoint2: CGPointMake(20.5, 72.5)]; [bezierPath addCurveToPoint: CGPointMake(72.5, 39) controlPoint1: CGPointMake(57.5, 72.5) controlPoint2: CGPointMake(72.5, 57.5)]; [bezierPath addCurveToPoint: CGPointMake(39, 5.5) controlPoint1: CGPointMake(72.5, 20.5) controlPoint2: CGPointMake(57.5, 5.5)]; [bezierPath closePath]; [bezierPath moveToPoint: CGPointMake(75.5, 39)]; [bezierPath addCurveToPoint: CGPointMake(39, 75.5) controlPoint1: CGPointMake(75.5, 59.16) controlPoint2: CGPointMake(59.16, 75.5)]; [bezierPath addCurveToPoint: CGPointMake(2.5, 39) controlPoint1: CGPointMake(18.84, 75.5) controlPoint2: CGPointMake(2.5, 59.16)]; [bezierPath addCurveToPoint: CGPointMake(18.06, 9.1) controlPoint1: CGPointMake(2.5, 26.63) controlPoint2: CGPointMake(8.65, 15.71)]; [bezierPath addCurveToPoint: CGPointMake(39, 2.5) controlPoint1: CGPointMake(23.98, 4.94) controlPoint2: CGPointMake(31.21, 2.5)]; [bezierPath addCurveToPoint: CGPointMake(75.5, 39) controlPoint1: CGPointMake(59.16, 2.5) controlPoint2: CGPointMake(75.5, 18.84)]; [bezierPath closePath]; CGContextSaveGState(context); CGContextSetShadowWithColor(context, shadow.shadowOffset, shadow.shadowBlurRadius, [shadow.shadowColor CGColor]); [color setFill]; [bezierPath fill]; CGContextRestoreGState(context); } + (UIImage*)imageOfSelectorOnIconWithTintColor: (UIColor*)tintColor { UIGraphicsBeginImageContextWithOptions(CGSizeMake(78, 78), NO, 0.0f); [self drawSelectorOnIconWithTintColor: tintColor]; UIImage* imageOfSelectorIcon = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); return imageOfSelectorIcon; } + (UIImage*)imageOfSelectorOffIcon { if (_imageOfSelectorOffIcon) return _imageOfSelectorOffIcon; UIGraphicsBeginImageContextWithOptions(CGSizeMake(78, 78), NO, 0.0f); [self drawSelectorOffIcon]; _imageOfSelectorOffIcon = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); return _imageOfSelectorOffIcon; } #pragma mark - + (void)drawFileIconWithExtensionText: (NSString*)text { //// General Declarations CGContextRef context = UIGraphicsGetCurrentContext(); //// Shadow Declarations NSShadow* shadow = [[NSShadow alloc] init]; [shadow setShadowColor: [UIColor.blackColor colorWithAlphaComponent: 0.5]]; [shadow setShadowOffset: CGSizeMake(0.1, -0.1)]; [shadow setShadowBlurRadius: 10]; NSShadow* shadow2 = [[NSShadow alloc] init]; [shadow2 setShadowColor: [UIColor.blackColor colorWithAlphaComponent: 0.5]]; [shadow2 setShadowOffset: CGSizeMake(0.1, -0.1)]; [shadow2 setShadowBlurRadius: 10]; //// Rectangle Drawing UIBezierPath* rectanglePath = UIBezierPath.bezierPath; [rectanglePath moveToPoint: CGPointMake(48, 219)]; [rectanglePath addLineToPoint: CGPointMake(186, 219)]; [rectanglePath addLineToPoint: CGPointMake(186, 60)]; [rectanglePath addLineToPoint: CGPointMake(141, 60)]; [rectanglePath addLineToPoint: CGPointMake(141, 15)]; [rectanglePath addLineToPoint: CGPointMake(48, 15)]; [rectanglePath addLineToPoint: CGPointMake(48, 219)]; [rectanglePath closePath]; CGContextSaveGState(context); CGContextSetShadowWithColor(context, shadow.shadowOffset, shadow.shadowBlurRadius, [shadow.shadowColor CGColor]); [UIColor.whiteColor setFill]; [rectanglePath fill]; CGContextRestoreGState(context); //// Rectangle 2 Drawing UIBezierPath* rectangle2Path = UIBezierPath.bezierPath; [rectangle2Path moveToPoint: CGPointMake(141, 60)]; [rectangle2Path addLineToPoint: CGPointMake(186, 60)]; [rectangle2Path addLineToPoint: CGPointMake(141, 15)]; [rectangle2Path addLineToPoint: CGPointMake(141, 36)]; [rectangle2Path addLineToPoint: CGPointMake(141, 60)]; [rectangle2Path closePath]; CGContextSaveGState(context); CGContextSetShadowWithColor(context, shadow2.shadowOffset, shadow2.shadowBlurRadius, [shadow2.shadowColor CGColor]); [UIColor.whiteColor setFill]; [rectangle2Path fill]; CGContextRestoreGState(context); //// Text Drawing CGRect textRect = CGRectMake(48, 161, 138, 50); NSMutableParagraphStyle* textStyle = NSMutableParagraphStyle.defaultParagraphStyle.mutableCopy; textStyle.alignment = NSTextAlignmentCenter; NSDictionary* textFontAttributes = @{NSFontAttributeName: [UIFont systemFontOfSize: 38], NSForegroundColorAttributeName: UIColor.grayColor, NSParagraphStyleAttributeName: textStyle}; CGFloat textTextHeight = [text boundingRectWithSize: CGSizeMake(textRect.size.width, INFINITY) options: NSStringDrawingUsesLineFragmentOrigin attributes: textFontAttributes context: nil].size.height; CGContextSaveGState(context); CGContextClipToRect(context, textRect); [text drawInRect: CGRectMake(CGRectGetMinX(textRect), CGRectGetMinY(textRect) + (CGRectGetHeight(textRect) - textTextHeight) / 2, CGRectGetWidth(textRect), textTextHeight) withAttributes: textFontAttributes]; CGContextRestoreGState(context); } + (UIImage*)imageOfFileIconWithExtensionText: (NSString*)text { UIGraphicsBeginImageContextWithOptions(CGSizeMake(234, 234), NO, 0.0f); [self drawFileIconWithExtensionText:text]; UIImage* imageOfSelectorIcon = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); return imageOfSelectorIcon; } #pragma mark - + (UIImage *)placeholderImageWithSize:(CGSize)size { UIGraphicsBeginImageContext(size); CGContextRef context = UIGraphicsGetCurrentContext(); UIColor *backgroundColor = [UIColor colorWithRed:(239.0 / 255.0) green:(239.0 / 255.0) blue:(244.0 / 255.0) alpha:1.0]; UIColor *iconColor = [UIColor colorWithRed:(179.0 / 255.0) green:(179.0 / 255.0) blue:(182.0 / 255.0) alpha:1.0]; // Background CGContextSetFillColorWithColor(context, [backgroundColor CGColor]); CGContextFillRect(context, CGRectMake(0, 0, size.width, size.height)); // Icon (back) CGRect backIconRect = CGRectMake(size.width * (16.0 / 68.0), size.height * (20.0 / 68.0), size.width * (32.0 / 68.0), size.height * (24.0 / 68.0)); CGContextSetFillColorWithColor(context, [iconColor CGColor]); CGContextFillRect(context, backIconRect); CGContextSetFillColorWithColor(context, [backgroundColor CGColor]); CGContextFillRect(context, CGRectInset(backIconRect, 1.0, 1.0)); // Icon (front) CGRect frontIconRect = CGRectMake(size.width * (20.0 / 68.0), size.height * (24.0 / 68.0), size.width * (32.0 / 68.0), size.height * (24.0 / 68.0)); CGContextSetFillColorWithColor(context, [backgroundColor CGColor]); CGContextFillRect(context, CGRectInset(frontIconRect, -1.0, -1.0)); CGContextSetFillColorWithColor(context, [iconColor CGColor]); CGContextFillRect(context, frontIconRect); CGContextSetFillColorWithColor(context, [backgroundColor CGColor]); CGContextFillRect(context, CGRectInset(frontIconRect, 1.0, 1.0)); UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); return image; } @end ================================================ FILE: Source/Cells/DBAssetGroupCell.h ================================================ // // DBAssetGroupCell.h // DBAttachmentPickerController // // Created by Denis Bogatyrev on 14.03.16. // // The MIT License (MIT) // Copyright (c) 2016 Denis Bogatyrev. // // Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), // to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, // and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS // IN THE SOFTWARE. // #import #import "DBAssetImageView.h" @interface DBAssetGroupCell : UITableViewCell @property (weak, nonatomic) IBOutlet DBAssetImageView *imageViewFront; @property (weak, nonatomic) IBOutlet UIImageView *imageViewMid; @property (weak, nonatomic) IBOutlet UIImageView *imageViewBack; @property (weak, nonatomic) IBOutlet UILabel *titleLabel; @property (weak, nonatomic) IBOutlet UILabel *countLabel; @end ================================================ FILE: Source/Cells/DBAssetGroupCell.m ================================================ // // DBAssetGroupCell.m // DBAttachmentPickerController // // Created by Denis Bogatyrev on 14.03.16. // // The MIT License (MIT) // Copyright (c) 2016 Denis Bogatyrev. // // Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), // to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, // and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS // IN THE SOFTWARE. // #import "DBAssetGroupCell.h" @implementation DBAssetGroupCell @end ================================================ FILE: Source/Cells/DBAssetGroupCell.xib ================================================ ================================================ FILE: Source/Cells/DBThumbnailPhotoCell.h ================================================ // // DBThumbnailPhotoCell.h // DBAttachmentPickerController // // Created by Denis Bogatyrev on 14.03.16. // // The MIT License (MIT) // Copyright (c) 2016 Denis Bogatyrev. // // Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), // to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, // and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS // IN THE SOFTWARE. // #import #import #import "DBAssetImageView.h" @interface DBThumbnailPhotoCell : UICollectionViewCell @property (weak, nonatomic, nullable) IBOutlet DBAssetImageView *assetImageView; @property (weak, nonatomic, nullable) IBOutlet UILabel *durationLabel; @property (assign, nonatomic) BOOL needsDisplayEmptySelectedIndicator; @property (copy, nonatomic, nullable) NSString *identifier; @property (nonatomic) PHImageRequestID phImageRequestID; @property (assign, nonatomic) CGFloat selectorOffset; + (_Nonnull instancetype)thumbnailImageCell; @end ================================================ FILE: Source/Cells/DBThumbnailPhotoCell.m ================================================ // // DBThumbnailPhotoCell.m // DBAttachmentPickerController // // Created by Denis Bogatyrev on 14.03.16. // // The MIT License (MIT) // Copyright (c) 2016 Denis Bogatyrev. // // Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), // to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, // and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS // IN THE SOFTWARE. // #import "DBThumbnailPhotoCell.h" #import "UIImage+DBAssetIcons.h" #import "NSBundle+DBLibrary.h" static const CGFloat kDefaultSelectorImageViewOffset = 4.f; @interface DBThumbnailPhotoCell () @property (weak, nonatomic) IBOutlet UIImageView *selectorImageView; @property (weak, nonatomic) IBOutlet NSLayoutConstraint *selectorImageViewRightConstraint; @end @implementation DBThumbnailPhotoCell #pragma mark - Class methods + (instancetype)thumbnailImageCell { DBThumbnailPhotoCell *view = [[[NSBundle dbAttachmentPickerBundle] loadNibNamed:NSStringFromClass([self class]) owner:nil options:nil] firstObject]; return view; } #pragma mark - Lifecycle - (void)awakeFromNib { [super awakeFromNib]; self.selectorImageView.image = [UIImage imageOfSelectorOffIcon]; self.selectorImageView.highlightedImage = [UIImage imageOfSelectorOnIconWithTintColor:self.tintColor]; } - (void)prepareForReuse { [super prepareForReuse]; self.selectorOffset = 0.f; } #pragma mark - Accessors - (void)setTintColor:(UIColor *)tintColor { [super setTintColor:tintColor]; [self.selectorImageView setTintColor:tintColor]; } - (void)setSelectorOffset:(CGFloat)selectorOffset { _selectorOffset = selectorOffset; const CGFloat maxOfsset = CGRectGetWidth(self.bounds) - kDefaultSelectorImageViewOffset - CGRectGetWidth(self.selectorImageView.frame); self.selectorImageViewRightConstraint.constant = MIN(maxOfsset, kDefaultSelectorImageViewOffset + selectorOffset); } - (void)setSelected:(BOOL)selected { [super setSelected:selected]; [self updateSelectorIndicatorStateIfNedded]; } - (void)setHighlighted:(BOOL)highlighted { [super setHighlighted:highlighted]; [self updateSelectorIndicatorStateIfNedded]; } - (void)setNeedsDisplayEmptySelectedIndicator:(BOOL)needsDisplayEmptySelectedIndicator { if (_needsDisplayEmptySelectedIndicator != needsDisplayEmptySelectedIndicator) { _needsDisplayEmptySelectedIndicator = needsDisplayEmptySelectedIndicator; [self updateSelectorIndicatorStateIfNedded]; } } #pragma mark Helpers - (void)updateSelectorIndicatorStateIfNedded { self.selectorImageView.highlighted = (self.selected || self.highlighted); self.selectorImageView.hidden = ( !self.selected && !self.needsDisplayEmptySelectedIndicator); } @end ================================================ FILE: Source/Cells/DBThumbnailPhotoCell.xib ================================================ ================================================ FILE: Source/Common/DBAssetImageView.h ================================================ // // DBAssetImageView.h // DBAttachmentPickerControllerExample // // Created by Denis Bogatyrev on 30.03.16. // Copyright © 2016 Denis Bogatyrev. All rights reserved. // #import #import @interface DBAssetImageView : UIImageView - (void)configureWithAssetMediaType:(PHAssetMediaType)mediaType subtype:(PHAssetMediaSubtype)mediaSubtype; - (void)configureCollectionSubtype:(PHAssetCollectionSubtype)collectionSubtype; @end ================================================ FILE: Source/Common/DBAssetImageView.m ================================================ // // DBAssetImageView.m // DBAttachmentPickerControllerExample // // Created by Denis Bogatyrev on 30.03.16. // Copyright © 2016 Denis Bogatyrev. All rights reserved. // @import Photos; #import "DBAssetImageView.h" #import "UIImage+DBAssetIcons.h" static const CGFloat kDefaultGradientHeight = 24.f; static const CGFloat kDefaultMediaTypeIconOffset = 4.f; static const CGSize kDefaultMediaTypeIconSize = {16.f, 16.f}; @interface DBAssetImageView () @property (strong, nonatomic) CAGradientLayer *gradient; @property (strong, nonatomic) UIImageView *mediaTypeImageView; @end @implementation DBAssetImageView - (void)awakeFromNib { self.mediaTypeImageView = [[UIImageView alloc] init]; [self addSubview:self.mediaTypeImageView]; self.gradient = [CAGradientLayer layer]; self.gradient.colors = [NSArray arrayWithObjects:(id)[[UIColor clearColor] CGColor], (id)[[[UIColor blackColor] colorWithAlphaComponent:.75f] CGColor], nil]; } - (void)configureWithAssetMediaType:(PHAssetMediaType)mediaType subtype:(PHAssetMediaSubtype)mediaSubtype { UIImage *iconImage = nil; if (mediaSubtype & PHAssetMediaSubtypeVideoHighFrameRate) { iconImage =[UIImage imageOfVideoHighFrameRateIcon]; } else if (mediaSubtype & PHAssetMediaSubtypeVideoTimelapse) { iconImage =[UIImage imageOfVideoTimelapseIcon]; } else if (mediaSubtype == PHAssetMediaSubtypePhotoPanorama) { iconImage =[UIImage imageOfSmartAlbumPanoramasIcon]; } else if (mediaType == PHAssetMediaTypeVideo) { iconImage =[UIImage imageOfVideoIcon]; } self.mediaTypeImageView.image = iconImage; if (iconImage) { [self.layer insertSublayer:self.gradient atIndex:0]; } else { [self.gradient removeFromSuperlayer]; } } - (void)configureCollectionSubtype:(PHAssetCollectionSubtype)collectionSubtype { UIImage *iconImage = nil; switch (collectionSubtype) { case PHAssetCollectionSubtypeSmartAlbumVideos: iconImage =[UIImage imageOfSmartAlbumVideosIcon]; break; case PHAssetCollectionSubtypeSmartAlbumPanoramas: iconImage =[UIImage imageOfSmartAlbumPanoramasIcon]; break; case PHAssetCollectionSubtypeSmartAlbumFavorites: iconImage =[UIImage imageOfSmartAlbumFavoritesIcon]; break; case PHAssetCollectionSubtypeSmartAlbumSlomoVideos: iconImage =[UIImage imageOfSmartAlbumSlomoVideosIcon]; break; case PHAssetCollectionSubtypeSmartAlbumTimelapses: iconImage =[UIImage imageOfSmartAlbumTimelapsesIcon]; break; case PHAssetCollectionSubtypeSmartAlbumSelfPortraits: iconImage =[UIImage imageOfSmartAlbumSelfPortraitsIcon]; break; case PHAssetCollectionSubtypeSmartAlbumBursts: iconImage =[UIImage imageOfSmartAlbumBurstsIcon]; break; case PHAssetCollectionSubtypeSmartAlbumScreenshots: iconImage =[UIImage imageOfSmartAlbumScreenshotsIcon]; break; default: break; } self.mediaTypeImageView.image = iconImage; if (iconImage) { [self.layer insertSublayer:self.gradient atIndex:0]; } else { [self.gradient removeFromSuperlayer]; } } - (void)layoutSubviews { CGSize iconSize = kDefaultMediaTypeIconSize; self.mediaTypeImageView.frame = CGRectMake(kDefaultMediaTypeIconOffset, CGRectGetHeight(self.bounds) - iconSize.height - kDefaultMediaTypeIconOffset, iconSize.width, iconSize.height); self.gradient.frame = CGRectMake(.0f, CGRectGetHeight(self.bounds) - kDefaultGradientHeight, CGRectGetWidth(self.bounds), kDefaultGradientHeight); } @end ================================================ FILE: Source/DBAssetPickerController/DBAssetGroupsViewController.h ================================================ // // DBAssetGroupsViewController.h // DBAttachmentPickerController // // Created by Denis Bogatyrev on 14.03.16. // // The MIT License (MIT) // Copyright (c) 2016 Denis Bogatyrev. // // Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), // to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, // and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS // IN THE SOFTWARE. // #import @class DBAssetGroupsViewController; @protocol DBAssetGroupsViewControllerDelegate NS_ASSUME_NONNULL_BEGIN @optional - (void)DBAssetGroupsViewController:(DBAssetGroupsViewController *)controller didSelectAssetColoection:(PHAssetCollection *)assetCollection; - (void)DBAssetGroupsViewControllerDidCancel:(DBAssetGroupsViewController *)controller; NS_ASSUME_NONNULL_END @end @interface DBAssetGroupsViewController : UITableViewController @property (weak, nonatomic, nullable) id assetGroupsDelegate; @property (assign, nonatomic) PHAssetMediaType assetMediaType; @end ================================================ FILE: Source/DBAssetPickerController/DBAssetGroupsViewController.m ================================================ // // DBAssetGroupsViewController.m // DBAttachmentPickerController // // Created by Denis Bogatyrev on 14.03.16. // // The MIT License (MIT) // Copyright (c) 2016 Denis Bogatyrev. // // Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), // to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, // and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS // IN THE SOFTWARE. // @import Photos; #import "DBAssetGroupsViewController.h" #import "DBAssetItemsViewController.h" #import "DBAssetGroupCell.h" #import "UIImage+DBAssetIcons.h" #import "NSBundle+DBLibrary.h" static const CGSize kDefaultThumbnailSize = {68.f, 68.f}; static NSString *const kAssetGroupsCellIdentifier = @"DBAssetGroupCellID"; @interface DBAssetGroupsViewController () @property (nonatomic, copy) NSArray *fetchResults; @property (nonatomic, copy) NSArray *assetCollections; @end @implementation DBAssetGroupsViewController - (void)viewDidLoad { [super viewDidLoad]; self.navigationItem.title = NSLocalizedString(@"Albums", nil); self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemCancel target:self action:@selector(cancelButtonDidSelect:)]; [self.tableView registerNib:[UINib nibWithNibName:NSStringFromClass([DBAssetGroupCell class]) bundle:[NSBundle dbAttachmentPickerBundle]] forCellReuseIdentifier:kAssetGroupsCellIdentifier]; PHFetchResult *smartAlbums = [PHAssetCollection fetchAssetCollectionsWithType:PHAssetCollectionTypeSmartAlbum subtype:PHAssetCollectionSubtypeAny options:nil]; PHFetchResult *userAlbums = [PHAssetCollection fetchAssetCollectionsWithType:PHAssetCollectionTypeAlbum subtype:PHAssetCollectionSubtypeAny options:nil]; self.fetchResults = @[smartAlbums, userAlbums]; [self updateAssetCollections]; } #pragma mark - Actions - (void)cancelButtonDidSelect:(UIBarButtonItem *)sender { if ([self.assetGroupsDelegate respondsToSelector:@selector(DBAssetGroupsViewControllerDidCancel:)]) { [self.assetGroupsDelegate DBAssetGroupsViewControllerDidCancel:self]; } } #pragma mark - - (void)updateAssetCollections { // Filter albums NSArray *assetCollectionSubtypes = @[ // @(PHAssetCollectionSubtypeAlbumRegular), // @(PHAssetCollectionSubtypeAlbumImported), @(PHAssetCollectionSubtypeSmartAlbumUserLibrary), @(PHAssetCollectionSubtypeAlbumMyPhotoStream), @(PHAssetCollectionSubtypeSmartAlbumFavorites), @(PHAssetCollectionSubtypeSmartAlbumSelfPortraits), @(PHAssetCollectionSubtypeSmartAlbumPanoramas), @(PHAssetCollectionSubtypeSmartAlbumVideos), @(PHAssetCollectionSubtypeSmartAlbumSlomoVideos), @(PHAssetCollectionSubtypeSmartAlbumTimelapses), @(PHAssetCollectionSubtypeSmartAlbumBursts), @(PHAssetCollectionSubtypeSmartAlbumScreenshots), @(PHAssetCollectionSubtypeAlbumCloudShared) ]; NSMutableDictionary *smartAlbums = [NSMutableDictionary dictionaryWithCapacity:assetCollectionSubtypes.count]; NSMutableArray *userAlbums = [NSMutableArray array]; for (PHFetchResult *fetchResult in self.fetchResults) { [fetchResult enumerateObjectsUsingBlock:^(PHAssetCollection *assetCollection, NSUInteger index, BOOL *stop) { PHAssetCollectionSubtype subtype = assetCollection.assetCollectionSubtype; if (subtype == PHAssetCollectionSubtypeAlbumRegular) { [userAlbums addObject:assetCollection]; } else if ([assetCollectionSubtypes containsObject:@(subtype)]) { if (!smartAlbums[@(subtype)]) { smartAlbums[@(subtype)] = [NSMutableArray array]; } [smartAlbums[@(subtype)] addObject:assetCollection]; } }]; } NSMutableArray *assetCollections = [NSMutableArray array]; // Fetch smart albums for (NSNumber *assetCollectionSubtype in assetCollectionSubtypes) { NSArray *collections = smartAlbums[assetCollectionSubtype]; for (PHAssetCollection *assetCollection in collections) { PHFetchOptions *options = [PHFetchOptions new]; if (self.assetMediaType == PHAssetMediaTypeVideo || self.assetMediaType == PHAssetMediaTypeImage) { options.predicate = [NSPredicate predicateWithFormat:@"mediaType == %ld", self.assetMediaType]; } PHFetchResult *fetchResult = [PHAsset fetchAssetsInAssetCollection:assetCollection options:options]; if (fetchResult.count) { [assetCollections addObject:assetCollection]; } } } // Fetch user albums [assetCollections addObjectsFromArray:userAlbums]; self.assetCollections = assetCollections; } #pragma mark - PHPhotoLibraryChangeObserver - (void)photoLibraryDidChange:(PHChange *)changeInstance { dispatch_async(dispatch_get_main_queue(), ^{ // Update fetch results NSMutableArray *fetchResults = [self.fetchResults mutableCopy]; [self.fetchResults enumerateObjectsUsingBlock:^(PHFetchResult *fetchResult, NSUInteger index, BOOL *stop) { PHFetchResultChangeDetails *changeDetails = [changeInstance changeDetailsForFetchResult:fetchResult]; if (changeDetails) { [fetchResults replaceObjectAtIndex:index withObject:changeDetails.fetchResultAfterChanges]; } }]; if (![self.fetchResults isEqualToArray:fetchResults]) { self.fetchResults = fetchResults; // Reload albums [self updateAssetCollections]; [self.tableView reloadData]; } }); } #pragma mark - UITableView DataSource && Delegate - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { return self.assetCollections.count; } - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { DBAssetGroupCell *cell = [tableView dequeueReusableCellWithIdentifier:kAssetGroupsCellIdentifier]; if (cell == nil) { cell = [[DBAssetGroupCell alloc] init]; } [self configureCell:cell atIndexPath:indexPath]; return cell; } - (void)configureCell:(DBAssetGroupCell *)cell atIndexPath:(NSIndexPath *)indexPath { PHAssetCollection *assetCollection = self.assetCollections[indexPath.row]; PHFetchOptions *options = [PHFetchOptions new]; if (self.assetMediaType == PHAssetMediaTypeVideo || self.assetMediaType == PHAssetMediaTypeImage) { options.predicate = [NSPredicate predicateWithFormat:@"mediaType == %ld", self.assetMediaType]; } PHFetchResult *fetchResult = [PHAsset fetchAssetsInAssetCollection:assetCollection options:options]; cell.titleLabel.text = assetCollection.localizedTitle; cell.countLabel.text = [NSString stringWithFormat:@"%lu", (long)fetchResult.count]; [cell.imageViewFront configureCollectionSubtype:assetCollection.assetCollectionSubtype]; [self configureImageViewForCell:cell atIndexPath:indexPath fetchResult:fetchResult]; } - (void)configureImageViewForCell:(DBAssetGroupCell *)cell atIndexPath:(NSIndexPath *)indexPath fetchResult:(PHFetchResult *)fetchResult { cell.tag = indexPath.row; cell.imageViewBack.hidden = (fetchResult.count >= 1 && fetchResult.count <=2); cell.imageViewMid.hidden = (fetchResult.count == 1); PHImageManager *imageManager = [PHImageManager defaultManager]; CGFloat scale = [UIScreen mainScreen].scale; CGSize size = CGSizeMake( kDefaultThumbnailSize.width * scale, kDefaultThumbnailSize.height * scale ); PHImageContentMode contentMode = PHImageContentModeAspectFill; if (fetchResult.count >= 3) { [imageManager requestImageForAsset:fetchResult[fetchResult.count - 3] targetSize:size contentMode:contentMode options:nil resultHandler:^(UIImage *result, NSDictionary *info) { if (cell.tag == indexPath.row) { cell.imageViewBack.image = result; } }]; } if (fetchResult.count >= 2) { [imageManager requestImageForAsset:fetchResult[fetchResult.count - 2] targetSize:size contentMode:contentMode options:nil resultHandler:^(UIImage *result, NSDictionary *info) { if (cell.tag == indexPath.row) { cell.imageViewMid.image = result; } }]; } if (fetchResult.count >= 1) { [imageManager requestImageForAsset:fetchResult[fetchResult.count - 1] targetSize:size contentMode:contentMode options:nil resultHandler:^(UIImage *result, NSDictionary *info) { if (cell.tag == indexPath.row) { cell.imageViewFront.image = result; } }]; } if (fetchResult.count == 0) { UIImage *placeholderImage = [UIImage placeholderImageWithSize:cell.imageViewFront.frame.size]; cell.imageViewFront.image = placeholderImage; cell.imageViewMid.image = placeholderImage; cell.imageViewBack.image = placeholderImage; } } - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { [tableView deselectRowAtIndexPath:indexPath animated:YES]; if ([self.assetGroupsDelegate respondsToSelector:@selector(DBAssetGroupsViewController:didSelectAssetColoection:)]) { PHAssetCollection *assetCollection = self.assetCollections[indexPath.row]; [self.assetGroupsDelegate DBAssetGroupsViewController:self didSelectAssetColoection:assetCollection]; } } @end ================================================ FILE: Source/DBAssetPickerController/DBAssetGroupsViewController.xib ================================================ ================================================ FILE: Source/DBAssetPickerController/DBAssetItemsViewController.h ================================================ // // DBAssetItemsViewController.h // DBAttachmentPickerController // // Created by Denis Bogatyrev on 14.03.16. // // The MIT License (MIT) // Copyright (c) 2016 Denis Bogatyrev. // // Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), // to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, // and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS // IN THE SOFTWARE. // #import @class DBAssetItemsViewController, PHAsset; @protocol DBAssetItemsViewControllerDelegate NS_ASSUME_NONNULL_BEGIN @optional - (void)DBAssetItemsViewController:(DBAssetItemsViewController *)controller didFinishPickingAssetArray:(NSArray *)assetArray; - (BOOL)DBAssetImageViewControllerAllowsMultipleSelection:(DBAssetItemsViewController *)controller; NS_ASSUME_NONNULL_END @end @interface DBAssetItemsViewController : UICollectionViewController @property (weak, nonatomic, nullable) id assetItemsDelegate; @property (strong, nonatomic, nonnull) PHAssetCollection *assetCollection; @property (assign, nonatomic) PHAssetMediaType assetMediaType; @end ================================================ FILE: Source/DBAssetPickerController/DBAssetItemsViewController.m ================================================ // // DBAssetItemsViewController.m // DBAttachmentPickerController // // Created by Denis Bogatyrev on 14.03.16. // // The MIT License (MIT) // Copyright (c) 2016 Denis Bogatyrev. // // Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), // to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, // and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS // IN THE SOFTWARE. // @import Photos; #import "DBAssetItemsViewController.h" #import "DBThumbnailPhotoCell.h" #import "NSIndexSet+DBLibrary.h" #import "NSBundle+DBLibrary.h" static const NSInteger kNumberItemsPerRowPortrait = 4; static const NSInteger kNumberItemsPerRowLandscape = 7; static const CGFloat kDefaultItemOffset = 1.f; static NSString *const kPhotoCellIdentifier = @"DBThumbnailPhotoCellID"; @interface DBAssetItemsViewController () @property (strong, nonatomic) PHFetchResult *assetsFetchResults; @property (strong, nonatomic) PHCachingImageManager *imageManager; @property (strong, nonatomic) NSMutableArray *selectedIndexPathArray; @end @implementation DBAssetItemsViewController static NSString * const reuseIdentifier = @"Cell"; - (void)viewDidLoad { [super viewDidLoad]; self.selectedIndexPathArray = [NSMutableArray arrayWithCapacity:100]; self.navigationItem.title = self.assetCollection.localizedTitle; if ([self.assetItemsDelegate respondsToSelector:@selector(DBAssetImageViewControllerAllowsMultipleSelection:)]) { if ( [self.assetItemsDelegate DBAssetImageViewControllerAllowsMultipleSelection:self] ) { self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:NSLocalizedString(@"Attach", nil) style:UIBarButtonItemStyleDone target:self action:@selector(attachButtonDidSelect:)]; } } UICollectionViewFlowLayout *flowLayout = [[UICollectionViewFlowLayout alloc]init]; flowLayout.sectionInset = UIEdgeInsetsMake(kDefaultItemOffset, kDefaultItemOffset, kDefaultItemOffset, kDefaultItemOffset); flowLayout.minimumLineSpacing = kDefaultItemOffset; flowLayout.minimumInteritemSpacing = kDefaultItemOffset; self.collectionView.collectionViewLayout = flowLayout; self.collectionView.allowsMultipleSelection = YES; self.imageManager = [[PHCachingImageManager alloc] init]; [self.imageManager stopCachingImagesForAllAssets]; [self.collectionView registerNib:[UINib nibWithNibName:NSStringFromClass([DBThumbnailPhotoCell class]) bundle:[NSBundle dbAttachmentPickerBundle]] forCellWithReuseIdentifier:kPhotoCellIdentifier]; [[PHPhotoLibrary sharedPhotoLibrary] registerChangeObserver:self]; } - (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id)coordinator { [super viewWillTransitionToSize:size withTransitionCoordinator:coordinator]; [self.collectionView.collectionViewLayout invalidateLayout]; } - (void)dealloc { [[PHPhotoLibrary sharedPhotoLibrary] unregisterChangeObserver:self]; } #pragma mark - Actions - (void)attachButtonDidSelect:(UIBarButtonItem *)sender { if ([self.assetItemsDelegate respondsToSelector:@selector(DBAssetItemsViewController:didFinishPickingAssetArray:)]) { [self.assetItemsDelegate DBAssetItemsViewController:self didFinishPickingAssetArray:[self getSelectedAssetArray]]; } } #pragma mark Helpers - (NSArray *)getSelectedAssetArray { NSArray *selectedItems = self.selectedIndexPathArray; NSMutableArray *assetArray = [NSMutableArray arrayWithCapacity:selectedItems.count]; for (NSIndexPath *indexPath in selectedItems) { PHAsset *asset = self.assetsFetchResults[indexPath.item]; [assetArray addObject:asset]; } return [assetArray copy]; } #pragma mark - Accessors - (void)setAssetCollection:(PHAssetCollection *)assetCollection { _assetCollection = assetCollection; [self updateFetchRequest]; [self.collectionView reloadData]; } #pragma mark - Fetching Assets - (void)updateFetchRequest { if (self.assetCollection) { PHFetchOptions *allPhotosOptions = [PHFetchOptions new]; allPhotosOptions.sortDescriptors = @[[NSSortDescriptor sortDescriptorWithKey:@"creationDate" ascending:NO]]; if (self.assetMediaType == PHAssetMediaTypeVideo || self.assetMediaType == PHAssetMediaTypeImage) { allPhotosOptions.predicate = [NSPredicate predicateWithFormat:@"mediaType == %ld", self.assetMediaType]; } self.assetsFetchResults = [PHAsset fetchAssetsInAssetCollection:self.assetCollection options:allPhotosOptions]; [self.collectionView reloadData]; } else { self.assetsFetchResults = nil; } } #pragma mark - PHPhotoLibraryChangeObserver - (void)photoLibraryDidChange:(PHChange *)changeInstance { dispatch_async(dispatch_get_main_queue(), ^{ PHFetchResultChangeDetails *collectionChanges = [changeInstance changeDetailsForFetchResult:self.assetsFetchResults]; if (collectionChanges) { self.assetsFetchResults = [collectionChanges fetchResultAfterChanges]; if (![collectionChanges hasIncrementalChanges] || [collectionChanges hasMoves]) { [self.collectionView reloadData]; } else { [self.collectionView performBatchUpdates:^{ NSIndexSet *removedIndexes = [collectionChanges removedIndexes]; if ([removedIndexes count]) { [self.collectionView deleteItemsAtIndexPaths:[removedIndexes indexPathsFromIndexesWithSection:0]]; } NSIndexSet *insertedIndexes = [collectionChanges insertedIndexes]; if ([insertedIndexes count]) { [self.collectionView insertItemsAtIndexPaths:[insertedIndexes indexPathsFromIndexesWithSection:0]]; } NSIndexSet *changedIndexes = [collectionChanges changedIndexes]; if ([changedIndexes count]) { [self.collectionView reloadItemsAtIndexPaths:[changedIndexes indexPathsFromIndexesWithSection:0]]; } } completion:nil]; } [self.imageManager stopCachingImagesForAllAssets]; for (NSIndexPath *indexPath in [self.collectionView indexPathsForSelectedItems]) { [self.collectionView deselectItemAtIndexPath:indexPath animated:YES]; } [self.selectedIndexPathArray removeAllObjects]; } }); } #pragma mark - UICollectionView DataSource && Delegate - (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section { return self.assetsFetchResults.count; } - (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath { DBThumbnailPhotoCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:kPhotoCellIdentifier forIndexPath:indexPath]; if (cell == nil) { cell = [[DBThumbnailPhotoCell alloc] init]; } [self configurePhotoCell:cell atIndexPath:indexPath]; return cell; } - (void)configurePhotoCell:(DBThumbnailPhotoCell *)cell atIndexPath:(NSIndexPath *)indexPath { PHAsset *asset = self.assetsFetchResults[indexPath.item]; cell.tintColor = self.collectionView.tintColor; cell.identifier = asset.localIdentifier; cell.needsDisplayEmptySelectedIndicator = NO; [self.imageManager cancelImageRequest:cell.phImageRequestID]; [cell.assetImageView configureWithAssetMediaType:asset.mediaType subtype:asset.mediaSubtypes]; if (asset.mediaType == PHAssetMediaTypeVideo) { NSDateComponentsFormatter *formatter = [[NSDateComponentsFormatter alloc] init]; formatter.zeroFormattingBehavior = NSDateComponentsFormatterZeroFormattingBehaviorPad; formatter.unitsStyle = NSDateComponentsFormatterUnitsStylePositional; formatter.allowedUnits = NSCalendarUnitMinute | NSCalendarUnitSecond; cell.durationLabel.text = [formatter stringFromTimeInterval:asset.duration]; } else { cell.durationLabel.text = nil; } CGFloat scale = [UIScreen mainScreen].scale; CGSize size = [self collectionItemCellSizeAtIndexPath:indexPath]; CGSize scaledThumbnailSize = CGSizeMake( size.width * scale, size.height * scale ); PHImageRequestOptions *options = [[PHImageRequestOptions alloc] init]; options.resizeMode = PHImageRequestOptionsResizeModeExact; options.deliveryMode = PHImageRequestOptionsDeliveryModeOpportunistic; cell.phImageRequestID = [self.imageManager requestImageForAsset:asset targetSize:scaledThumbnailSize contentMode:PHImageContentModeAspectFill options:options resultHandler:^(UIImage *result, NSDictionary *info) { if ([cell.identifier isEqualToString:asset.localIdentifier]) { cell.assetImageView.image = result; } }]; } - (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath { [self.selectedIndexPathArray addObject:indexPath]; BOOL allowsMultipleSelection = NO; if ([self.assetItemsDelegate respondsToSelector:@selector(DBAssetImageViewControllerAllowsMultipleSelection:)]) { allowsMultipleSelection = [self.assetItemsDelegate DBAssetImageViewControllerAllowsMultipleSelection:self]; } if ( !allowsMultipleSelection ) { [self attachButtonDidSelect:nil]; } } - (void)collectionView:(UICollectionView *)collectionView didDeselectItemAtIndexPath:(NSIndexPath *)indexPath { [self.selectedIndexPathArray removeObject:indexPath]; } #pragma mark UICollectionViewDelegateFlowLayout - (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath { return [self collectionItemCellSizeAtIndexPath:indexPath]; } #pragma mark Helpers - (CGSize)collectionItemCellSizeAtIndexPath:(NSIndexPath *)indexPath { if (self.assetCollection.assetCollectionSubtype == PHAssetCollectionSubtypeSmartAlbumPanoramas) { PHAsset *asset = self.assetsFetchResults[indexPath.item]; const CGFloat coef = (CGFloat)asset.pixelWidth / (CGFloat)asset.pixelHeight; CGFloat itemWidth = CGRectGetWidth(self.collectionView.bounds) - kDefaultItemOffset *2; return CGSizeMake( itemWidth, itemWidth / coef ); } else { NSInteger numberOfItems = [self numberOfItemsPerRow]; CGFloat itemWidth = floorf( ( CGRectGetWidth(self.collectionView.bounds) - kDefaultItemOffset * ( numberOfItems + 1 ) ) / numberOfItems ); return CGSizeMake(itemWidth, itemWidth); } } - (NSInteger)numberOfItemsPerRow { UIInterfaceOrientation orientation = [[UIApplication sharedApplication] statusBarOrientation]; const BOOL isLandscape = ( orientation == UIInterfaceOrientationLandscapeLeft || orientation == UIInterfaceOrientationLandscapeRight); return isLandscape ? kNumberItemsPerRowLandscape : kNumberItemsPerRowPortrait; } @end ================================================ FILE: Source/DBAssetPickerController/DBAssetItemsViewController.xib ================================================ ================================================ FILE: Source/DBAssetPickerController/DBAssetPickerController.h ================================================ // // DBAssetPickerController.h // DBAttachmentPickerController // // Created by Denis Bogatyrev on 14.03.16. // // The MIT License (MIT) // Copyright (c) 2016 Denis Bogatyrev. // // Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), // to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, // and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS // IN THE SOFTWARE. // #import @class DBAssetPickerController, PHAsset; @protocol DBAssetPickerControllerDelegate NS_ASSUME_NONNULL_BEGIN @optional - (void)DBAssetPickerController:(DBAssetPickerController *)controller didFinishPickingAssetArray:(NSArray *)assetArray; - (void)DBAssetPickerControllerDidCancel:(DBAssetPickerController *)controller; - (BOOL)DBAssetPickerControllerAllowsMultipleSelection:(DBAssetPickerController *)controller; NS_ASSUME_NONNULL_END @end @interface DBAssetPickerController : UINavigationController @property (weak, nonatomic, nullable) id assetPickerDelegate; @property (assign, nonatomic) PHAssetMediaType assetMediaType; @end ================================================ FILE: Source/DBAssetPickerController/DBAssetPickerController.m ================================================ // // DBAssetPickerController.m // DBAttachmentPickerController // // Created by Denis Bogatyrev on 14.03.16. // // The MIT License (MIT) // Copyright (c) 2016 Denis Bogatyrev. // // Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), // to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, // and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS // IN THE SOFTWARE. // @import Photos; #import "DBAssetPickerController.h" #import "DBAssetGroupsViewController.h" #import "DBAssetItemsViewController.h" #import "NSBundle+DBLibrary.h" @interface DBAssetPickerController () @end @implementation DBAssetPickerController - (void)viewDidLoad { [super viewDidLoad]; DBAssetGroupsViewController *groupController = [[DBAssetGroupsViewController alloc] initWithNibName:NSStringFromClass([DBAssetGroupsViewController class]) bundle:[NSBundle dbAttachmentPickerBundle]]; groupController.assetMediaType = self.assetMediaType; groupController.assetGroupsDelegate = self; [self setViewControllers:@[groupController]]; } #pragma mark - DBAssetGroupsViewControllerDelegate - (void)DBAssetGroupsViewController:(DBAssetGroupsViewController *)controller didSelectAssetColoection:(PHAssetCollection *)assetCollection { DBAssetItemsViewController *itemsController = [[DBAssetItemsViewController alloc] initWithNibName:NSStringFromClass([DBAssetItemsViewController class]) bundle:[NSBundle dbAttachmentPickerBundle]]; itemsController.assetMediaType = self.assetMediaType; itemsController.assetItemsDelegate = self; itemsController.assetCollection = assetCollection; [self pushViewController:itemsController animated:YES]; } - (void)DBAssetGroupsViewControllerDidCancel:(DBAssetGroupsViewController *)controller { if ([self.assetPickerDelegate respondsToSelector:@selector(DBAssetPickerControllerDidCancel:)]) { [self.assetPickerDelegate DBAssetPickerControllerDidCancel:self]; } } #pragma mark = DBAssetItemsViewControllerDelegate - (void)DBAssetItemsViewController:(nonnull DBAssetItemsViewController *)controller didFinishPickingAssetArray:(nonnull NSArray *)assetArray { if ([self.assetPickerDelegate respondsToSelector:@selector(DBAssetPickerController:didFinishPickingAssetArray:)]) { [self.assetPickerDelegate DBAssetPickerController:self didFinishPickingAssetArray:assetArray]; } } - (BOOL)DBAssetImageViewControllerAllowsMultipleSelection:(DBAssetItemsViewController *)controller { BOOL allowsMultipleSelection = NO; if ([self.assetPickerDelegate respondsToSelector:@selector(DBAssetPickerControllerAllowsMultipleSelection:)]) { allowsMultipleSelection = [self.assetPickerDelegate DBAssetPickerControllerAllowsMultipleSelection:self]; } return allowsMultipleSelection; } @end ================================================ FILE: Source/DBAttachmentAlertController/DBAttachmentAlertController.h ================================================ // // DBAttachmentAlertController.h // DBAttachmentPickerController // // Created by Denis Bogatyrev on 14.03.16. // // The MIT License (MIT) // Copyright (c) 2016 Denis Bogatyrev. // // Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), // to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, // and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS // IN THE SOFTWARE. // #import typedef void (^AlertAttachAssetsHandler)(NSArray * _Nonnull assetArray); typedef void (^AlertActionHandler)(UIAlertAction * _Nonnull action); @interface DBAttachmentAlertController : UIAlertController @property (assign, nonatomic, readonly) PHAssetMediaType assetMediaType; @property (assign, nonatomic, readonly) BOOL allowsMultipleSelection; + (_Nonnull instancetype)attachmentAlertControllerWithMediaType:(PHAssetMediaType)assetMediaType allowsMultipleSelection:(BOOL)allowsMultipleSelection allowsMediaLibrary:(BOOL)allowsPhotoOrVideo allowsOtherApps:(BOOL)allowsOtherApps attachHandler:(nullable AlertAttachAssetsHandler)attachHandler allAlbumsHandler:(nullable AlertActionHandler)allAlbumsHandler takePictureHandler:(nullable AlertActionHandler)takePictureHandler otherAppsHandler:(nullable AlertActionHandler)otherAppsHandler cancelHandler:(nullable AlertActionHandler)cancelHandler; @end ================================================ FILE: Source/DBAttachmentAlertController/DBAttachmentAlertController.m ================================================ // // DBAttachmentAlertController.m // DBAttachmentPickerController // // Created by Denis Bogatyrev on 14.03.16. // // The MIT License (MIT) // Copyright (c) 2016 Denis Bogatyrev. // // Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), // to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, // and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS // IN THE SOFTWARE. // @import Photos; #import "DBAttachmentAlertController.h" #import "DBAttachmentPickerController.h" #import "DBThumbnailPhotoCell.h" #import "NSIndexSet+DBLibrary.h" static const CGFloat kDefaultThumbnailHeight = 100.f; static const CGFloat kDefaultItemOffset = 11.f; static const CGFloat kDefaultInteritemSpacing = 4.f; static NSString *const kPhotoCellIdentifier = @"DBThumbnailPhotoCellID"; @interface DBAttachmentAlertController () @property (assign, nonatomic) BOOL showCollectionView; @property (strong, nonatomic) UICollectionView *collectionView; @property (copy, nonatomic) NSString *attachActionText; @property (strong, nonatomic) PHFetchResult *assetsFetchResult; @property (strong, nonatomic) PHCachingImageManager *imageManager; @property (assign, nonatomic) PHAssetMediaType assetMediaType; @property (assign, nonatomic) BOOL needsDisplayEmptySelectedIndicator; @property (assign, nonatomic) BOOL allowsMultipleSelection; @property (strong, nonatomic) NSMutableArray *selectedIndexPathArray; @property (strong, nonatomic) AlertAttachAssetsHandler extensionAttachHandler; @end @implementation DBAttachmentAlertController #pragma mark - Class methods + (_Nonnull instancetype)attachmentAlertControllerWithMediaType:(PHAssetMediaType)assetMediaType allowsMultipleSelection:(BOOL)allowsMultipleSelection allowsMediaLibrary:(BOOL)allowsPhotoOrVideo allowsOtherApps:(BOOL)allowsOtherApps attachHandler:(nullable AlertAttachAssetsHandler)attachHandler allAlbumsHandler:(nullable AlertActionHandler)allAlbumsHandler takePictureHandler:(nullable AlertActionHandler)takePictureHandler otherAppsHandler:(nullable AlertActionHandler)otherAppsHandler cancelHandler:(nullable AlertActionHandler)cancelHandler { const BOOL showPhotoOrVideo = allowsPhotoOrVideo; DBAttachmentAlertController *controller = [DBAttachmentAlertController alertControllerWithTitle:nil message:nil preferredStyle:UIAlertControllerStyleActionSheet]; controller.assetMediaType = assetMediaType; controller.allowsMultipleSelection = allowsMultipleSelection; controller.showCollectionView = ( showPhotoOrVideo && controller.assetsFetchResult.count ); controller.title = ( controller.showCollectionView ? @"\n\n\n\n\n" : NSLocalizedString(@"Attach files", @"Title") ); if ( showPhotoOrVideo && controller.assetsFetchResult.count ) { __weak DBAttachmentAlertController *weakController = controller; UIAlertAction *attachAction = [UIAlertAction actionWithTitle:NSLocalizedString(@"All albums", @"Button on main menu") style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) { if ([weakController.collectionView indexPathsForSelectedItems].count) { if (attachHandler) { attachHandler([weakController getSelectedAssetArray]); } } else if (allAlbumsHandler) { allAlbumsHandler(action); } }]; [controller addAction:attachAction]; controller.attachActionText = attachAction.title; controller.extensionAttachHandler = ^(NSArray * _Nonnull assetArray) { [weakController dismissViewControllerAnimated:YES completion: ^{ if (attachHandler) { attachHandler(assetArray); } }]; }; } if ( showPhotoOrVideo ) { NSString *buttonTitle; switch (controller.assetMediaType) { case PHAssetMediaTypeVideo: buttonTitle = NSLocalizedString(@"Take a video", @"Button on main menu"); break; case PHAssetMediaTypeImage: buttonTitle = NSLocalizedString(@"Take a picture", @"Button on main menu"); break; default: buttonTitle = NSLocalizedString(@"Take a picture or a video", @"Button on main menu"); break; } UIAlertAction *cameraAction = [UIAlertAction actionWithTitle:buttonTitle style:UIAlertActionStyleDefault handler:takePictureHandler]; [controller addAction:cameraAction]; } if (allowsOtherApps) { UIAlertAction *otherAppsAction = [UIAlertAction actionWithTitle:NSLocalizedString(@"Other apps", @"Button on main menu") style:UIAlertActionStyleDefault handler:otherAppsHandler]; [controller addAction:otherAppsAction]; } UIAlertAction *actionCancel = [UIAlertAction actionWithTitle:NSLocalizedString(@"Cancel", @"Common") style:UIAlertActionStyleCancel handler:cancelHandler]; [controller addAction:actionCancel]; return controller; } #pragma mark - Lifecycle - (void)viewDidLoad { [super viewDidLoad]; self.selectedIndexPathArray = [NSMutableArray arrayWithCapacity:100]; if (self.showCollectionView) { UICollectionViewFlowLayout *flowLayout = [[UICollectionViewFlowLayout alloc]init]; [flowLayout setScrollDirection:UICollectionViewScrollDirectionHorizontal]; flowLayout.sectionInset = UIEdgeInsetsMake(.0f, kDefaultItemOffset, .0f, kDefaultItemOffset); flowLayout.minimumInteritemSpacing = kDefaultInteritemSpacing; CGRect collectionRect = CGRectMake(.0f, .0f, self.view.bounds.size.width, kDefaultThumbnailHeight + kDefaultItemOffset *2); self.collectionView = [[UICollectionView alloc] initWithFrame:collectionRect collectionViewLayout:flowLayout]; self.collectionView.autoresizingMask = UIViewAutoresizingFlexibleWidth; self.collectionView.backgroundColor = [UIColor clearColor]; self.collectionView.allowsMultipleSelection = YES; self.collectionView.delegate = self; self.collectionView.dataSource = self; self.collectionView.tintColor = [[[UIApplication sharedApplication] delegate] window].tintColor; [self.collectionView registerNib:[UINib nibWithNibName:NSStringFromClass([DBThumbnailPhotoCell class]) bundle:[NSBundle dbAttachmentPickerBundle]] forCellWithReuseIdentifier:kPhotoCellIdentifier]; [self.view addSubview:self.collectionView]; self.imageManager = [[PHCachingImageManager alloc] init]; [self.imageManager stopCachingImagesForAllAssets]; [[PHPhotoLibrary sharedPhotoLibrary] registerChangeObserver:self]; } } - (void)viewDidAppear:(BOOL)animated { [super viewDidAppear:animated]; [self recalculateVisibleCellsSelectorOffsetWithScrollViewOffsetX:self.collectionView.contentOffset.x]; } - (void)dealloc { if (self.showCollectionView) { [[PHPhotoLibrary sharedPhotoLibrary] unregisterChangeObserver:self]; } } #pragma mark Helpers - (PHFetchResult *)assetsFetchResult { if (_assetsFetchResult == nil) { PHFetchOptions *allPhotosOptions = [PHFetchOptions new]; allPhotosOptions.sortDescriptors = @[[NSSortDescriptor sortDescriptorWithKey:@"creationDate" ascending:NO]]; if (self.assetMediaType == PHAssetMediaTypeImage || self.assetMediaType == PHAssetMediaTypeVideo) { allPhotosOptions.predicate = [NSPredicate predicateWithFormat:@"mediaType == %ld", self.assetMediaType]; _assetsFetchResult = [PHAsset fetchAssetsWithMediaType:self.assetMediaType options:allPhotosOptions]; } else { _assetsFetchResult = [PHAsset fetchAssetsWithOptions:allPhotosOptions]; } } return _assetsFetchResult; } - (NSArray *)getSelectedAssetArray { NSArray *selectedItems = self.selectedIndexPathArray; NSMutableArray *assetArray = [NSMutableArray arrayWithCapacity:selectedItems.count]; for (NSIndexPath *indexPath in selectedItems) { PHAsset *asset = self.assetsFetchResult[indexPath.item]; [assetArray addObject:asset]; } return [assetArray copy]; } #pragma mark - Accessors - (void)setNeedsDisplayEmptySelectedIndicator:(BOOL)needsDisplayEmptySelectedIndicator { if (_needsDisplayEmptySelectedIndicator != needsDisplayEmptySelectedIndicator) { _needsDisplayEmptySelectedIndicator = needsDisplayEmptySelectedIndicator; for (DBThumbnailPhotoCell *cell in self.collectionView.visibleCells) { cell.needsDisplayEmptySelectedIndicator = needsDisplayEmptySelectedIndicator; } } } - (void)setAttachActionText:(NSString *)attachActionText { if (![_attachActionText isEqualToString:attachActionText]) { if (self.isViewLoaded) { UILabel *attachLabel = [self attachActionLabelForView:self.view]; attachLabel.text = [attachActionText copy]; } _attachActionText = attachActionText; } } #pragma mark Helpers - (UILabel *)attachActionLabelForView:(UIView *)baseView { UILabel *label = nil; if ([baseView isKindOfClass:[UILabel class]] && [((UILabel *)baseView).text isEqualToString:self.attachActionText]) { label = (UILabel *)baseView; } else if (baseView.subviews.count > 0) { for (UIView *subview in baseView.subviews) { label = [self attachActionLabelForView:subview]; if (label) break; } } return label; } #pragma mark - PHPhotoLibraryChangeObserver - (void)photoLibraryDidChange:(PHChange *)changeInstance { dispatch_async(dispatch_get_main_queue(), ^{ PHFetchResultChangeDetails *collectionChanges = [changeInstance changeDetailsForFetchResult:self.assetsFetchResult]; if (collectionChanges) { self.assetsFetchResult = [collectionChanges fetchResultAfterChanges]; if (![collectionChanges hasIncrementalChanges] || [collectionChanges hasMoves]) { [self.collectionView reloadData]; } else { [self.collectionView performBatchUpdates:^{ NSIndexSet *removedIndexes = [collectionChanges removedIndexes]; if ([removedIndexes count]) { [self.collectionView deleteItemsAtIndexPaths:[removedIndexes indexPathsFromIndexesWithSection:0]]; } NSIndexSet *insertedIndexes = [collectionChanges insertedIndexes]; if ([insertedIndexes count]) { [self.collectionView insertItemsAtIndexPaths:[insertedIndexes indexPathsFromIndexesWithSection:0]]; } NSIndexSet *changedIndexes = [collectionChanges changedIndexes]; if ([changedIndexes count]) { [self.collectionView reloadItemsAtIndexPaths:[changedIndexes indexPathsFromIndexesWithSection:0]]; } } completion:nil]; } [self.imageManager stopCachingImagesForAllAssets]; for (NSIndexPath *indexPath in [self.collectionView indexPathsForSelectedItems]) { [self.collectionView deselectItemAtIndexPath:indexPath animated:YES]; } [self.selectedIndexPathArray removeAllObjects]; [self updateAttachPhotoCountIfNedded]; } }); } #pragma mark - UICollectionView DataSource && Delegate - (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section { return self.assetsFetchResult.count; } - (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath { DBThumbnailPhotoCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:kPhotoCellIdentifier forIndexPath:indexPath]; if (cell == nil) { cell = [DBThumbnailPhotoCell thumbnailImageCell]; } [self configurePhotoCell:cell atIndexPath:indexPath]; return cell; } - (void)configurePhotoCell:(DBThumbnailPhotoCell *)cell atIndexPath:(NSIndexPath *)indexPath { PHAsset *asset = self.assetsFetchResult[indexPath.item]; cell.tintColor = self.collectionView.tintColor; cell.identifier = asset.localIdentifier; cell.needsDisplayEmptySelectedIndicator = self.needsDisplayEmptySelectedIndicator; [cell.assetImageView configureWithAssetMediaType:asset.mediaType subtype:asset.mediaSubtypes]; if (asset.mediaType == PHAssetMediaTypeVideo) { NSDateComponentsFormatter *formatter = [[NSDateComponentsFormatter alloc] init]; formatter.zeroFormattingBehavior = NSDateComponentsFormatterZeroFormattingBehaviorPad; formatter.unitsStyle = NSDateComponentsFormatterUnitsStylePositional; formatter.allowedUnits = NSCalendarUnitMinute | NSCalendarUnitSecond; cell.durationLabel.text = [formatter stringFromTimeInterval:asset.duration]; } else { cell.durationLabel.text = nil; } CGFloat scale = [UIScreen mainScreen].scale; CGSize cellSize = [self collectionItemCellSizeAtIndexPath:indexPath]; CGSize scaledThumbnailSize = CGSizeMake( cellSize.width * scale, cellSize.height * scale ); [self.imageManager requestImageForAsset:asset targetSize:scaledThumbnailSize contentMode:PHImageContentModeAspectFill options:nil resultHandler:^(UIImage *result, NSDictionary *info) { if ([cell.identifier isEqualToString:asset.localIdentifier]) { cell.assetImageView.image = result; } }]; } - (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath { [self.selectedIndexPathArray addObject:indexPath]; if (self.allowsMultipleSelection) { [self updateAttachPhotoCountIfNedded]; self.needsDisplayEmptySelectedIndicator = YES; } else { self.extensionAttachHandler([self getSelectedAssetArray]); } } - (void)collectionView:(UICollectionView *)collectionView didDeselectItemAtIndexPath:(NSIndexPath *)indexPath { [self.selectedIndexPathArray removeObject:indexPath]; [self updateAttachPhotoCountIfNedded]; } #pragma mark Helpers - (void)updateAttachPhotoCountIfNedded { NSArray *selectedItems = [self.collectionView indexPathsForSelectedItems]; self.attachActionText = ( selectedItems.count ? [NSString stringWithFormat:NSLocalizedString(@"Attach %zd file(s)", @"Button on main menu"), selectedItems.count] : NSLocalizedString(@"All albums", nil) ); } #pragma mark UICollectionViewDelegateFlowLayout - (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath { return [self collectionItemCellSizeAtIndexPath:indexPath]; } #pragma mark - UIScrollViewDelegate - (void)scrollViewDidScroll:(UIScrollView *)scrollView { [self recalculateVisibleCellsSelectorOffsetWithScrollViewOffsetX:scrollView.contentOffset.x]; } #pragma mark Helpers - (CGSize)collectionItemCellSizeAtIndexPath:(NSIndexPath *)indexPath { PHAsset *asset = self.assetsFetchResult[indexPath.item]; const CGFloat coef = (CGFloat)asset.pixelWidth / (CGFloat)asset.pixelHeight; CGFloat maxWidth = CGRectGetWidth(self.collectionView.bounds) - kDefaultInteritemSpacing *2; return CGSizeMake( MIN( kDefaultThumbnailHeight * coef, maxWidth ), kDefaultThumbnailHeight ); } - (void)recalculateVisibleCellsSelectorOffsetWithScrollViewOffsetX:(CGFloat)offsetX { for (DBThumbnailPhotoCell *cell in self.collectionView.visibleCells) { BOOL isLastItem = ( offsetX + CGRectGetWidth(self.collectionView.frame) < CGRectGetMaxX(cell.frame) ); cell.selectorOffset = ( isLastItem ? CGRectGetMaxX(cell.frame) - ( offsetX + CGRectGetWidth(self.collectionView.frame) ) : .0f); } } @end ================================================ FILE: Source/DBAttachmentPickerController.h ================================================ // // DBAttachmentPickerController.h // DBAttachmentPickerController // // Created by Denis Bogatyrev on 14.03.16. // // The MIT License (MIT) // Copyright (c) 2016 Denis Bogatyrev. // // Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), // to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, // and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS // IN THE SOFTWARE. // #import #import "NSBundle+DBLibrary.h" typedef NS_OPTIONS(NSUInteger, DBAttachmentMediaType) { DBAttachmentMediaTypeImage = (1 << 0), DBAttachmentMediaTypeVideo = (1 << 1), DBAttachmentMediaTypeOther = (1 << 2), }; UIKIT_EXTERN const DBAttachmentMediaType DBAttachmentMediaTypeMaskAll; NS_ASSUME_NONNULL_BEGIN @class DBAttachment; typedef void (^FinishPickingBlock)(NSArray * attachmentArray); typedef void (^FinishImagePickingBlock)(NSArray * imageArray); typedef void (^FinishVideoPickingBlock)(NSArray* resourceArray); typedef void (^CancelBlock)(); @interface DBAttachmentPickerController : NSObject /*! @brief Used to provide opportunity to correctly calculate position popover view when app works on iPad. You can specify UIButton, UITableViewCell, etc. instance to which the user touched. @attention The parameter must contain only UIView subclass instance or nil */ @property (weak, nonatomic) UIView *senderView; /*! @brief Used to determine the types of attachments that can be picked @note You can specify one or more than one from following types:
● @a DBAttachmentMediaTypePhoto - photo from camera, camera roll or other apps;
● @a DBAttachmentMediaTypeVideo - video from camera, camera roll or other apps;
● @a DBAttachmentMediaTypeOther - any files from other apps.
Also you can use @a DBAttachmentMediaTypeMaskAll to select all available types. */ @property (assign, nonatomic) DBAttachmentMediaType mediaType; // default is DBAttachmentMediaTypeMaskAll /*! @brief Used to determine the quality of the captured video from camera */ @property (assign, nonatomic) UIImagePickerControllerQualityType capturedVideoQulity; // default is UIImagePickerControllerQualityTypeMedium /*! @brief Used to add Other Apps button @attention To correctly work this option you must select iCloud Documents capability on project settings. To view detail information, see README.md. */ @property (assign, nonatomic) BOOL allowsSelectionFromOtherApps; // default is NO /*! @brief Used to allow multiple selection where it possible */ @property (assign, nonatomic) BOOL allowsMultipleSelection; // default is NO /*! @brief Creates and returns an attachment picker controller @see presentOnViewController: @param finishPickingBlock The block will be performed when user select attachment(s) @param cancelBlock The block will be performed when user select cancel button @return An instance attachment picker controller */ + (instancetype)attachmentPickerControllerFinishPickingBlock:(FinishPickingBlock)finishPickingBlock cancelBlock:(_Nullable CancelBlock)cancelBlock; /*! @brief Creates and returns an attachment picker controller with constant media type (image) @see presentOnViewController: @param finishPickingBlock The block will be performed when user select image(s) @param cancelBlock The block will be performed when user select cancel button @return An instance attachment picker controller */ + (instancetype)imagePickerControllerFinishPickingBlock:(FinishImagePickingBlock)finishPickingBlock cancelBlock:(_Nullable CancelBlock)cancelBlock; /*! @brief Creates and returns an attachment picker controller with constant media type (video) @see presentOnViewController: @param finishPickingBlock The block will be performed when user select video(s) @param cancelBlock The block will be performed when user select cancel button @return An instance attachment picker controller */ + (instancetype)videoPickerControllerFinishPickingBlock:(FinishVideoPickingBlock)finishPickingBlock cancelBlock:(_Nullable CancelBlock)cancelBlock; /*! @brief Present attachment picker controller on specify UIViewController @param initialViewController The view controller to present the attachment picker controller */ - (void)presentOnViewController:(UIViewController *)initialViewController; @end NS_ASSUME_NONNULL_END ================================================ FILE: Source/DBAttachmentPickerController.m ================================================ // // DBAttachmentPickerController.m // DBAttachmentPickerController // // Created by Denis Bogatyrev on 14.03.16. // // The MIT License (MIT) // Copyright (c) 2016 Denis Bogatyrev. // // Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), // to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, // and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS // IN THE SOFTWARE. // @import Photos; #import #import "DBAttachmentPickerController.h" #import "DBAttachmentAlertController.h" #import "DBAttachment.h" #import "DBAssetPickerController.h" const DBAttachmentMediaType DBAttachmentMediaTypeMaskAll = DBAttachmentMediaTypeImage | DBAttachmentMediaTypeVideo | DBAttachmentMediaTypeOther; @interface DBAttachmentPickerController () @property (strong, nonatomic) UIViewController *initialViewController; @property (strong, nonatomic) DBAttachmentAlertController *alertController; @property (strong, nonatomic) FinishPickingBlock extendedFinishPickingBlock; @property (strong, nonatomic) CancelBlock extendedCancelBlock; @property (assign, nonatomic) BOOL ignoreChangeMediaType; @end @implementation DBAttachmentPickerController #pragma mark - Class methods + (instancetype)attachmentPickerControllerFinishPickingBlock:(FinishPickingBlock)finishPickingBlock cancelBlock:(_Nullable CancelBlock)cancelBlock { DBAttachmentPickerController *controller = [[DBAttachmentPickerController alloc] init]; controller.mediaType = DBAttachmentMediaTypeMaskAll; controller.allowsSelectionFromOtherApps = NO; controller.allowsMultipleSelection = NO; controller.capturedVideoQulity = UIImagePickerControllerQualityTypeMedium; #pragma clang diagnostic push #pragma clang diagnostic ignored "-Warc-retain-cycles" controller.extendedFinishPickingBlock = ^void(NSArray * attachmentArray) { if (finishPickingBlock) finishPickingBlock(attachmentArray); [controller dismissAttachmentPicker]; }; controller.extendedCancelBlock = ^void() { if (cancelBlock) cancelBlock(); [controller dismissAttachmentPicker]; }; #pragma clang diagnostic pop return controller; } + (instancetype)imagePickerControllerFinishPickingBlock:(FinishImagePickingBlock)finishPickingBlock cancelBlock:(_Nullable CancelBlock)cancelBlock { void (^finishBlock)(NSArray * _Nonnull attachmentArray) = ^(NSArray * _Nonnull attachmentArray) { dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{ dispatch_group_t loadGroup = dispatch_group_create(); NSMutableDictionary *imageDict = [NSMutableDictionary dictionaryWithCapacity:attachmentArray.count]; for (DBAttachment *attachment in attachmentArray) { dispatch_group_enter(loadGroup); [attachment loadOriginalImageWithCompletion:^(UIImage * _Nullable resultImage) { if (resultImage) { [imageDict setObject:resultImage forKey:@([attachment hash])]; } dispatch_group_leave(loadGroup); }]; } dispatch_group_wait(loadGroup, DISPATCH_TIME_FOREVER); dispatch_async(dispatch_get_main_queue(), ^{ NSMutableArray *imageArray = [NSMutableArray arrayWithCapacity:attachmentArray.count]; for (DBAttachment *attachment in attachmentArray) { UIImage *image = imageDict[@([attachment hash])]; if (image) { [imageArray addObject:image]; } } if (finishPickingBlock) { finishPickingBlock([imageArray copy]); } }); }); }; DBAttachmentPickerController *controller = [self attachmentPickerControllerFinishPickingBlock:finishBlock cancelBlock:cancelBlock]; controller.mediaType = DBAttachmentMediaTypeImage; controller.ignoreChangeMediaType = YES; return controller; } + (instancetype)videoPickerControllerFinishPickingBlock:(FinishVideoPickingBlock)finishPickingBlock cancelBlock:(_Nullable CancelBlock)cancelBlock { void (^finishBlock)(NSArray * _Nonnull attachmentArray) = ^(NSArray * _Nonnull attachmentArray) { NSMutableArray *resourceArray = [NSMutableArray arrayWithCapacity:attachmentArray.count]; for (DBAttachment *attachment in attachmentArray) { id resource = [attachment originalFileResource]; if (resource) { [resourceArray addObject:resource]; } } if (finishPickingBlock) { finishPickingBlock([resourceArray copy]); } }; DBAttachmentPickerController *controller = [self attachmentPickerControllerFinishPickingBlock:finishBlock cancelBlock:cancelBlock]; controller.mediaType = DBAttachmentMediaTypeVideo; controller.ignoreChangeMediaType = YES; return controller; } #pragma mark - Lifecycle - (void)dealloc { [self.alertController dismissViewControllerAnimated:YES completion:^{ NSLog(@"Error: Responder must initialize DBAttachmentPickerController through special method attachmentPickerControllerFinishPickingBlock:cancelBlock:"); }]; } #pragma mark - Public - (void)presentOnViewController:(UIViewController *)initialViewController { self.initialViewController = initialViewController; __weak typeof(self) weakSelf = self; self.alertController = [DBAttachmentAlertController attachmentAlertControllerWithMediaType:[self assetMediaType] allowsMultipleSelection:self.allowsMultipleSelection allowsMediaLibrary:( (self.mediaType & DBAttachmentMediaTypeImage) || (self.mediaType & DBAttachmentMediaTypeVideo) ) allowsOtherApps:self.allowsSelectionFromOtherApps attachHandler:^(NSArray *assetArray) { NSArray *attachmentArray = [weakSelf attachmentArrayFromPHAssetArray:assetArray]; [weakSelf finishPickingWithAttachmentArray:attachmentArray]; } allAlbumsHandler:^(UIAlertAction *action) { [weakSelf allAlbumsDidSelect]; } takePictureHandler:^(UIAlertAction *action) { [weakSelf takePictureButtonDidSelect]; } otherAppsHandler:^(UIAlertAction *action) { [weakSelf otherAppsButtonDidSelect]; } cancelHandler:^(UIAlertAction * _Nonnull action) { [weakSelf cancelDidSelect]; }]; self.alertController.popoverPresentationController.sourceView = [self popoverPresentationView]; self.alertController.popoverPresentationController.sourceRect = [self popoverPresentationRect]; self.alertController.popoverPresentationController.permittedArrowDirections = [self popoverPresentationArrowDirection]; [self.initialViewController presentViewController:self.alertController animated:YES completion:^{ weakSelf.alertController = nil; }]; } - (void)dismissAttachmentPicker { self.extendedFinishPickingBlock = nil; self.extendedCancelBlock = nil; } #pragma mark Helpers - (PHAssetMediaType)assetMediaType { if ( (self.mediaType & DBAttachmentMediaTypeImage) && !(self.mediaType & DBAttachmentMediaTypeVideo) ) { return PHAssetMediaTypeImage; } else if ( !(self.mediaType & DBAttachmentMediaTypeImage) && (self.mediaType & DBAttachmentMediaTypeVideo) ) { return PHAssetMediaTypeVideo; } return PHAssetMediaTypeUnknown; } - (NSArray *)attachmentArrayFromPHAssetArray:(NSArray *)assetArray { NSMutableArray *attachmentArray = [NSMutableArray arrayWithCapacity:assetArray.count]; for (PHAsset *asset in assetArray) { DBAttachment *model = [DBAttachment attachmentFromPHAsset:asset]; [attachmentArray addObject:model]; } return [attachmentArray copy]; } #pragma mark - Popover presentation options - (UIView *)popoverPresentationView { return ( self.senderView ? self.senderView : self.initialViewController.view ); } - (CGRect)popoverPresentationRect { return ( self.senderView ? self.senderView.bounds : CGRectMake( CGRectGetMidX(self.initialViewController.view.bounds), CGRectGetMidY(self.initialViewController.view.bounds), .0f, .0f) ); } - (UIPopoverArrowDirection)popoverPresentationArrowDirection { return ( self.senderView ? UIPopoverArrowDirectionAny : 0 ); } #pragma mark - Accessors - (void)setMediaType:(DBAttachmentMediaType)mediaType { if (self.ignoreChangeMediaType) return; _mediaType = mediaType; } #pragma mark - Actions - (void)allAlbumsDidSelect { DBAssetPickerController *viewController =[[DBAssetPickerController alloc] init]; viewController.assetMediaType = [self assetMediaType]; viewController.assetPickerDelegate = self; [self.initialViewController presentViewController:viewController animated:YES completion:nil]; } - (void)otherAppsButtonDidSelect { NSMutableArray *documentMediaTypes = [NSMutableArray arrayWithCapacity:10]; if (self.mediaType & DBAttachmentMediaTypeImage) { [documentMediaTypes addObject:(NSString *)kUTTypeImage]; } if (self.mediaType & DBAttachmentMediaTypeVideo) { [documentMediaTypes addObject:(NSString *)kUTTypeVideo]; [documentMediaTypes addObject:(NSString *)kUTTypeMovie]; } if (self.mediaType & DBAttachmentMediaTypeOther) { [documentMediaTypes addObject:(NSString *)kUTTypeItem]; } @try { UIDocumentMenuViewController *viewController = [[UIDocumentMenuViewController alloc] initWithDocumentTypes:documentMediaTypes inMode:UIDocumentPickerModeImport]; viewController.delegate = self; viewController.modalPresentationStyle = UIModalPresentationFullScreen; viewController.popoverPresentationController.sourceView = [self popoverPresentationView]; viewController.popoverPresentationController.sourceRect = [self popoverPresentationRect]; viewController.popoverPresentationController.permittedArrowDirections = [self popoverPresentationArrowDirection]; [self.initialViewController presentViewController:viewController animated:YES completion:nil]; } @catch (NSException *exception) { [self cancelWithAlertErrorText:NSLocalizedString(@"Can't load Document Picker", @"Error text")]; } } - (void)takePictureButtonDidSelect { if (![UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]) { [self cancelWithAlertErrorText:NSLocalizedString(@"Device has no camera", @"Error text")]; return; } UIImagePickerController *picker = [[UIImagePickerController alloc] init]; picker.delegate = self; picker.allowsEditing = NO; picker.sourceType = UIImagePickerControllerSourceTypeCamera; if ( (self.mediaType & DBAttachmentMediaTypeImage) && !(self.mediaType & DBAttachmentMediaTypeVideo) ) { picker.mediaTypes = @[(NSString *)kUTTypeImage]; picker.cameraCaptureMode = UIImagePickerControllerCameraCaptureModePhoto; } else if ( !(self.mediaType & DBAttachmentMediaTypeImage) && (self.mediaType & DBAttachmentMediaTypeVideo) ) { picker.mediaTypes = @[(NSString *)kUTTypeMovie, (NSString *)kUTTypeVideo]; picker.cameraCaptureMode = UIImagePickerControllerCameraCaptureModeVideo; picker.videoQuality = self.capturedVideoQulity; } else { picker.mediaTypes = @[(NSString *)kUTTypeMovie, (NSString *)kUTTypeVideo, (NSString *)kUTTypeImage]; picker.cameraCaptureMode = UIImagePickerControllerCameraCaptureModePhoto; } [self.initialViewController presentViewController:picker animated:YES completion:nil]; } #pragma mark - (void)finishPickingWithAttachmentArray:(NSArray *)attachmentArray { self.extendedFinishPickingBlock(attachmentArray); } - (void)cancelDidSelect { self.extendedCancelBlock(); } - (void)cancelWithAlertErrorText:(NSString *)errorText { UIAlertController *alertController = [UIAlertController alertControllerWithTitle:NSLocalizedString(@"Error", @"Common") message:errorText preferredStyle:UIAlertControllerStyleAlert]; UIAlertAction *actionCancel = [UIAlertAction actionWithTitle:NSLocalizedString(@"OK", @"Common") style:UIAlertActionStyleCancel handler:nil]; [alertController addAction:actionCancel]; [self.initialViewController presentViewController:alertController animated:YES completion:nil]; self.extendedCancelBlock(); } #pragma mark - UIDocumentMenuViewControllerDelegate - (void)documentMenu:(UIDocumentMenuViewController *)documentMenu didPickDocumentPicker:(UIDocumentPickerViewController *)documentPicker { documentPicker.delegate = self; [self.initialViewController presentViewController:documentPicker animated:YES completion:nil]; } - (void)documentMenuWasCancelled:(UIDocumentMenuViewController *)documentMenu { [self cancelDidSelect]; } #pragma mark - UIDocumentPickerViewControllerDelegate - (void)documentPicker:(UIDocumentPickerViewController *)controller didPickDocumentAtURL:(NSURL *)url { DBAttachment *attachment = [DBAttachment attachmentFromDocumentURL:url]; [self finishPickingWithAttachmentArray:@[attachment]]; } - (void)documentPickerWasCancelled:(UIDocumentPickerViewController *)controller { [self cancelDidSelect]; } #pragma mark - UIImagePickerControllerDelegate - (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info { NSString *mediaType = info[UIImagePickerControllerMediaType]; NSArray *attachmentArray = @[]; if ([mediaType isEqualToString:(NSString *)kUTTypeImage]) { UIImage *image = info[UIImagePickerControllerOriginalImage]; DBAttachment *attachment = [DBAttachment attachmentFromCameraImage:image]; attachmentArray = @[attachment]; } else if ([mediaType isEqualToString:(NSString *)kUTTypeMovie] || [mediaType isEqualToString:(NSString *)kUTTypeVideo]) { NSURL *documentURL = info[UIImagePickerControllerMediaURL]; if (documentURL) { DBAttachment *attachment = [DBAttachment attachmentFromDocumentURL:documentURL]; attachmentArray = @[attachment]; } } __weak typeof(self) weakSelf = self; [picker dismissViewControllerAnimated:YES completion:^{ [weakSelf finishPickingWithAttachmentArray:attachmentArray]; }]; } - (void)imagePickerControllerDidCancel:(UIImagePickerController *)picker { __weak typeof(self) weakSelf = self; [picker dismissViewControllerAnimated:YES completion:^{ [weakSelf cancelDidSelect]; }]; } #pragma mark - DBImagePickerControllerDelegate - (void)DBAssetPickerController:(nonnull DBAssetPickerController *)controller didFinishPickingAssetArray:(nonnull NSArray *)assetArray { __weak typeof(self) weakSelf = self; [controller dismissViewControllerAnimated:YES completion:^{ NSArray *attachmentArray = [weakSelf attachmentArrayFromPHAssetArray:assetArray]; [weakSelf finishPickingWithAttachmentArray:attachmentArray]; }]; } - (void)DBAssetPickerControllerDidCancel:(nonnull DBAssetPickerController *)controller { __weak typeof(self) weakSelf = self; [controller dismissViewControllerAnimated:YES completion:^{ [weakSelf cancelDidSelect]; }]; } - (BOOL)DBAssetPickerControllerAllowsMultipleSelection:(DBAssetPickerController *)controller { return self.allowsMultipleSelection; } @end ================================================ FILE: Source/Localization/de.lproj/DBAttachmentPickerController.stringsdict ================================================ Attach %zd file(s) NSStringLocalizedFormatKey %#@files@ files NSStringFormatSpecTypeKey NSStringPluralRuleType NSStringFormatValueTypeKey zd one Bringen Sie %zd Datei other Bringen Sie %zd Dateien ================================================ FILE: Source/Localization/en.lproj/DBAttachmentPickerController.stringsdict ================================================ Attach %zd file(s) NSStringLocalizedFormatKey %#@files@ files NSStringFormatSpecTypeKey NSStringPluralRuleType NSStringFormatValueTypeKey zd one Attach %zd file other Attach %zd files ================================================ FILE: Source/Localization/es.lproj/DBAttachmentPickerController.stringsdict ================================================ Attach %zd file(s) NSStringLocalizedFormatKey %#@files@ files NSStringFormatSpecTypeKey NSStringPluralRuleType NSStringFormatValueTypeKey zd one Adjuntar %zd archivo other Adjuntar %zd archivos ================================================ FILE: Source/Localization/fr.lproj/DBAttachmentPickerController.stringsdict ================================================ Attach %zd file(s) NSStringLocalizedFormatKey %#@files@ files NSStringFormatSpecTypeKey NSStringPluralRuleType NSStringFormatValueTypeKey zd one Joindre %zd fichier other Joindre %zd fichiers ================================================ FILE: Source/Localization/ja.lproj/DBAttachmentPickerController.stringsdict ================================================ Attach %zd file(s) NSStringLocalizedFormatKey %#@files@ files NSStringFormatSpecTypeKey NSStringPluralRuleType NSStringFormatValueTypeKey zd other %zdファイルを添付 ================================================ FILE: Source/Localization/pl-PL.lproj/DBAttachmentPickerController.stringsdict ================================================ Attach %zd file(s) NSStringLocalizedFormatKey %#@files@ files NSStringFormatSpecTypeKey NSStringPluralRuleType NSStringFormatValueTypeKey zd one Załącz %zd plik many Załącz %zd pliki other Załącz %zd plików ================================================ FILE: Source/Localization/pt-BR.lproj/DBAttachmentPickerController.stringsdict ================================================ Attach %zd file(s) NSStringLocalizedFormatKey %#@files@ files NSStringFormatSpecTypeKey NSStringPluralRuleType NSStringFormatValueTypeKey zd one Anexar %zd arquivo other Anexar %zd arquivos ================================================ FILE: Source/Localization/ru.lproj/DBAttachmentPickerController.strings ================================================ /* No comment provided by engineer. */ "Albums" = "Альбомы"; /* Button on main menu */ "All albums" = "Все альбомы"; /* No comment provided by engineer. */ "Attach" = "Прикрепить"; /* Button on main menu */ "Attach %zd file(s)" = "Прикрепить %zd файл(ов)"; /* Title */ "Attach files" = "Прикрепить файлы"; /* Error text */ "Can't load Document Picker" = "Невозможно загрузить Document Picker"; /* Common */ "Cancel" = "Отменить"; /* Error text */ "Device has no camera" = "Устройство не имеет камеры"; /* Common */ "Error" = "Ошибка"; /* Common */ "OK" = "Ок"; /* Button on main menu */ "Other apps" = "Другие приложения"; /* Button on main menu */ "Take a picture" = "Сделать фото"; /* Button on main menu */ "Take a picture or a video" = "Сделать фото или видео"; /* Button on main menu */ "Take a video" = "Сделать видео"; ================================================ FILE: Source/Localization/ru.lproj/DBAttachmentPickerController.stringsdict ================================================ Attach %zd file(s) NSStringLocalizedFormatKey %#@files@ files NSStringFormatSpecTypeKey NSStringPluralRuleType NSStringFormatValueTypeKey zd one Прикрепить %zd файл many Прикрепить %zd файлов other Прикрепить %zd файла ================================================ FILE: Source/Localization/uk.lproj/DBAttachmentPickerController.stringsdict ================================================ Attach %zd file(s) NSStringLocalizedFormatKey %#@files@ files NSStringFormatSpecTypeKey NSStringPluralRuleType NSStringFormatValueTypeKey zd one Докласти %zd файл other Докласти %zd файлів ================================================ FILE: Source/Localization/zh-Hans.lproj/DBAttachmentPickerController.stringsdict ================================================ Attach %zd file(s) NSStringLocalizedFormatKey %#@files@ files NSStringFormatSpecTypeKey NSStringPluralRuleType NSStringFormatValueTypeKey zd other 附加 %zd 个文件 ================================================ FILE: Source/Localization/zh-Hant.lproj/DBAttachmentPickerController.stringsdict ================================================ Attach %zd file(s) NSStringLocalizedFormatKey %#@files@ files NSStringFormatSpecTypeKey NSStringPluralRuleType NSStringFormatValueTypeKey zd other 附加 %zd 個文件 ================================================ FILE: Source/Models/DBAttachment.h ================================================ // // DBAttachment.h // DBAttachmentPickerController // // Created by Denis Bogatyrev on 14.03.16. // // The MIT License (MIT) // Copyright (c) 2016 Denis Bogatyrev. // // Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), // to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, // and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS // IN THE SOFTWARE. // #import #import "DBAttachmentPickerController.h" typedef NS_ENUM(NSInteger, DBAttachmentSourceType) { DBAttachmentSourceTypeUnknown = 0, DBAttachmentSourceTypePHAsset, DBAttachmentSourceTypeImage, DBAttachmentSourceTypeDocumentURL, }; @class PHAsset; @interface DBAttachment : NSObject /*! @brief The name of the file. Can be empty. */ @property (strong, nonatomic, readonly, nullable) NSString *fileName; /*! @brief Creation date of the file. Can be nil. */ @property (strong, nonatomic, readonly, nullable) NSDate *creationDate; /*! @brief Size of the file in byte. Available only for existing files. @attention If you want get file size for PHAsset or something like that, you should calculate it after getting file data. @see fileSizeStr */ @property (assign, nonatomic, readonly) NSUInteger fileSize; /*! @brief Formatted string of file size. Can be empty. @see fileSize */ @property (readonly, nullable) NSString *fileSizeStr; /*! @brief Attachment source type */ @property (assign, nonatomic, readonly) DBAttachmentSourceType sourceType; /*! @brief Attachment media type */ @property (assign, nonatomic, readonly) DBAttachmentMediaType mediaType; NS_ASSUME_NONNULL_BEGIN /*! @brief Creates and returns an attachment on the basis of PHAsset @see attachmentFromCameraImage: @see attachmentFromDocumentURL: @param asset PHAsset object @return An instance attachment object */ + (instancetype)attachmentFromPHAsset:(PHAsset *)asset; /*! @brief Creates and returns an attachment on the basis of UIImage @see attachmentFromPHAsset: @see attachmentFromDocumentURL: @param image UIImage object @return An instance attachment object */ + (instancetype)attachmentFromCameraImage:(UIImage *)image; /*! @brief Creates and returns an attachment on the basis of NSURL @see attachmentFromPHAsset: @see attachmentFromCameraImage: @param url NSURL object @return An instance attachment object */ + (instancetype)attachmentFromDocumentURL:(NSURL *)url; /*! @brief Load thumbnail image for specified size. Resultant image size can differ from the specified size. @see loadOriginalImageWithCompletion: @param targetSize The desired image size @param completion result block */ - (void)loadThumbnailImageWithTargetSize:(CGSize)targetSize completion:(void(^)(UIImage * _Nullable resultImage))completion; /*! @brief Load original image @see loadThumbnailImageWithTargetSize:completion: @param completion result block */ - (void)loadOriginalImageWithCompletion:(void(^)(UIImage * _Nullable resultImage))completion; /*! @brief Get original resource @return Can be return PHAsset, UIImage or local file path (NSString) */ - (id)originalFileResource; NS_ASSUME_NONNULL_END @end ================================================ FILE: Source/Models/DBAttachment.m ================================================ // // DBAttachment.m // DBAttachmentPickerController // // Created by Denis Bogatyrev on 14.03.16. // // The MIT License (MIT) // Copyright (c) 2016 Denis Bogatyrev. // // Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), // to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, // and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS // IN THE SOFTWARE. // @import Photos; @import CoreLocation; #import "DBAttachment.h" #import "UIImage+DBAssetIcons.h" @interface DBAttachment () @property (strong, nonatomic) NSString *fileName; @property (assign, nonatomic) NSUInteger fileSize; @property (strong, nonatomic) NSDate *creationDate; @property (assign, nonatomic) DBAttachmentSourceType sourceType; @property (assign, nonatomic) DBAttachmentMediaType mediaType; @property (strong, nonatomic) PHAsset *photoAsset; @property (strong, nonatomic) UIImage *image; @property (strong, nonatomic) UIImage *thumbmailImage; @property (strong, nonatomic) NSString *originalFilePath; @property (strong, nonatomic) NSString *restorationIdentifier; @end @implementation DBAttachment + (instancetype)attachmentFromPHAsset:(PHAsset *)asset { DBAttachment *model = [[[self class] alloc] init]; model.sourceType = DBAttachmentSourceTypePHAsset; model.photoAsset = asset; NSArray *resources = [PHAssetResource assetResourcesForAsset:asset]; PHAssetResource *resource = [resources firstObject]; switch (asset.mediaType) { case PHAssetMediaTypeImage: model.mediaType = DBAttachmentMediaTypeImage; break; case PHAssetMediaTypeVideo: model.mediaType = DBAttachmentMediaTypeVideo; break; default: model.mediaType = DBAttachmentMediaTypeOther; break; } model.fileName = resource.originalFilename; model.creationDate = asset.creationDate; return model; } + (instancetype)attachmentFromCameraImage:(UIImage *)image { DBAttachment *model = [[[self class] alloc] init]; model.sourceType = DBAttachmentSourceTypeImage; model.mediaType = DBAttachmentMediaTypeImage; model.image = image; NSData *imgData = UIImageJPEGRepresentation(image, 1); model.fileSize = imgData.length; model.creationDate = [NSDate date]; model.fileName = @"capturedimage.jpg"; return model; } + (instancetype)attachmentFromDocumentURL:(NSURL *)url { DBAttachment *model = [[[self class] alloc] init]; model.sourceType = DBAttachmentSourceTypeDocumentURL; NSString *fileTmpPath = [url path]; if ( [[NSFileManager defaultManager] fileExistsAtPath:fileTmpPath] ) { model.fileName = [fileTmpPath lastPathComponent]; NSString *cacheFolderPath = [[model cacheFolderPath] stringByAppendingPathComponent:model.restorationIdentifier]; NSError *error; if ( ![[NSFileManager defaultManager] fileExistsAtPath:cacheFolderPath] ) { [[NSFileManager defaultManager] createDirectoryAtPath:cacheFolderPath withIntermediateDirectories:YES attributes:nil error:&error]; } NSDictionary *attributes = [[NSFileManager defaultManager] attributesOfItemAtPath:fileTmpPath error:nil]; model.creationDate = attributes[NSFileCreationDate]; model.fileSize = [attributes[NSFileSize] integerValue]; model.originalFilePath = [cacheFolderPath stringByAppendingPathComponent:model.fileName]; [[NSFileManager defaultManager] copyItemAtPath:fileTmpPath toPath:model.originalFilePath error:&error]; } NSString *fileExt = [[[url absoluteString] pathExtension] lowercaseString]; if ( [fileExt isEqualToString:@"png"] || [fileExt isEqualToString:@"jpeg"] || [fileExt isEqualToString:@"jpg"] || [fileExt isEqualToString:@"gif"] || [fileExt isEqualToString:@"tiff"]) { model.mediaType = DBAttachmentMediaTypeImage; model.thumbmailImage = [UIImage imageWithContentsOfFile:model.originalFilePath]; } else if ( [fileExt isEqualToString:@"mov"] || [fileExt isEqualToString:@"avi"]) { model.mediaType = DBAttachmentMediaTypeVideo; model.thumbmailImage = [model generateThumbnailImageFromURL:url]; } else { model.mediaType = DBAttachmentMediaTypeOther; model.thumbmailImage = [UIImage imageOfFileIconWithExtensionText:[fileExt uppercaseString]]; } return model; } #pragma mark Helpers - (NSString *)cacheFolderPath { NSString *documentsDirectory = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject]; return [documentsDirectory stringByAppendingPathComponent:@"DBAttachmentPickerControllerCache"]; } #pragma mark - Lifecycle - (instancetype)init { self = [super init]; if (self) { self.restorationIdentifier = [NSUUID UUID].UUIDString; } return self; } - (void)dealloc { NSString *cacheFolderPath = [[self cacheFolderPath] stringByAppendingPathComponent:self.restorationIdentifier]; if ( [[NSFileManager defaultManager] fileExistsAtPath:cacheFolderPath] ) { NSError *error; [[NSFileManager defaultManager] removeItemAtPath:cacheFolderPath error:&error]; } } #pragma mark - Accessors - (NSString *)fileSizeStr { if (self.fileSize == 0) return nil; return [NSByteCountFormatter stringFromByteCount:self.fileSize countStyle:NSByteCountFormatterCountStyleFile]; } #pragma mark - - (void)loadThumbnailImageWithTargetSize:(CGSize)targetSize completion:(void(^)(UIImage *resultImage))completion { switch (self.sourceType) { case DBAttachmentSourceTypePHAsset: if (completion) { [[PHImageManager defaultManager] requestImageForAsset:self.photoAsset targetSize:targetSize contentMode:PHImageContentModeDefault options:nil resultHandler:^(UIImage *result, NSDictionary *info) { if (![info[PHImageResultIsDegradedKey] boolValue]) { completion(result); } }]; } break; case DBAttachmentSourceTypeDocumentURL: { if (self.thumbmailImage) { completion(self.thumbmailImage); } else { [self loadOriginalImageWithCompletion:completion]; } } break; default: [self loadOriginalImageWithCompletion:completion]; break; } } - (void)loadOriginalImageWithCompletion:(void(^)(UIImage *resultImage))completion { switch (self.sourceType) { case DBAttachmentSourceTypePHAsset: if (completion) { [[PHImageManager defaultManager] requestImageForAsset:self.photoAsset targetSize:PHImageManagerMaximumSize contentMode:PHImageContentModeDefault options:nil resultHandler:^(UIImage *result, NSDictionary *info) { completion(result); }]; } break; case DBAttachmentSourceTypeImage: { if (completion) { completion(self.image); } break; } case DBAttachmentSourceTypeDocumentURL: { if (!self.image) { self.image = [UIImage imageWithContentsOfFile:self.originalFilePath]; } if (completion) { completion(self.image); } break; } default: if (completion) { completion(nil); } break; } } - (id)originalFileResource { switch (self.sourceType) { case DBAttachmentSourceTypePHAsset: return self.photoAsset; break; case DBAttachmentSourceTypeImage: return self.image; break; case DBAttachmentSourceTypeDocumentURL: return self.originalFilePath; break; default: return nil; break; } } #pragma mark Helpers - (UIImage *)generateThumbnailImageFromURL:(NSURL *)url { AVAsset *asset = [AVAsset assetWithURL:url]; AVAssetImageGenerator *imageGenerator = [[AVAssetImageGenerator alloc] initWithAsset:asset]; imageGenerator.appliesPreferredTrackTransform = YES; CMTime time = [asset duration]; time.value = 0; CGImageRef imageRef = [imageGenerator copyCGImageAtTime:time actualTime:NULL error:NULL]; UIImage *thumbnail = [UIImage imageWithCGImage:imageRef]; CGImageRelease(imageRef); return thumbnail; } @end