Repository: northwind/FlyImage Branch: master Commit: 08d2f5ccced4 Files: 96 Total size: 315.8 KB Directory structure: gitextract_2t9t0g8x/ ├── .gitignore ├── .swift-version ├── .travis.yml ├── Examples/ │ ├── FlyImageIconView/ │ │ ├── AppDelegate.h │ │ ├── AppDelegate.m │ │ ├── Info.plist │ │ └── main.m │ ├── FlyImageView/ │ │ ├── AppDelegate.h │ │ ├── AppDelegate.m │ │ ├── Cells/ │ │ │ ├── BaseTableViewCell.h │ │ │ ├── BaseTableViewCell.m │ │ │ ├── FlyImageIconLayerTableViewCell.h │ │ │ ├── FlyImageIconLayerTableViewCell.m │ │ │ ├── FlyImageIconViewTableViewCell.h │ │ │ ├── FlyImageIconViewTableViewCell.m │ │ │ ├── FlyImageLayerTableViewCell.h │ │ │ ├── FlyImageLayerTableViewCell.m │ │ │ ├── FlyImageTableViewCell.h │ │ │ ├── FlyImageTableViewCell.m │ │ │ ├── SDWebImageTableViewCell.h │ │ │ ├── SDWebImageTableViewCell.m │ │ │ ├── TriditionTableViewCell.h │ │ │ └── TriditionTableViewCell.m │ │ ├── Info.plist │ │ ├── Launch Screen.storyboard │ │ ├── ProgressImageView.h │ │ ├── ProgressImageView.m │ │ ├── RootViewController.h │ │ ├── RootViewController.m │ │ └── main.m │ ├── FlyImageView.xcodeproj/ │ │ └── project.pbxproj │ ├── FlyImageView.xcworkspace/ │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata/ │ │ └── xcschemes/ │ │ ├── FlyImageIconView.xcscheme │ │ ├── FlyImageView.xcscheme │ │ ├── SingleView.xcscheme │ │ └── WebP.xcscheme │ ├── Podfile │ ├── SingleView/ │ │ ├── AppDelegate.h │ │ ├── AppDelegate.m │ │ ├── Info.plist │ │ ├── SingleViewController.h │ │ ├── SingleViewController.m │ │ └── main.m │ └── WebP/ │ ├── AppDelegate.h │ ├── AppDelegate.m │ ├── Info.plist │ └── main.m ├── FlyImage/ │ ├── Core/ │ │ ├── FlyImageCache.h │ │ ├── FlyImageCache.m │ │ ├── FlyImageCacheProtocol.h │ │ ├── FlyImageDataFile.h │ │ ├── FlyImageDataFile.m │ │ ├── FlyImageDataFileManager.h │ │ ├── FlyImageDataFileManager.m │ │ ├── FlyImageDecoder.h │ │ ├── FlyImageDecoder.m │ │ ├── FlyImageDownloader.h │ │ ├── FlyImageDownloader.m │ │ ├── FlyImageEncoder.h │ │ ├── FlyImageEncoder.m │ │ ├── FlyImageIconCache.h │ │ ├── FlyImageIconCache.m │ │ ├── FlyImageRetrieveOperation.h │ │ ├── FlyImageRetrieveOperation.m │ │ ├── FlyImageUtils.h │ │ └── FlyImageUtils.m │ ├── FlyImage.h │ ├── Info.plist │ └── UI/ │ ├── CALayer+FlyImageCache.h │ ├── CALayer+FlyImageCache.m │ ├── CALayer+FlyImageIconCache.h │ ├── CALayer+FlyImageIconCache.m │ ├── FlyImageCacheUIProtocol.h │ ├── FlyImageIconCacheUIProtocol.h │ ├── FlyImageIconRenderer.h │ ├── FlyImageIconRenderer.m │ ├── FlyImageRenderer.h │ ├── FlyImageRenderer.m │ ├── UIImageView+FlyImageCache.h │ ├── UIImageView+FlyImageCache.m │ ├── UIImageView+FlyImageIconCache.h │ └── UIImageView+FlyImageIconCache.m ├── FlyImage.podspec ├── FlyImage.xcodeproj/ │ └── project.pbxproj ├── FlyImage.xcworkspace/ │ ├── contents.xcworkspacedata │ └── xcshareddata/ │ └── xcschemes/ │ ├── FlyImage.xcscheme │ └── FlyImageTests.xcscheme ├── FlyImageTests/ │ ├── FlyImageCacheTests.m │ ├── FlyImageDataFileManagerTests.m │ ├── FlyImageDataFileTests.m │ ├── FlyImageDownloadManagerTests.m │ ├── FlyImageIconCacheTests.m │ └── Info.plist ├── LICENSE ├── Podfile └── README.md ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ # Xcode # build/ *.pbxuser !default.pbxuser *.mode1v3 !default.mode1v3 *.mode2v3 !default.mode2v3 *.perspectivev3 !default.perspectivev3 project.xcworkspace !default.xcworkspace xcuserdata *.xccheckout *.moved-aside DerivedData *.hmap *.ipa *.xcuserstate *.xcscmblueprint # CocoaPods # # We recommend against adding the Pods directory to your .gitignore. However # you should judge for yourself, the pros and cons are mentioned at: # http://guides.cocoapods.org/using/using-cocoapods.html#should-i-ignore-the-pods-directory-in-source-control # Pods/ Podfile.lock ================================================ FILE: .swift-version ================================================ 2.3 ================================================ FILE: .travis.yml ================================================ language: objective-c cache: - bundler - cocoapods osx_image: xcode7.1 before_install: - export LANG=en_US.UTF-8 - env - locale - gem install cocoapods --no-rdoc --no-ri --no-document --quiet - pod --version - pod setup --silent > /dev/null - pod repo update --silent script: - pod lib lint --allow-warnings - xctool -workspace FlyImage.xcworkspace -scheme 'FlyImage' -sdk iphonesimulator -arch i386 build - pod install --project-directory=Examples - xctool -workspace './Examples/FlyImageView.xcworkspace' -scheme 'FlyImageView' -sdk iphonesimulator -arch i386 build - xctool -workspace './Examples/FlyImageView.xcworkspace' -scheme 'FlyImageIconView' -sdk iphonesimulator -arch i386 build - xctool -workspace './Examples/FlyImageView.xcworkspace' -scheme 'WebP' -sdk iphonesimulator -arch i386 build - xctool -workspace './Examples/FlyImageView.xcworkspace' -scheme 'SingleView' -sdk iphonesimulator -arch i386 build - xcodebuild -workspace FlyImage.xcworkspace -scheme 'FlyImageTests' -sdk iphonesimulator -destination 'platform=iOS Simulator,name=iPhone 6,OS=latest' test ================================================ FILE: Examples/FlyImageIconView/AppDelegate.h ================================================ // // AppDelegate.h // FlyImageIconView // // Created by Ye Tong on 4/27/16. // Copyright © 2016 Augmn. All rights reserved. // #import @interface AppDelegate : UIResponder @property (strong, nonatomic) UIWindow *window; @end ================================================ FILE: Examples/FlyImageIconView/AppDelegate.m ================================================ // // AppDelegate.m // FlyImageIconView // // Created by Ye Tong on 4/27/16. // Copyright © 2016 Augmn. All rights reserved. // #import "AppDelegate.h" #import "RootViewController.h" #import "TriditionTableViewCell.h" #import "SDWebImageTableViewCell.h" #import "FlyImageIconLayerTableViewCell.h" #import "FlyImageIconViewTableViewCell.h" #import "FlyImage.h" @interface AppDelegate () @end @implementation AppDelegate - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds]; self.window.backgroundColor = [UIColor whiteColor]; RootViewController *rootViewController = [[RootViewController alloc] init]; rootViewController.suffix = @"_tn.jpg"; rootViewController.cellsPerRow = 10; rootViewController.heightOfCell = self.window.bounds.size.width / rootViewController.cellsPerRow; rootViewController.activeIndex = 3; rootViewController.cells = @[ @{ @"class": [TriditionTableViewCell class], @"title": @"UIKit" },@{ @"class": [SDWebImageTableViewCell class], @"title": @"SDWebImage" }, @{ @"class": [FlyImageIconViewTableViewCell class], @"title": @"FlyImageIconView" }, @{ @"class": [FlyImageIconLayerTableViewCell class], @"title": @"FlyImageIconLayer" }]; self.window.rootViewController = rootViewController; [self.window makeKeyAndVisible]; 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: Examples/FlyImageIconView/Info.plist ================================================ CFBundleDevelopmentRegion en CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIdentifier $(PRODUCT_BUNDLE_IDENTIFIER) CFBundleInfoDictionaryVersion 6.0 CFBundleName $(PRODUCT_NAME) CFBundlePackageType APPL CFBundleShortVersionString 1.0 CFBundleSignature ???? CFBundleVersion 1 LSRequiresIPhoneOS NSAppTransportSecurity NSAllowsArbitraryLoads UILaunchStoryboardName Launch Screen UIRequiredDeviceCapabilities armv7 UIRequiresFullScreen UIStatusBarHidden UISupportedInterfaceOrientations UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UISupportedInterfaceOrientations~ipad UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight ================================================ FILE: Examples/FlyImageIconView/main.m ================================================ // // main.m // FlyImageIconView // // Created by Ye Tong on 4/27/16. // Copyright © 2016 Augmn. All rights reserved. // #import #import "AppDelegate.h" int main(int argc, char * argv[]) { @autoreleasepool { return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); } } ================================================ FILE: Examples/FlyImageView/AppDelegate.h ================================================ // // AppDelegate.h // FlyImageView // // Created by Ye Tong on 4/18/16. // Copyright © 2016 Augmn. All rights reserved. // #import @interface AppDelegate : UIResponder @property (strong, nonatomic) UIWindow *window; @end ================================================ FILE: Examples/FlyImageView/AppDelegate.m ================================================ // // AppDelegate.m // FlyImageView // // Created by Ye Tong on 4/18/16. // Copyright © 2016 Augmn. All rights reserved. // #import "AppDelegate.h" #import "RootViewController.h" #import "TriditionTableViewCell.h" #import "FlyImageTableViewCell.h" #import "FlyImageLayerTableViewCell.h" #import "SDWebImageTableViewCell.h" #import "FlyImage.h" @interface AppDelegate () @end @implementation AppDelegate - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds]; self.window.backgroundColor = [UIColor whiteColor]; [FlyImageCache sharedInstance].autoDismissImage = YES; RootViewController *rootViewController = [[RootViewController alloc] init]; rootViewController.suffix = @".jpg"; rootViewController.heightOfCell = 150; rootViewController.cellsPerRow = 1; rootViewController.activeIndex = 3; rootViewController.cells = @[ @{ @"class": [TriditionTableViewCell class], @"title": @"UIKit" },@{ @"class": [SDWebImageTableViewCell class], @"title": @"SDWebImage" } ,@{ @"class": [FlyImageTableViewCell class], @"title": @"FlyImageView" } ,@{ @"class": [FlyImageLayerTableViewCell class], @"title": @"FlyImageLayer" }]; self.window.rootViewController = rootViewController; [self.window makeKeyAndVisible]; 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:. } - (void)applicationDidReceiveMemoryWarning:(UIApplication *)application { NSLog(@"Memory Warning"); } @end ================================================ FILE: Examples/FlyImageView/Cells/BaseTableViewCell.h ================================================ // // BaseTableViewCell.h // Demo // // Created by Norris Tong on 4/15/16. // Copyright © 2016 NorrisTong. All rights reserved. // #import @interface BaseTableViewCell : UITableViewCell - (void)displayImageWithPhotos:(NSArray *)photos; - (id)imageViewWithFrame:(CGRect)frame; - (void)renderImageView:(id)imageView url:(NSURL *)url; @end ================================================ FILE: Examples/FlyImageView/Cells/BaseTableViewCell.m ================================================ // // BaseTableViewCell.m // Demo // // Created by Norris Tong on 4/15/16. // Copyright © 2016 NorrisTong. All rights reserved. // #import "BaseTableViewCell.h" @implementation BaseTableViewCell { NSMutableArray *_imageViews; NSArray *_photos; } - (void)displayImageWithPhotos:(NSArray *)photos { if ( _imageViews == nil ) { NSInteger photoCount = [photos count]; _imageViews = [[NSMutableArray alloc] initWithCapacity:photoCount]; CGRect frame = self.frame; CGFloat itemWidth = floor(frame.size.width / photoCount); CGFloat padding = 2; for (int i=0; i #import @interface NSString (Extension) - (NSString *)md5; @end @implementation NSString (Extension) - (NSString *)md5 { const char *cStr = [self UTF8String]; unsigned char result[CC_MD5_DIGEST_LENGTH]; CC_MD5(cStr, (CC_LONG)strlen(cStr), result); // This is the md5 call return [NSString stringWithFormat:@"%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x", result[0], result[1], result[2], result[3], result[4], result[5], result[6], result[7], result[8], result[9], result[10], result[11], result[12], result[13], result[14], result[15]]; } @end @implementation TriditionTableViewCell - (id)imageViewWithFrame:(CGRect)frame { UIImageView *imageView = [[UIImageView alloc] initWithFrame:frame]; imageView.contentMode = UIViewContentModeScaleAspectFill; imageView.layer.masksToBounds = YES; imageView.layer.cornerRadius = 10; [self addSubview:imageView]; return imageView; } - (void)renderImageView:(id)imageView url:(NSURL *)url { NSString *key = url.absoluteString; if ( [[FlyImageCache sharedInstance] isImageExistWithKey:key] ) { NSString *path = [[FlyImageCache sharedInstance] imagePathWithKey:key]; NSURL *url = [NSURL fileURLWithPath:path]; [self doRenderImageView:imageView url:url]; }else{ NSURLRequest *request = [NSURLRequest requestWithURL:url]; [[FlyImageDownloader sharedInstance] downloadImageForURLRequest:request progress:nil success:^(NSURLRequest *request, NSURL *filePath) { [[FlyImageCache sharedInstance] addImageWithKey:key filename:[filePath lastPathComponent] completed:^(NSString *key, UIImage *image) { ((UIImageView *)imageView).image = image; }]; } failed:^(NSURLRequest *request, NSError *error) { NSLog(@"occur error = %@", error ); }]; ((UIImageView *)imageView).image = nil; } } - (void)doRenderImageView:(UIImageView *)imageView url:(NSURL *)url { NSData *data = [NSData dataWithContentsOfURL:url]; UIImage *image = [UIImage imageWithData:data]; imageView.image = image; } @end ================================================ FILE: Examples/FlyImageView/Info.plist ================================================ CFBundleDevelopmentRegion en CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIdentifier $(PRODUCT_BUNDLE_IDENTIFIER) CFBundleInfoDictionaryVersion 6.0 CFBundleName $(PRODUCT_NAME) CFBundlePackageType APPL CFBundleShortVersionString 1.0 CFBundleSignature ???? CFBundleVersion 1 LSRequiresIPhoneOS NSAppTransportSecurity NSAllowsArbitraryLoads UILaunchStoryboardName Launch Screen UIRequiredDeviceCapabilities armv7 UIRequiresFullScreen UIStatusBarHidden UISupportedInterfaceOrientations UIInterfaceOrientationPortrait UISupportedInterfaceOrientations~ipad UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight ================================================ FILE: Examples/FlyImageView/Launch Screen.storyboard ================================================ ================================================ FILE: Examples/FlyImageView/ProgressImageView.h ================================================ // // ProgressImageView.h // FlyImageView // // Created by Ye Tong on 8/12/16. // Copyright © 2016 Augmn. All rights reserved. // #import @interface ProgressImageView : UIImageView @end ================================================ FILE: Examples/FlyImageView/ProgressImageView.m ================================================ // // ProgressImageView.m // FlyImageView // // Created by Ye Tong on 8/12/16. // Copyright © 2016 Augmn. All rights reserved. // #import "ProgressImageView.h" #import "FlyImage.h" @implementation ProgressImageView - (instancetype)initWithFrame:(CGRect)frame { if ( self = [super initWithFrame:frame] ) { [self addObserver:self forKeyPath:@"downloadingPercentage" options:NSKeyValueObservingOptionNew context:nil]; } return self; } - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { if ([keyPath isEqualToString:@"downloadingPercentage"]) { NSLog(@"downloadingURL : %@", self.downloadingURL ); NSLog(@"downloadingPercentage : %f", self.downloadingPercentage ); } } @end ================================================ FILE: Examples/FlyImageView/RootViewController.h ================================================ // // RootViewController.h // Demo // // Created by Ye Tong on 3/24/16. // Copyright © 2016 NorrisTong. All rights reserved. // #import @interface RootViewController : UIViewController @property (nonatomic, strong) NSArray *cells; @property (nonatomic, assign) CGFloat heightOfCell; @property (nonatomic, assign) NSInteger cellsPerRow; @property (nonatomic, assign) NSInteger activeIndex; @property (nonatomic, copy) NSString *suffix; @end ================================================ FILE: Examples/FlyImageView/RootViewController.m ================================================ // // RootViewController.m // Demo // // Created by Ye Tong on 3/24/16. // Copyright © 2016 NorrisTong. All rights reserved. // #import "RootViewController.h" #import "BaseTableViewCell.h" #import "SDImageCache.h" #import @interface RootViewController () @property (nonatomic, assign) CGFloat itemWidth; @property (nonatomic, assign) CGFloat itemHeight; @end @implementation RootViewController { UITableView *_tableView; UISegmentedControl *_segment; NSMutableArray *_imageURLs; NSMutableArray *_cells; NSMutableArray *_indentifiers; } - (instancetype)init { if (self = [super init]) { _cells = [[NSMutableArray alloc] init]; _indentifiers = [[NSMutableArray alloc] init]; _activeIndex = 0; } return self; } - (void)viewDidLoad { [super viewDidLoad]; self.view.backgroundColor = [UIColor whiteColor]; // setup image paths _imageURLs = [[NSMutableArray alloc] init]; for (int i=0; i<100; i++) { NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:@"https://flyimage.oss-us-west-1.aliyuncs.com/%d%@", i, self.suffix ]]; [_imageURLs addObject:url]; } CGFloat segmentHeight = 30; CGRect bounds = self.view.bounds; _tableView = [[UITableView alloc] initWithFrame:CGRectMake(0, 0, bounds.size.width, bounds.size.height-segmentHeight) style:UITableViewStylePlain]; _tableView.opaque = YES; _tableView.directionalLockEnabled = YES; _tableView.backgroundColor = [UIColor clearColor]; _tableView.allowsSelection = NO; _tableView.separatorStyle = UITableViewCellSeparatorStyleNone; _tableView.dataSource = self; _tableView.delegate = self; NSMutableArray *items = [NSMutableArray array]; for (NSDictionary *info in _cells) { [items addObject: [info objectForKey:@"title"]]; Class class = [info objectForKey:@"class"]; NSString *indentifier = NSStringFromClass(class); [_indentifiers addObject:indentifier]; [_tableView registerClass:class forCellReuseIdentifier:indentifier]; } [self.view addSubview:_tableView]; NSDictionary *textAttributes = [NSDictionary dictionaryWithObjectsAndKeys:[UIFont systemFontOfSize:10], NSFontAttributeName, nil]; [[UISegmentedControl appearance] setTitleTextAttributes:textAttributes forState:UIControlStateNormal]; _segment = [[UISegmentedControl alloc] initWithItems:items]; _segment.backgroundColor = [UIColor whiteColor]; _segment.frame = CGRectMake(0, bounds.size.height - segmentHeight, bounds.size.width, segmentHeight); [_segment setSelectedSegmentIndex:_activeIndex]; [_segment addTarget:self action:@selector(onTapSegment) forControlEvents:UIControlEventValueChanged]; [self.view addSubview:_segment]; SDWebImageManager *sdManager = [SDWebImageManager sharedManager]; sdManager.delegate = self; _itemWidth = floor(self.view.frame.size.width / _cellsPerRow) - 4; _itemHeight = _heightOfCell - 4; } #pragma mark - SDWebImageManagerDelegate - (UIImage *)imageManager:(SDWebImageManager *)imageManager transformDownloadedImage:(UIImage *)image withURL:(NSURL *)imageURL{ UIGraphicsBeginImageContextWithOptions(CGSizeMake(_itemWidth, _heightOfCell), NO, [UIScreen mainScreen].scale); CGRect box = CGRectMake(0, 0, _itemWidth, _itemHeight); [[UIBezierPath bezierPathWithRoundedRect:box cornerRadius:10.f] addClip]; [image drawInRect:box]; UIImage* ret = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); return ret; } - (BOOL)prefersStatusBarHidden { return YES; } - (void)onTapSegment { _activeIndex = _segment.selectedSegmentIndex; [_tableView reloadData]; } #pragma mark - UITableViewDataSource - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { return 2000; } - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { return self.heightOfCell; } // The cell that is returned must be retrieved from a call to -dequeueReusableCellWithReuseIdentifier:forIndexPath: - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { NSInteger startIndex = ([indexPath row] * self.cellsPerRow) % [_imageURLs count]; NSInteger count = MIN(self.cellsPerRow, [_imageURLs count] - startIndex); NSArray *photos = [_imageURLs subarrayWithRange:NSMakeRange(startIndex, count)]; BaseTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:_indentifiers[_activeIndex] forIndexPath:indexPath]; [cell displayImageWithPhotos:photos]; return cell; } @end ================================================ FILE: Examples/FlyImageView/main.m ================================================ // // main.m // FlyImageView // // Created by Ye Tong on 4/18/16. // Copyright © 2016 Augmn. All rights reserved. // #import #import "AppDelegate.h" int main(int argc, char * argv[]) { @autoreleasepool { return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); } } ================================================ FILE: Examples/FlyImageView.xcodeproj/project.pbxproj ================================================ // !$*UTF8*$! { archiveVersion = 1; classes = { }; objectVersion = 46; objects = { /* Begin PBXBuildFile section */ 7539169AF2FD0C6CD1ED1213 /* libPods-FlyImageIconView.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5F9C3948F7DFDB6E2FE5F0B0 /* libPods-FlyImageIconView.a */; }; 8E45DB317D60C68FC813142D /* libPods-FlyImageView.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 9DCBFE3EE450E44921AFAF53 /* libPods-FlyImageView.a */; }; 977F44737DA3E198C81BF6F4 /* libPods-SingleView.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 0280F6E421151FBFAE698C23 /* libPods-SingleView.a */; }; CEE17C7A1B5057E6682927EF /* libPods-WebP.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 4EC1098E069AA6CA893D0F2C /* libPods-WebP.a */; }; DD2EDB131D61E6C30085C9E3 /* ProgressImageView.m in Sources */ = {isa = PBXBuildFile; fileRef = DD8E088D1D5DD5BD00FEBC3A /* ProgressImageView.m */; }; DD8E088E1D5DD5BD00FEBC3A /* ProgressImageView.m in Sources */ = {isa = PBXBuildFile; fileRef = DD8E088D1D5DD5BD00FEBC3A /* ProgressImageView.m */; }; DD9D97481CC5044D008F279E /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = DD9D97471CC5044D008F279E /* main.m */; }; DD9D974B1CC5044D008F279E /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = DD9D974A1CC5044D008F279E /* AppDelegate.m */; }; DDBECFCD1CD8C0FA00EC9263 /* FlyImageIconViewTableViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = DDBECFCC1CD8C0FA00EC9263 /* FlyImageIconViewTableViewCell.m */; }; DDBED17A1CD8C5D600EC9263 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = DDBED1791CD8C5D600EC9263 /* main.m */; }; DDBED17D1CD8C5D600EC9263 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = DDBED17C1CD8C5D600EC9263 /* AppDelegate.m */; }; DDBED19B1CD8C70C00EC9263 /* RootViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = DDD1F1381CD0831F00EEC549 /* RootViewController.m */; }; DDBED19C1CD8C71100EC9263 /* BaseTableViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = DDD1F13C1CD0832400EEC549 /* BaseTableViewCell.m */; }; DDBED19D1CD8C71900EC9263 /* TriditionTableViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = DDD1F1481CD0832400EEC549 /* TriditionTableViewCell.m */; }; DDBED19E1CD8C71B00EC9263 /* SDWebImageTableViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = DDD1F1461CD0832400EEC549 /* SDWebImageTableViewCell.m */; }; DDBED19F1CD8C72A00EC9263 /* FlyImageTableViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = DDD1F1441CD0832400EEC549 /* FlyImageTableViewCell.m */; }; DDBED1A01CD8C72E00EC9263 /* FlyImageLayerTableViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = DDD1F1421CD0832400EEC549 /* FlyImageLayerTableViewCell.m */; }; DDBED1A11CD8C76700EC9263 /* Default-568h@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = DDD1F1501CD083B300EEC549 /* Default-568h@2x.png */; }; DDD1F1391CD0831F00EEC549 /* RootViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = DDD1F1381CD0831F00EEC549 /* RootViewController.m */; }; DDD1F1491CD0832400EEC549 /* BaseTableViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = DDD1F13C1CD0832400EEC549 /* BaseTableViewCell.m */; }; DDD1F14C1CD0832400EEC549 /* FlyImageLayerTableViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = DDD1F1421CD0832400EEC549 /* FlyImageLayerTableViewCell.m */; }; DDD1F14D1CD0832400EEC549 /* FlyImageTableViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = DDD1F1441CD0832400EEC549 /* FlyImageTableViewCell.m */; }; DDD1F14E1CD0832400EEC549 /* SDWebImageTableViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = DDD1F1461CD0832400EEC549 /* SDWebImageTableViewCell.m */; }; DDD1F14F1CD0832400EEC549 /* TriditionTableViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = DDD1F1481CD0832400EEC549 /* TriditionTableViewCell.m */; }; DDD1F1511CD083B300EEC549 /* Default-568h@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = DDD1F1501CD083B300EEC549 /* Default-568h@2x.png */; }; DDD1F15A1CD084E500EEC549 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = DDD1F1591CD084E500EEC549 /* main.m */; }; DDD1F15D1CD084E500EEC549 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = DDD1F15C1CD084E500EEC549 /* AppDelegate.m */; }; DDD1F18F1CD0872D00EEC549 /* BaseTableViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = DDD1F13C1CD0832400EEC549 /* BaseTableViewCell.m */; }; DDD1F1911CD0872D00EEC549 /* SDWebImageTableViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = DDD1F1461CD0832400EEC549 /* SDWebImageTableViewCell.m */; }; DDD1F1931CD0872D00EEC549 /* TriditionTableViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = DDD1F1481CD0832400EEC549 /* TriditionTableViewCell.m */; }; DDD1F1941CD08D1E00EEC549 /* RootViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = DDD1F1381CD0831F00EEC549 /* RootViewController.m */; }; DDD1F1951CD08E7D00EEC549 /* Default-568h@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = DDD1F1501CD083B300EEC549 /* Default-568h@2x.png */; }; DDD1F1A01CD0B92F00EEC549 /* FlyImageIconLayerTableViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = DDD1F19F1CD0B92F00EEC549 /* FlyImageIconLayerTableViewCell.m */; }; DDD8FB601D59EB9200821392 /* Launch Screen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = DDD8FB5F1D59EB9200821392 /* Launch Screen.storyboard */; }; DDD8FB611D59EB9200821392 /* Launch Screen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = DDD8FB5F1D59EB9200821392 /* Launch Screen.storyboard */; }; DDD8FB621D59EB9200821392 /* Launch Screen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = DDD8FB5F1D59EB9200821392 /* Launch Screen.storyboard */; }; DDD8FB631D59EB9200821392 /* Launch Screen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = DDD8FB5F1D59EB9200821392 /* Launch Screen.storyboard */; }; DDDEF4321CE0AF4600C8366C /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = DDDEF4311CE0AF4600C8366C /* main.m */; }; DDDEF4351CE0AF4600C8366C /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = DDDEF4341CE0AF4600C8366C /* AppDelegate.m */; }; DDDEF4381CE0AF4600C8366C /* SingleViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = DDDEF4371CE0AF4600C8366C /* SingleViewController.m */; }; DDDEF4451CE0B8AA00C8366C /* Default-568h@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = DDD1F1501CD083B300EEC549 /* Default-568h@2x.png */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ 0280F6E421151FBFAE698C23 /* libPods-SingleView.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-SingleView.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 02F5D49EB862BEC96204662A /* Pods-FlyImageIconView.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-FlyImageIconView.release.xcconfig"; path = "Pods/Target Support Files/Pods-FlyImageIconView/Pods-FlyImageIconView.release.xcconfig"; sourceTree = ""; }; 33A4E8609F3283491E3DD133 /* Pods-SingleView.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SingleView.release.xcconfig"; path = "Pods/Target Support Files/Pods-SingleView/Pods-SingleView.release.xcconfig"; sourceTree = ""; }; 3ABB5A3F814E3BFC95A513BD /* Pods-FlyImageView.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-FlyImageView.release.xcconfig"; path = "Pods/Target Support Files/Pods-FlyImageView/Pods-FlyImageView.release.xcconfig"; sourceTree = ""; }; 4EC1098E069AA6CA893D0F2C /* libPods-WebP.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-WebP.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 5F9C3948F7DFDB6E2FE5F0B0 /* libPods-FlyImageIconView.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-FlyImageIconView.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 7D3083C3FEEB3316F9CCCF92 /* Pods-FlyImageIconView.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-FlyImageIconView.debug.xcconfig"; path = "Pods/Target Support Files/Pods-FlyImageIconView/Pods-FlyImageIconView.debug.xcconfig"; sourceTree = ""; }; 841CA4D45E6400A9646A0F66 /* Pods-FlyImageView.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-FlyImageView.debug.xcconfig"; path = "Pods/Target Support Files/Pods-FlyImageView/Pods-FlyImageView.debug.xcconfig"; sourceTree = ""; }; 9DCBFE3EE450E44921AFAF53 /* libPods-FlyImageView.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-FlyImageView.a"; sourceTree = BUILT_PRODUCTS_DIR; }; D95E1058D32716446EB3DC84 /* Pods-WebP.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-WebP.debug.xcconfig"; path = "Pods/Target Support Files/Pods-WebP/Pods-WebP.debug.xcconfig"; sourceTree = ""; }; DD8E088C1D5DD5BD00FEBC3A /* ProgressImageView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ProgressImageView.h; path = ../ProgressImageView.h; sourceTree = ""; }; DD8E088D1D5DD5BD00FEBC3A /* ProgressImageView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = ProgressImageView.m; path = ../ProgressImageView.m; sourceTree = ""; }; DD9D97431CC5044D008F279E /* FlyImageView.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = FlyImageView.app; sourceTree = BUILT_PRODUCTS_DIR; }; DD9D97471CC5044D008F279E /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; DD9D97491CC5044D008F279E /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; DD9D974A1CC5044D008F279E /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; DD9D97571CC5044D008F279E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; DDBECFCB1CD8C0FA00EC9263 /* FlyImageIconViewTableViewCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FlyImageIconViewTableViewCell.h; sourceTree = ""; }; DDBECFCC1CD8C0FA00EC9263 /* FlyImageIconViewTableViewCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FlyImageIconViewTableViewCell.m; sourceTree = ""; }; DDBED1761CD8C5D600EC9263 /* WebP.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = WebP.app; sourceTree = BUILT_PRODUCTS_DIR; }; DDBED1791CD8C5D600EC9263 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; DDBED17B1CD8C5D600EC9263 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; DDBED17C1CD8C5D600EC9263 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; DDBED1891CD8C5D600EC9263 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; DDD1F1371CD0831F00EEC549 /* RootViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RootViewController.h; sourceTree = ""; }; DDD1F1381CD0831F00EEC549 /* RootViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RootViewController.m; sourceTree = ""; }; DDD1F13B1CD0832400EEC549 /* BaseTableViewCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BaseTableViewCell.h; sourceTree = ""; }; DDD1F13C1CD0832400EEC549 /* BaseTableViewCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BaseTableViewCell.m; sourceTree = ""; }; DDD1F1411CD0832400EEC549 /* FlyImageLayerTableViewCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FlyImageLayerTableViewCell.h; sourceTree = ""; }; DDD1F1421CD0832400EEC549 /* FlyImageLayerTableViewCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FlyImageLayerTableViewCell.m; sourceTree = ""; }; DDD1F1431CD0832400EEC549 /* FlyImageTableViewCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FlyImageTableViewCell.h; sourceTree = ""; }; DDD1F1441CD0832400EEC549 /* FlyImageTableViewCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FlyImageTableViewCell.m; sourceTree = ""; }; DDD1F1451CD0832400EEC549 /* SDWebImageTableViewCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDWebImageTableViewCell.h; sourceTree = ""; }; DDD1F1461CD0832400EEC549 /* SDWebImageTableViewCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SDWebImageTableViewCell.m; sourceTree = ""; }; DDD1F1471CD0832400EEC549 /* TriditionTableViewCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TriditionTableViewCell.h; sourceTree = ""; }; DDD1F1481CD0832400EEC549 /* TriditionTableViewCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TriditionTableViewCell.m; sourceTree = ""; }; DDD1F1501CD083B300EEC549 /* Default-568h@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "Default-568h@2x.png"; path = "../Default-568h@2x.png"; sourceTree = ""; }; DDD1F1561CD084E500EEC549 /* FlyImageIconView.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = FlyImageIconView.app; sourceTree = BUILT_PRODUCTS_DIR; }; DDD1F1591CD084E500EEC549 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; DDD1F15B1CD084E500EEC549 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; DDD1F15C1CD084E500EEC549 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; DDD1F1691CD084E500EEC549 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; DDD1F19E1CD0B92F00EEC549 /* FlyImageIconLayerTableViewCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FlyImageIconLayerTableViewCell.h; sourceTree = ""; }; DDD1F19F1CD0B92F00EEC549 /* FlyImageIconLayerTableViewCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FlyImageIconLayerTableViewCell.m; sourceTree = ""; }; DDD8FB5F1D59EB9200821392 /* Launch Screen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = "Launch Screen.storyboard"; sourceTree = ""; }; DDDEF42E1CE0AF4600C8366C /* SingleView.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SingleView.app; sourceTree = BUILT_PRODUCTS_DIR; }; DDDEF4311CE0AF4600C8366C /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; DDDEF4331CE0AF4600C8366C /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; DDDEF4341CE0AF4600C8366C /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; DDDEF4361CE0AF4600C8366C /* SingleViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SingleViewController.h; sourceTree = ""; }; DDDEF4371CE0AF4600C8366C /* SingleViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SingleViewController.m; sourceTree = ""; }; DDDEF4411CE0AF4600C8366C /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; E8620E15B0D1F42DDE6C37AA /* Pods-WebP.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-WebP.release.xcconfig"; path = "Pods/Target Support Files/Pods-WebP/Pods-WebP.release.xcconfig"; sourceTree = ""; }; FF39DAB46C23D364F5631D94 /* Pods-SingleView.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SingleView.debug.xcconfig"; path = "Pods/Target Support Files/Pods-SingleView/Pods-SingleView.debug.xcconfig"; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ DD9D97401CC5044D008F279E /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( 8E45DB317D60C68FC813142D /* libPods-FlyImageView.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; DDBED1731CD8C5D600EC9263 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( CEE17C7A1B5057E6682927EF /* libPods-WebP.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; DDD1F1531CD084E500EEC549 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( 7539169AF2FD0C6CD1ED1213 /* libPods-FlyImageIconView.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; DDDEF42B1CE0AF4600C8366C /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( 977F44737DA3E198C81BF6F4 /* libPods-SingleView.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ 3F7EFE6AE8450C9D550265DA /* Pods */ = { isa = PBXGroup; children = ( 7D3083C3FEEB3316F9CCCF92 /* Pods-FlyImageIconView.debug.xcconfig */, 02F5D49EB862BEC96204662A /* Pods-FlyImageIconView.release.xcconfig */, 841CA4D45E6400A9646A0F66 /* Pods-FlyImageView.debug.xcconfig */, 3ABB5A3F814E3BFC95A513BD /* Pods-FlyImageView.release.xcconfig */, FF39DAB46C23D364F5631D94 /* Pods-SingleView.debug.xcconfig */, 33A4E8609F3283491E3DD133 /* Pods-SingleView.release.xcconfig */, D95E1058D32716446EB3DC84 /* Pods-WebP.debug.xcconfig */, E8620E15B0D1F42DDE6C37AA /* Pods-WebP.release.xcconfig */, ); name = Pods; sourceTree = ""; }; 566B055403ECA67AB6D690C2 /* Frameworks */ = { isa = PBXGroup; children = ( 5F9C3948F7DFDB6E2FE5F0B0 /* libPods-FlyImageIconView.a */, 9DCBFE3EE450E44921AFAF53 /* libPods-FlyImageView.a */, 0280F6E421151FBFAE698C23 /* libPods-SingleView.a */, 4EC1098E069AA6CA893D0F2C /* libPods-WebP.a */, ); name = Frameworks; sourceTree = ""; }; DD9D973A1CC5044D008F279E = { isa = PBXGroup; children = ( DD9D97451CC5044D008F279E /* FlyImageView */, DDD1F1571CD084E500EEC549 /* FlyImageIconView */, DDBED1771CD8C5D600EC9263 /* WebP */, DDDEF42F1CE0AF4600C8366C /* SingleView */, DD9D97441CC5044D008F279E /* Products */, 3F7EFE6AE8450C9D550265DA /* Pods */, 566B055403ECA67AB6D690C2 /* Frameworks */, ); sourceTree = ""; }; DD9D97441CC5044D008F279E /* Products */ = { isa = PBXGroup; children = ( DD9D97431CC5044D008F279E /* FlyImageView.app */, DDD1F1561CD084E500EEC549 /* FlyImageIconView.app */, DDBED1761CD8C5D600EC9263 /* WebP.app */, DDDEF42E1CE0AF4600C8366C /* SingleView.app */, ); name = Products; sourceTree = ""; }; DD9D97451CC5044D008F279E /* FlyImageView */ = { isa = PBXGroup; children = ( DDD1F13A1CD0832400EEC549 /* Cells */, DD9D97491CC5044D008F279E /* AppDelegate.h */, DD9D974A1CC5044D008F279E /* AppDelegate.m */, DDD1F1371CD0831F00EEC549 /* RootViewController.h */, DDD1F1381CD0831F00EEC549 /* RootViewController.m */, DD9D97571CC5044D008F279E /* Info.plist */, DD9D97461CC5044D008F279E /* Supporting Files */, DDD8FB5F1D59EB9200821392 /* Launch Screen.storyboard */, ); path = FlyImageView; sourceTree = ""; }; DD9D97461CC5044D008F279E /* Supporting Files */ = { isa = PBXGroup; children = ( DDD1F1501CD083B300EEC549 /* Default-568h@2x.png */, DD9D97471CC5044D008F279E /* main.m */, ); name = "Supporting Files"; sourceTree = ""; }; DDBED1771CD8C5D600EC9263 /* WebP */ = { isa = PBXGroup; children = ( DDBED17B1CD8C5D600EC9263 /* AppDelegate.h */, DDBED17C1CD8C5D600EC9263 /* AppDelegate.m */, DDBED1891CD8C5D600EC9263 /* Info.plist */, DDBED1781CD8C5D600EC9263 /* Supporting Files */, ); path = WebP; sourceTree = ""; }; DDBED1781CD8C5D600EC9263 /* Supporting Files */ = { isa = PBXGroup; children = ( DDBED1791CD8C5D600EC9263 /* main.m */, ); name = "Supporting Files"; sourceTree = ""; }; DDD1F13A1CD0832400EEC549 /* Cells */ = { isa = PBXGroup; children = ( DDD1F13B1CD0832400EEC549 /* BaseTableViewCell.h */, DDD1F13C1CD0832400EEC549 /* BaseTableViewCell.m */, DDD1F1411CD0832400EEC549 /* FlyImageLayerTableViewCell.h */, DDD1F1421CD0832400EEC549 /* FlyImageLayerTableViewCell.m */, DDD1F1431CD0832400EEC549 /* FlyImageTableViewCell.h */, DDD1F1441CD0832400EEC549 /* FlyImageTableViewCell.m */, DDD1F1451CD0832400EEC549 /* SDWebImageTableViewCell.h */, DDD1F1461CD0832400EEC549 /* SDWebImageTableViewCell.m */, DDD1F1471CD0832400EEC549 /* TriditionTableViewCell.h */, DDD1F1481CD0832400EEC549 /* TriditionTableViewCell.m */, DDD1F19E1CD0B92F00EEC549 /* FlyImageIconLayerTableViewCell.h */, DDD1F19F1CD0B92F00EEC549 /* FlyImageIconLayerTableViewCell.m */, DDBECFCB1CD8C0FA00EC9263 /* FlyImageIconViewTableViewCell.h */, DDBECFCC1CD8C0FA00EC9263 /* FlyImageIconViewTableViewCell.m */, DD8E088C1D5DD5BD00FEBC3A /* ProgressImageView.h */, DD8E088D1D5DD5BD00FEBC3A /* ProgressImageView.m */, ); path = Cells; sourceTree = ""; }; DDD1F1571CD084E500EEC549 /* FlyImageIconView */ = { isa = PBXGroup; children = ( DDD1F15B1CD084E500EEC549 /* AppDelegate.h */, DDD1F15C1CD084E500EEC549 /* AppDelegate.m */, DDD1F1691CD084E500EEC549 /* Info.plist */, DDD1F1581CD084E500EEC549 /* Supporting Files */, ); path = FlyImageIconView; sourceTree = ""; }; DDD1F1581CD084E500EEC549 /* Supporting Files */ = { isa = PBXGroup; children = ( DDD1F1591CD084E500EEC549 /* main.m */, ); name = "Supporting Files"; sourceTree = ""; }; DDDEF42F1CE0AF4600C8366C /* SingleView */ = { isa = PBXGroup; children = ( DDDEF4331CE0AF4600C8366C /* AppDelegate.h */, DDDEF4341CE0AF4600C8366C /* AppDelegate.m */, DDDEF4361CE0AF4600C8366C /* SingleViewController.h */, DDDEF4371CE0AF4600C8366C /* SingleViewController.m */, DDDEF4411CE0AF4600C8366C /* Info.plist */, DDDEF4301CE0AF4600C8366C /* Supporting Files */, ); path = SingleView; sourceTree = ""; }; DDDEF4301CE0AF4600C8366C /* Supporting Files */ = { isa = PBXGroup; children = ( DDDEF4311CE0AF4600C8366C /* main.m */, ); name = "Supporting Files"; sourceTree = ""; }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ DD9D97421CC5044D008F279E /* FlyImageView */ = { isa = PBXNativeTarget; buildConfigurationList = DD9D97651CC5044D008F279E /* Build configuration list for PBXNativeTarget "FlyImageView" */; buildPhases = ( 7BB62688892610CDF5BF155B /* [CP] Check Pods Manifest.lock */, DD9D973F1CC5044D008F279E /* Sources */, DD9D97401CC5044D008F279E /* Frameworks */, DD9D97411CC5044D008F279E /* Resources */, AF73610CB4A4FC61F7066FFF /* [CP] Embed Pods Frameworks */, FC8AB57C3D212803BBD51308 /* [CP] Copy Pods Resources */, ); buildRules = ( ); dependencies = ( ); name = FlyImageView; productName = FlyImageView; productReference = DD9D97431CC5044D008F279E /* FlyImageView.app */; productType = "com.apple.product-type.application"; }; DDBED1751CD8C5D600EC9263 /* WebP */ = { isa = PBXNativeTarget; buildConfigurationList = DDBED1991CD8C5D600EC9263 /* Build configuration list for PBXNativeTarget "WebP" */; buildPhases = ( F81A2E353A1692446C6C00E7 /* [CP] Check Pods Manifest.lock */, DDBED1721CD8C5D600EC9263 /* Sources */, DDBED1731CD8C5D600EC9263 /* Frameworks */, DDBED1741CD8C5D600EC9263 /* Resources */, E33D266BAC01DC9109F97E25 /* [CP] Embed Pods Frameworks */, EC7C9F769F09EA99C51E707D /* [CP] Copy Pods Resources */, ); buildRules = ( ); dependencies = ( ); name = WebP; productName = WebP; productReference = DDBED1761CD8C5D600EC9263 /* WebP.app */; productType = "com.apple.product-type.application"; }; DDD1F1551CD084E500EEC549 /* FlyImageIconView */ = { isa = PBXNativeTarget; buildConfigurationList = DDD1F1751CD084E500EEC549 /* Build configuration list for PBXNativeTarget "FlyImageIconView" */; buildPhases = ( 1DE98FA8A78C1C6538E9434F /* [CP] Check Pods Manifest.lock */, DDD1F1521CD084E500EEC549 /* Sources */, DDD1F1531CD084E500EEC549 /* Frameworks */, DDD1F1541CD084E500EEC549 /* Resources */, EC7C322CCF25D58E17ADE873 /* [CP] Embed Pods Frameworks */, 3D1664DB16FAAE5424CC6B29 /* [CP] Copy Pods Resources */, ); buildRules = ( ); dependencies = ( ); name = FlyImageIconView; productName = FlyImageIconView; productReference = DDD1F1561CD084E500EEC549 /* FlyImageIconView.app */; productType = "com.apple.product-type.application"; }; DDDEF42D1CE0AF4600C8366C /* SingleView */ = { isa = PBXNativeTarget; buildConfigurationList = DDDEF4441CE0AF4600C8366C /* Build configuration list for PBXNativeTarget "SingleView" */; buildPhases = ( 0A926CE0EFE2DD6A4B6420AC /* [CP] Check Pods Manifest.lock */, DDDEF42A1CE0AF4600C8366C /* Sources */, DDDEF42B1CE0AF4600C8366C /* Frameworks */, DDDEF42C1CE0AF4600C8366C /* Resources */, AA1CE48E20A4B22B24FDCBE0 /* [CP] Embed Pods Frameworks */, 1FE7AD92C126F35569E2DA7F /* [CP] Copy Pods Resources */, ); buildRules = ( ); dependencies = ( ); name = SingleView; productName = SingleView; productReference = DDDEF42E1CE0AF4600C8366C /* SingleView.app */; productType = "com.apple.product-type.application"; }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ DD9D973B1CC5044D008F279E /* Project object */ = { isa = PBXProject; attributes = { LastUpgradeCheck = 0830; ORGANIZATIONNAME = Augmn; TargetAttributes = { DD9D97421CC5044D008F279E = { CreatedOnToolsVersion = 7.3; }; DDBED1751CD8C5D600EC9263 = { CreatedOnToolsVersion = 7.3; }; DDD1F1551CD084E500EEC549 = { CreatedOnToolsVersion = 7.3; }; DDDEF42D1CE0AF4600C8366C = { CreatedOnToolsVersion = 7.3; }; }; }; buildConfigurationList = DD9D973E1CC5044D008F279E /* Build configuration list for PBXProject "FlyImageView" */; compatibilityVersion = "Xcode 3.2"; developmentRegion = English; hasScannedForEncodings = 0; knownRegions = ( en, Base, ); mainGroup = DD9D973A1CC5044D008F279E; productRefGroup = DD9D97441CC5044D008F279E /* Products */; projectDirPath = ""; projectRoot = ""; targets = ( DD9D97421CC5044D008F279E /* FlyImageView */, DDD1F1551CD084E500EEC549 /* FlyImageIconView */, DDBED1751CD8C5D600EC9263 /* WebP */, DDDEF42D1CE0AF4600C8366C /* SingleView */, ); }; /* End PBXProject section */ /* Begin PBXResourcesBuildPhase section */ DD9D97411CC5044D008F279E /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( DDD8FB601D59EB9200821392 /* Launch Screen.storyboard in Resources */, DDD1F1511CD083B300EEC549 /* Default-568h@2x.png in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; DDBED1741CD8C5D600EC9263 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( DDD8FB621D59EB9200821392 /* Launch Screen.storyboard in Resources */, DDBED1A11CD8C76700EC9263 /* Default-568h@2x.png in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; DDD1F1541CD084E500EEC549 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( DDD8FB611D59EB9200821392 /* Launch Screen.storyboard in Resources */, DDD1F1951CD08E7D00EEC549 /* Default-568h@2x.png in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; DDDEF42C1CE0AF4600C8366C /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( DDD8FB631D59EB9200821392 /* Launch Screen.storyboard in Resources */, DDDEF4451CE0B8AA00C8366C /* Default-568h@2x.png in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ 0A926CE0EFE2DD6A4B6420AC /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputPaths = ( ); name = "[CP] Check Pods Manifest.lock"; outputPaths = ( ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n"; showEnvVarsInLog = 0; }; 1DE98FA8A78C1C6538E9434F /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputPaths = ( ); name = "[CP] Check Pods Manifest.lock"; outputPaths = ( ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n"; showEnvVarsInLog = 0; }; 1FE7AD92C126F35569E2DA7F /* [CP] Copy Pods Resources */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputPaths = ( ); name = "[CP] Copy Pods Resources"; outputPaths = ( ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-SingleView/Pods-SingleView-resources.sh\"\n"; showEnvVarsInLog = 0; }; 3D1664DB16FAAE5424CC6B29 /* [CP] Copy Pods Resources */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputPaths = ( ); name = "[CP] Copy Pods Resources"; outputPaths = ( ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-FlyImageIconView/Pods-FlyImageIconView-resources.sh\"\n"; showEnvVarsInLog = 0; }; 7BB62688892610CDF5BF155B /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputPaths = ( ); name = "[CP] Check Pods Manifest.lock"; outputPaths = ( ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n"; showEnvVarsInLog = 0; }; AA1CE48E20A4B22B24FDCBE0 /* [CP] Embed Pods Frameworks */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputPaths = ( ); name = "[CP] Embed Pods Frameworks"; outputPaths = ( ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-SingleView/Pods-SingleView-frameworks.sh\"\n"; showEnvVarsInLog = 0; }; AF73610CB4A4FC61F7066FFF /* [CP] Embed Pods Frameworks */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputPaths = ( ); name = "[CP] Embed Pods Frameworks"; outputPaths = ( ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-FlyImageView/Pods-FlyImageView-frameworks.sh\"\n"; showEnvVarsInLog = 0; }; E33D266BAC01DC9109F97E25 /* [CP] Embed Pods Frameworks */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputPaths = ( ); name = "[CP] Embed Pods Frameworks"; outputPaths = ( ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-WebP/Pods-WebP-frameworks.sh\"\n"; showEnvVarsInLog = 0; }; EC7C322CCF25D58E17ADE873 /* [CP] Embed Pods Frameworks */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputPaths = ( ); name = "[CP] Embed Pods Frameworks"; outputPaths = ( ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-FlyImageIconView/Pods-FlyImageIconView-frameworks.sh\"\n"; showEnvVarsInLog = 0; }; EC7C9F769F09EA99C51E707D /* [CP] Copy Pods Resources */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputPaths = ( ); name = "[CP] Copy Pods Resources"; outputPaths = ( ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-WebP/Pods-WebP-resources.sh\"\n"; showEnvVarsInLog = 0; }; F81A2E353A1692446C6C00E7 /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputPaths = ( ); name = "[CP] Check Pods Manifest.lock"; outputPaths = ( ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n"; showEnvVarsInLog = 0; }; FC8AB57C3D212803BBD51308 /* [CP] Copy Pods Resources */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputPaths = ( ); name = "[CP] Copy Pods Resources"; outputPaths = ( ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-FlyImageView/Pods-FlyImageView-resources.sh\"\n"; showEnvVarsInLog = 0; }; /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ DD9D973F1CC5044D008F279E /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( DDD1F1491CD0832400EEC549 /* BaseTableViewCell.m in Sources */, DD8E088E1D5DD5BD00FEBC3A /* ProgressImageView.m in Sources */, DDD1F14E1CD0832400EEC549 /* SDWebImageTableViewCell.m in Sources */, DD9D974B1CC5044D008F279E /* AppDelegate.m in Sources */, DDD1F14F1CD0832400EEC549 /* TriditionTableViewCell.m in Sources */, DDD1F14D1CD0832400EEC549 /* FlyImageTableViewCell.m in Sources */, DDD1F14C1CD0832400EEC549 /* FlyImageLayerTableViewCell.m in Sources */, DD9D97481CC5044D008F279E /* main.m in Sources */, DDD1F1391CD0831F00EEC549 /* RootViewController.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; DDBED1721CD8C5D600EC9263 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( DD2EDB131D61E6C30085C9E3 /* ProgressImageView.m in Sources */, DDBED17D1CD8C5D600EC9263 /* AppDelegate.m in Sources */, DDBED19B1CD8C70C00EC9263 /* RootViewController.m in Sources */, DDBED19D1CD8C71900EC9263 /* TriditionTableViewCell.m in Sources */, DDBED19F1CD8C72A00EC9263 /* FlyImageTableViewCell.m in Sources */, DDBED19E1CD8C71B00EC9263 /* SDWebImageTableViewCell.m in Sources */, DDBED19C1CD8C71100EC9263 /* BaseTableViewCell.m in Sources */, DDBED1A01CD8C72E00EC9263 /* FlyImageLayerTableViewCell.m in Sources */, DDBED17A1CD8C5D600EC9263 /* main.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; DDD1F1521CD084E500EEC549 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( DDD1F1941CD08D1E00EEC549 /* RootViewController.m in Sources */, DDD1F18F1CD0872D00EEC549 /* BaseTableViewCell.m in Sources */, DDD1F1A01CD0B92F00EEC549 /* FlyImageIconLayerTableViewCell.m in Sources */, DDD1F1911CD0872D00EEC549 /* SDWebImageTableViewCell.m in Sources */, DDD1F1931CD0872D00EEC549 /* TriditionTableViewCell.m in Sources */, DDD1F15D1CD084E500EEC549 /* AppDelegate.m in Sources */, DDD1F15A1CD084E500EEC549 /* main.m in Sources */, DDBECFCD1CD8C0FA00EC9263 /* FlyImageIconViewTableViewCell.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; DDDEF42A1CE0AF4600C8366C /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( DDDEF4381CE0AF4600C8366C /* SingleViewController.m in Sources */, DDDEF4351CE0AF4600C8366C /* AppDelegate.m in Sources */, DDDEF4321CE0AF4600C8366C /* main.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXSourcesBuildPhase section */ /* Begin XCBuildConfiguration section */ DD9D97631CC5044D008F279E /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_NONNULL = YES; 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_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_SUSPICIOUS_MOVE = YES; 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 = 8.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; TARGETED_DEVICE_FAMILY = "1,2"; }; name = Debug; }; DD9D97641CC5044D008F279E /* Release */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_NONNULL = YES; 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_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_SUSPICIOUS_MOVE = YES; 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 = 8.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; TARGETED_DEVICE_FAMILY = "1,2"; VALIDATE_PRODUCT = YES; }; name = Release; }; DD9D97661CC5044D008F279E /* Debug */ = { isa = XCBuildConfiguration; baseConfigurationReference = 841CA4D45E6400A9646A0F66 /* Pods-FlyImageView.debug.xcconfig */; buildSettings = { CODE_SIGN_IDENTITY = "iPhone Developer"; INFOPLIST_FILE = FlyImageView/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 8.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = com.augmn.test; PRODUCT_NAME = "$(TARGET_NAME)"; }; name = Debug; }; DD9D97671CC5044D008F279E /* Release */ = { isa = XCBuildConfiguration; baseConfigurationReference = 3ABB5A3F814E3BFC95A513BD /* Pods-FlyImageView.release.xcconfig */; buildSettings = { CODE_SIGN_IDENTITY = "iPhone Developer"; INFOPLIST_FILE = FlyImageView/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 8.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = com.augmn.test; PRODUCT_NAME = "$(TARGET_NAME)"; }; name = Release; }; DDBED1951CD8C5D600EC9263 /* Debug */ = { isa = XCBuildConfiguration; baseConfigurationReference = D95E1058D32716446EB3DC84 /* Pods-WebP.debug.xcconfig */; buildSettings = { INFOPLIST_FILE = WebP/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 8.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = com.augmn.flyimageview.WebP; PRODUCT_NAME = "$(TARGET_NAME)"; }; name = Debug; }; DDBED1961CD8C5D600EC9263 /* Release */ = { isa = XCBuildConfiguration; baseConfigurationReference = E8620E15B0D1F42DDE6C37AA /* Pods-WebP.release.xcconfig */; buildSettings = { INFOPLIST_FILE = WebP/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 8.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = com.augmn.flyimageview.WebP; PRODUCT_NAME = "$(TARGET_NAME)"; }; name = Release; }; DDD1F1761CD084E500EEC549 /* Debug */ = { isa = XCBuildConfiguration; baseConfigurationReference = 7D3083C3FEEB3316F9CCCF92 /* Pods-FlyImageIconView.debug.xcconfig */; buildSettings = { INFOPLIST_FILE = FlyImageIconView/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 8.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = com.augmn.flyimageiconview; PRODUCT_NAME = "$(TARGET_NAME)"; }; name = Debug; }; DDD1F1771CD084E500EEC549 /* Release */ = { isa = XCBuildConfiguration; baseConfigurationReference = 02F5D49EB862BEC96204662A /* Pods-FlyImageIconView.release.xcconfig */; buildSettings = { INFOPLIST_FILE = FlyImageIconView/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 8.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = com.augmn.flyimageiconview; PRODUCT_NAME = "$(TARGET_NAME)"; }; name = Release; }; DDDEF4421CE0AF4600C8366C /* Debug */ = { isa = XCBuildConfiguration; baseConfigurationReference = FF39DAB46C23D364F5631D94 /* Pods-SingleView.debug.xcconfig */; buildSettings = { INFOPLIST_FILE = SingleView/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 8.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = com.augmn.flyimageview.SingleView; PRODUCT_NAME = "$(TARGET_NAME)"; }; name = Debug; }; DDDEF4431CE0AF4600C8366C /* Release */ = { isa = XCBuildConfiguration; baseConfigurationReference = 33A4E8609F3283491E3DD133 /* Pods-SingleView.release.xcconfig */; buildSettings = { INFOPLIST_FILE = SingleView/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 8.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = com.augmn.flyimageview.SingleView; PRODUCT_NAME = "$(TARGET_NAME)"; }; name = Release; }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ DD9D973E1CC5044D008F279E /* Build configuration list for PBXProject "FlyImageView" */ = { isa = XCConfigurationList; buildConfigurations = ( DD9D97631CC5044D008F279E /* Debug */, DD9D97641CC5044D008F279E /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; DD9D97651CC5044D008F279E /* Build configuration list for PBXNativeTarget "FlyImageView" */ = { isa = XCConfigurationList; buildConfigurations = ( DD9D97661CC5044D008F279E /* Debug */, DD9D97671CC5044D008F279E /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; DDBED1991CD8C5D600EC9263 /* Build configuration list for PBXNativeTarget "WebP" */ = { isa = XCConfigurationList; buildConfigurations = ( DDBED1951CD8C5D600EC9263 /* Debug */, DDBED1961CD8C5D600EC9263 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; DDD1F1751CD084E500EEC549 /* Build configuration list for PBXNativeTarget "FlyImageIconView" */ = { isa = XCConfigurationList; buildConfigurations = ( DDD1F1761CD084E500EEC549 /* Debug */, DDD1F1771CD084E500EEC549 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; DDDEF4441CE0AF4600C8366C /* Build configuration list for PBXNativeTarget "SingleView" */ = { isa = XCConfigurationList; buildConfigurations = ( DDDEF4421CE0AF4600C8366C /* Debug */, DDDEF4431CE0AF4600C8366C /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; /* End XCConfigurationList section */ }; rootObject = DD9D973B1CC5044D008F279E /* Project object */; } ================================================ FILE: Examples/FlyImageView.xcworkspace/contents.xcworkspacedata ================================================ ================================================ FILE: Examples/FlyImageView.xcworkspace/xcshareddata/xcschemes/FlyImageIconView.xcscheme ================================================ ================================================ FILE: Examples/FlyImageView.xcworkspace/xcshareddata/xcschemes/FlyImageView.xcscheme ================================================ ================================================ FILE: Examples/FlyImageView.xcworkspace/xcshareddata/xcschemes/SingleView.xcscheme ================================================ ================================================ FILE: Examples/FlyImageView.xcworkspace/xcshareddata/xcschemes/WebP.xcscheme ================================================ ================================================ FILE: Examples/Podfile ================================================ platform :ios, '8.0' # ignore all warnings from all pods inhibit_all_warnings! target 'FlyImageView' do pod 'SDWebImage', '~> 3.7' pod 'FlyImage', :path => '../FlyImage.podspec' end target 'FlyImageIconView' do pod 'SDWebImage', '~> 3.7' pod 'FlyImage', :path => '../FlyImage.podspec' end target 'WebP' do pod 'SDWebImage/WebP', '~> 3.7' pod 'FlyImage/WebP', :path => '../FlyImage.podspec' end target 'SingleView' do pod 'FlyImage', :path => '../FlyImage.podspec' end ================================================ FILE: Examples/SingleView/AppDelegate.h ================================================ // // AppDelegate.h // SingleView // // Created by Ye Tong on 5/9/16. // Copyright © 2016 Augmn. All rights reserved. // #import @interface AppDelegate : UIResponder @property (strong, nonatomic) UIWindow *window; @end ================================================ FILE: Examples/SingleView/AppDelegate.m ================================================ // // AppDelegate.m // SingleView // // Created by Ye Tong on 5/9/16. // Copyright © 2016 Augmn. All rights reserved. // #import "AppDelegate.h" #import "SingleViewController.h" @interface AppDelegate () @end @implementation AppDelegate - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { // Override point for customization after application launch. self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds]; self.window.backgroundColor = [UIColor blackColor]; SingleViewController *rootViewController = [[SingleViewController alloc] init]; self.window.rootViewController = rootViewController; [self.window makeKeyAndVisible]; 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: Examples/SingleView/Info.plist ================================================ CFBundleDevelopmentRegion en CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIdentifier $(PRODUCT_BUNDLE_IDENTIFIER) CFBundleInfoDictionaryVersion 6.0 CFBundleName $(PRODUCT_NAME) CFBundlePackageType APPL CFBundleShortVersionString 1.0 CFBundleSignature ???? CFBundleVersion 1 LSRequiresIPhoneOS NSAppTransportSecurity NSAllowsArbitraryLoads UILaunchStoryboardName Launch Screen UIRequiredDeviceCapabilities armv7 UIRequiresFullScreen UIStatusBarHidden UISupportedInterfaceOrientations UIInterfaceOrientationPortrait UISupportedInterfaceOrientations~ipad UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight ================================================ FILE: Examples/SingleView/SingleViewController.h ================================================ // // ViewController.h // SingleView // // Created by Ye Tong on 5/9/16. // Copyright © 2016 Augmn. All rights reserved. // #import @interface SingleViewController : UIViewController @end ================================================ FILE: Examples/SingleView/SingleViewController.m ================================================ // // SingleViewController.m // SingleView // // Created by Ye Tong on 5/9/16. // Copyright © 2016 Augmn. All rights reserved. // #import "SingleViewController.h" #import "FlyImage.h" @implementation SingleViewController { NSMutableArray *_imageViews; NSMutableArray *_iconViews; } - (BOOL)prefersStatusBarHidden { return YES; } - (void)viewDidLoad { [super viewDidLoad]; [FlyImageCache sharedInstance].autoDismissImage = YES; _imageViews = [NSMutableArray new]; _iconViews = [NSMutableArray new]; // add // remove // clear CGFloat fromY = self.view.bounds.size.height - 200; [self insertButtonWithTitle:@"AddImageView" selector:@selector(onAddImageView) point:CGPointMake(10, fromY)]; [self insertButtonWithTitle:@"RemoveImageView" selector:@selector(onRemoveImageView) point:CGPointMake(self.view.bounds.size.width/2 - 40, fromY)]; [self insertButtonWithTitle:@"ClearImageViews" selector:@selector(onClearImageViews) point:CGPointMake(self.view.bounds.size.width - 90, fromY)]; [self insertButtonWithTitle:@"AddIconView" selector:@selector(onAddIconView) point:CGPointMake(10, fromY + 100)]; [self insertButtonWithTitle:@"RemoveIconView" selector:@selector(onRemoveIconView) point:CGPointMake(self.view.bounds.size.width/2 - 40, fromY + 100)]; [self insertButtonWithTitle:@"ClearIconViews" selector:@selector(onClearIconViews) point:CGPointMake(self.view.bounds.size.width - 90, fromY + 100)]; } - (void)insertButtonWithTitle:(NSString *)title selector:(SEL)selector point:(CGPoint)point { UIButton *addButton = [UIButton buttonWithType:UIButtonTypeSystem]; addButton.frame = CGRectMake(point.x, point.y, 80, 44); addButton.backgroundColor = [UIColor orangeColor]; [addButton setTitle:title forState:UIControlStateNormal]; [addButton addTarget:self action:selector forControlEvents:UIControlEventTouchUpInside]; addButton.titleLabel.adjustsFontSizeToFitWidth = YES; [self.view addSubview:addButton]; } - (void)onAddImageView { static NSInteger kCount = 0; NSMutableArray *imagesInRow = [NSMutableArray new]; CGFloat size = self.view.bounds.size.width / 5; NSInteger index = kCount % 100; for (NSInteger i=0; i<4; i++) { UIImageView *imageView = [[UIImageView alloc] initWithFrame:CGRectMake(i * (size + 10), [_imageViews count] * (size + 10), size, size)]; NSURL *url = [NSURL URLWithString: [NSString stringWithFormat:@"http://liuliantv.oss-cn-beijing.aliyuncs.com/flyimage/%ld.jpg", (long)index]]; imageView.imageURL = url; [self.view insertSubview:imageView atIndex:0]; [imagesInRow addObject:imageView]; } [_imageViews addObject:imagesInRow]; kCount++; } - (void)onRemoveImageView { NSArray *imagesInRow = [_imageViews lastObject]; for (UIImageView *imageView in imagesInRow) { [imageView removeFromSuperview]; } [_imageViews removeLastObject]; } - (void)onClearImageViews { for (NSArray *imagesInRow in _imageViews) { for (UIImageView *imageView in imagesInRow) { [imageView removeFromSuperview]; } } [_imageViews removeAllObjects]; } - (void)onAddIconView { static NSInteger kCount = 0; NSMutableArray *imagesInRow = [NSMutableArray new]; CGFloat size = self.view.bounds.size.width / 5; NSInteger index = kCount % 100; for (NSInteger i=0; i<4; i++) { UIImageView *imageView = [[UIImageView alloc] initWithFrame:CGRectMake(i * (size + 10), [_iconViews count] * (size + 10), size, size)]; NSURL *url = [NSURL URLWithString: [NSString stringWithFormat:@"http://liuliantv.oss-cn-beijing.aliyuncs.com/flyimage/%ld_tn.jpg", (long)index]]; imageView.iconURL = url; [self.view insertSubview:imageView atIndex:0]; [imagesInRow addObject:imageView]; } [_iconViews addObject:imagesInRow]; kCount++; } - (void)onRemoveIconView { NSArray *imagesInRow = [_iconViews lastObject]; for (UIImageView *imageView in imagesInRow) { [imageView removeFromSuperview]; } [_iconViews removeLastObject]; } - (void)onClearIconViews { for (NSArray *imagesInRow in _iconViews) { for (UIImageView *imageView in imagesInRow) { [imageView removeFromSuperview]; } } [_iconViews removeAllObjects]; } @end ================================================ FILE: Examples/SingleView/main.m ================================================ // // main.m // SingleView // // Created by Ye Tong on 5/9/16. // Copyright © 2016 Augmn. All rights reserved. // #import #import "AppDelegate.h" int main(int argc, char * argv[]) { @autoreleasepool { return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); } } ================================================ FILE: Examples/WebP/AppDelegate.h ================================================ // // AppDelegate.h // WebP // // Created by Ye Tong on 5/3/16. // Copyright © 2016 Augmn. All rights reserved. // #import @interface AppDelegate : UIResponder @property (strong, nonatomic) UIWindow *window; @end ================================================ FILE: Examples/WebP/AppDelegate.m ================================================ // // AppDelegate.m // WebP // // Created by Ye Tong on 5/3/16. // Copyright © 2016 Augmn. All rights reserved. // #import "AppDelegate.h" #import "RootViewController.h" #import "FlyImageTableViewCell.h" #import "FlyImageLayerTableViewCell.h" #import "SDWebImageTableViewCell.h" #import "FlyImage.h" @interface AppDelegate () @end @implementation AppDelegate - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds]; self.window.backgroundColor = [UIColor whiteColor]; [FlyImageCache sharedInstance].autoDismissImage = YES; RootViewController *rootViewController = [[RootViewController alloc] init]; rootViewController.suffix = @".webp"; rootViewController.heightOfCell = 150; rootViewController.cellsPerRow = 1; rootViewController.cells = @[ @{ @"class": [SDWebImageTableViewCell class], @"title": @"SDWebImage" } ,@{ @"class": [FlyImageTableViewCell class], @"title": @"FlyImageView" } ,@{ @"class": [FlyImageLayerTableViewCell class], @"title": @"FlyImageLayer" }]; self.window.rootViewController = rootViewController; [self.window makeKeyAndVisible]; 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: Examples/WebP/Info.plist ================================================ CFBundleDevelopmentRegion en CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIdentifier $(PRODUCT_BUNDLE_IDENTIFIER) CFBundleInfoDictionaryVersion 6.0 CFBundleName $(PRODUCT_NAME) CFBundlePackageType APPL CFBundleShortVersionString 1.0 CFBundleSignature ???? CFBundleVersion 1 LSRequiresIPhoneOS NSAppTransportSecurity NSAllowsArbitraryLoads UILaunchStoryboardName Launch Screen UIRequiredDeviceCapabilities armv7 UIRequiresFullScreen UIStatusBarHidden UISupportedInterfaceOrientations UIInterfaceOrientationPortrait UISupportedInterfaceOrientations~ipad UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight ================================================ FILE: Examples/WebP/main.m ================================================ // // main.m // WebP // // Created by Ye Tong on 5/3/16. // Copyright © 2016 Augmn. All rights reserved. // #import #import "AppDelegate.h" int main(int argc, char * argv[]) { @autoreleasepool { return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); } } ================================================ FILE: FlyImage/Core/FlyImageCache.h ================================================ // // FlyImageCache.h // Demo // // Created by Ye Tong on 3/17/16. // Copyright © 2016 NorrisTong. All rights reserved. // #import #import "FlyImageCacheProtocol.h" @class FlyImageDataFileManager; /** * Manage image files in one folder. */ @interface FlyImageCache : NSObject @property (nonatomic, assign) CGFloat maxCachedBytes; // Default is 512Mb. @property (nonatomic, assign) BOOL autoDismissImage; // If you want to reduce memory when the app enter background, set this flag as YES. Default is NO. @property (nonatomic, strong) FlyImageDataFileManager* dataFileManager; #ifdef FLYIMAGE_WEBP @property (nonatomic, assign) BOOL autoConvertWebP; // Should convert WebP file to JPEG file automaticlly. Default is NO. If yes, it will speed up retrieving operation for the next time. @property (nonatomic, assign) CGFloat compressionQualityForWebP; // Default is 0.8. #endif - (void)addImageWithKey:(NSString*)key filename:(NSString*)filename completed:(FlyImageCacheRetrieveBlock)completed; - (void)addImageWithKey:(NSString*)key filename:(NSString*)filename drawSize:(CGSize)drawSize contentsGravity:(NSString* const)contentsGravity cornerRadius:(CGFloat)cornerRadius completed:(FlyImageCacheRetrieveBlock)completed; /** * Get image with customize parameters from cache asynchronously. * Avoid executing `CGDataProviderCreateWithCopyOfData`. * * @param key image key * @param drawSize render size * @param contentsGravity contentMode of render view * @param cornerRadius cornerRadius of render view * @param completed callback */ - (void)asyncGetImageWithKey:(NSString*)key drawSize:(CGSize)drawSize contentsGravity:(NSString* const)contentsGravity cornerRadius:(CGFloat)cornerRadius completed:(FlyImageCacheRetrieveBlock)completed; /** * Get the image path saved in the disk. */ - (NSString*)imagePathWithKey:(NSString*)key; /** * Protect the file, which can't be removed. * * @param key image key */ - (void)protectFileWithKey:(NSString*)key; /** * Don't protect the file, which can be removed. * * @param key image key */ - (void)unProtectFileWithKey:(NSString*)key; @end ================================================ FILE: FlyImage/Core/FlyImageCache.m ================================================ // // FlyImageCache.m // Demo // // Created by Ye Tong on 3/17/16. // Copyright © 2016 NorrisTong. All rights reserved. // #import "FlyImageCache.h" #import "FlyImageDataFileManager.h" #import "FlyImageUtils.h" #import "FlyImageDecoder.h" #import "FlyImageRetrieveOperation.h" #define kImageInfoIndexFileName 0 #define kImageInfoIndexContentType 1 #define kImageInfoIndexWidth 2 #define kImageInfoIndexHeight 3 #define kImageInfoIndexLock 4 @interface FlyImageCache () @property (nonatomic, strong) FlyImageDecoder* decoder; @end @implementation FlyImageCache { NSRecursiveLock* _lock; NSString* _metaPath; NSMutableDictionary* _images; NSMutableDictionary* _addingImages; NSOperationQueue* _retrievingQueue; } + (instancetype)sharedInstance { static dispatch_once_t onceToken; static FlyImageCache* __instance = nil; dispatch_once(&onceToken, ^{ NSString *metaPath = [[FlyImageUtils directoryPath] stringByAppendingPathComponent:@"/__images"]; __instance = [[[self class] alloc] initWithMetaPath:metaPath]; }); return __instance; } - (instancetype)initWithMetaPath:(NSString*)metaPath { if (self = [self init]) { _lock = [[NSRecursiveLock alloc] init]; _addingImages = [[NSMutableDictionary alloc] init]; _maxCachedBytes = 1024 * 1024 * 512; _retrievingQueue = [NSOperationQueue new]; _retrievingQueue.qualityOfService = NSQualityOfServiceUserInteractive; _retrievingQueue.maxConcurrentOperationCount = 6; #ifdef FLYIMAGE_WEBP _autoConvertWebP = NO; _compressionQualityForWebP = 0.8; #endif _metaPath = [metaPath copy]; NSString* folderPath = [[_metaPath stringByDeletingLastPathComponent] stringByAppendingPathComponent:@"/files"]; self.dataFileManager = [[FlyImageDataFileManager alloc] initWithFolderPath:folderPath]; _metaPath = [metaPath copy]; [self loadMetadata]; _decoder = [[FlyImageDecoder alloc] init]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onWillTerminate) name:UIApplicationWillTerminateNotification object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onDidEnterBackground) name:UIApplicationDidEnterBackgroundNotification object:nil]; } return self; } #pragma mark - LifeCircle - (void)dealloc { [_retrievingQueue cancelAllOperations]; [self cleanCachedImages]; [[NSNotificationCenter defaultCenter] removeObserver:self]; } - (void)onWillTerminate { [self cleanCachedImages]; } - (void)onDidEnterBackground { [self cleanCachedImages]; } - (void)cleanCachedImages { [_retrievingQueue cancelAllOperations]; __weak __typeof__(self) weakSelf = self; [self.dataFileManager calculateSizeWithCompletionBlock:^(NSUInteger fileCount, NSUInteger totalSize) { if ( weakSelf.maxCachedBytes > totalSize ) { return; } NSMutableArray *lockedFilenames = [NSMutableArray array]; NSMutableArray *lockedKeys = [NSMutableArray array]; @synchronized (_images) { for (NSString *key in _images) { NSArray *imageInfo = [_images objectForKey:key]; if ( [imageInfo count] > kImageInfoIndexLock && [[imageInfo objectAtIndex:kImageInfoIndexLock] boolValue] ){ [lockedFilenames addObject:[imageInfo objectAtIndex:kImageInfoIndexFileName]]; [lockedKeys addObject:key]; } } } [weakSelf.dataFileManager purgeWithExceptions:lockedFilenames toSize:weakSelf.maxCachedBytes/2 completed:^(NSUInteger fileCount, NSUInteger totalSize) { // remove unlock keys @synchronized (_images) { NSArray *allKeys = [_images allKeys]; for (NSString *key in allKeys) { if ( [lockedKeys indexOfObject:key] == NSNotFound ) { [_images removeObjectForKey:key]; } } } [weakSelf saveMetadata]; }]; }]; } #pragma mark - APIs - (void)addImageWithKey:(NSString*)key filename:(NSString*)filename completed:(FlyImageCacheRetrieveBlock)completed { [self addImageWithKey:key filename:filename drawSize:CGSizeZero contentsGravity:kCAGravityResizeAspect cornerRadius:0 completed:completed]; } - (void)addImageWithKey:(NSString*)key filename:(NSString*)filename drawSize:(CGSize)drawSize contentsGravity:(NSString* const)contentsGravity cornerRadius:(CGFloat)cornerRadius completed:(FlyImageCacheRetrieveBlock)completed { NSParameterAssert(key != nil); NSParameterAssert(filename != nil); if ([self isImageExistWithKey:key] && completed != nil) { [self asyncGetImageWithKey:key drawSize:drawSize contentsGravity:contentsGravity cornerRadius:cornerRadius completed:completed]; return; } // ignore draw size when add images @synchronized(_addingImages) { if ([_addingImages objectForKey:key] == nil) { NSMutableArray* blocks = [NSMutableArray array]; if (completed != nil) { [blocks addObject:completed]; } [_addingImages setObject:blocks forKey:key]; } else { // waiting for drawing NSMutableArray* blocks = [_addingImages objectForKey:key]; if (completed != nil) { [blocks addObject:completed]; } return; } } [self doAddImageWithKey:[key copy] filename:[filename copy] drawSize:drawSize contentsGravity:contentsGravity cornerRadius:cornerRadius completed:completed]; } - (void)doAddImageWithKey:(NSString*)key filename:(NSString*)filename drawSize:(CGSize)drawSize contentsGravity:(NSString* const)contentsGravity cornerRadius:(CGFloat)cornerRadius completed:(FlyImageCacheRetrieveBlock)completed { static dispatch_queue_t __drawingQueue = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ NSString *name = [NSString stringWithFormat:@"com.flyimage.addimage.%@", [[NSUUID UUID] UUIDString]]; __drawingQueue = dispatch_queue_create([name cStringUsingEncoding:NSASCIIStringEncoding], NULL); }); dispatch_async(__drawingQueue, ^{ // get image meta CGSize imageSize = CGSizeZero; ImageContentType contentType; @autoreleasepool { NSString *filePath = [self.dataFileManager.folderPath stringByAppendingPathComponent:filename]; NSData *fileData = [NSData dataWithContentsOfFile:filePath]; contentType = [FlyImageUtils contentTypeForImageData:fileData]; UIImage *image = nil; if ( contentType == ImageContentTypeWebP ) { #ifdef FLYIMAGE_WEBP if ( _autoConvertWebP ) { // Decode WebP BOOL hasAlpha; image = [_decoder imageWithWebPData:fileData hasAlpha:&hasAlpha]; if ( image != nil ) { NSData *compresstionImageData; if ( hasAlpha ) { // Convert WebP to PNG compresstionImageData = UIImagePNGRepresentation(image); if ( compresstionImageData != nil ){ contentType = ImageContentTypePNG; } }else{ // Convert WebP to JPEG compresstionImageData = UIImageJPEGRepresentation(image, self.compressionQualityForWebP); if ( compresstionImageData != nil ){ contentType = ImageContentTypeJPEG; } } // save image into disk if ( compresstionImageData != nil ){ NSString *filePath = [self.dataFileManager.folderPath stringByAppendingPathComponent:filename]; [compresstionImageData writeToFile:filePath atomically:YES]; } } } #endif }else{ // read image meta, not data image = [UIImage imageWithData:fileData]; } imageSize = image.size; } [self.dataFileManager addExistFileName:filename]; FlyImageDataFile *dataFile = [self.dataFileManager retrieveFileWithName:filename]; if ( [dataFile open] == false ) { [self afterAddImage:nil key:key]; return; } // save data file's param void *bytes = dataFile.address; size_t fileLength = (size_t)dataFile.fileLength; // callback with image UIImage *decodeImage = [_decoder imageWithFile:(__bridge void *)(dataFile) contentType:contentType bytes:bytes length:fileLength drawSize:CGSizeEqualToSize(drawSize, CGSizeZero) ? imageSize : drawSize contentsGravity:contentsGravity cornerRadius:cornerRadius]; [self afterAddImage:decodeImage key:key]; @synchronized (_images) { // path, width, height, length NSArray *imageInfo = @[ filename, @(contentType), @(imageSize.width), @(imageSize.height) ]; [_images setObject:imageInfo forKey:key]; } // save meta [self saveMetadata]; }); } - (void)afterAddImage:(UIImage*)image key:(NSString*)key { NSArray* blocks = nil; @synchronized(_addingImages) { blocks = [[_addingImages objectForKey:key] copy]; [_addingImages removeObjectForKey:key]; } dispatch_main_sync_safe(^{ for ( FlyImageCacheRetrieveBlock block in blocks) { block( key, image ); } }); } - (void)removeImageWithKey:(NSString*)key { NSString* fileName = nil; @synchronized(_images) { NSArray* imageInfo = [_images objectForKey:key]; if (imageInfo == nil) { return; } // if locked if ([imageInfo count] > kImageInfoIndexLock && [[imageInfo objectAtIndex:kImageInfoIndexLock] boolValue] == YES) { return; } [_images removeObjectForKey:key]; fileName = [imageInfo firstObject]; } if (fileName != nil) { [self.dataFileManager removeFileWithName:fileName]; } } - (void)changeImageKey:(NSString*)oldKey newKey:(NSString*)newKey { @synchronized(_images) { id imageInfo = [_images objectForKey:oldKey]; if (imageInfo == nil) { return; } [_images setObject:imageInfo forKey:newKey]; [_images removeObjectForKey:oldKey]; } } - (BOOL)isImageExistWithKey:(NSString*)key { NSParameterAssert(key != nil); @synchronized(_images) { return [_images objectForKey:key] != nil; } } - (void)asyncGetImageWithKey:(NSString*)key completed:(FlyImageCacheRetrieveBlock)completed { [self asyncGetImageWithKey:key drawSize:CGSizeZero contentsGravity:kCAGravityResizeAspect cornerRadius:0 completed:completed]; } - (void)asyncGetImageWithKey:(NSString*)key drawSize:(CGSize)drawSize contentsGravity:(NSString* const)contentsGravity cornerRadius:(CGFloat)cornerRadius completed:(FlyImageCacheRetrieveBlock)completed { NSParameterAssert(key != nil); NSParameterAssert(completed != nil); NSArray* imageInfo; @synchronized(_images) { imageInfo = [_images objectForKey:key]; } if (imageInfo == nil || [imageInfo count] <= kImageInfoIndexHeight) { completed(key, nil); return; } // filename, width, height, length NSString* filename = [imageInfo firstObject]; FlyImageDataFile* dataFile = [self.dataFileManager retrieveFileWithName:filename]; if (dataFile == nil) { completed(key, nil); return; } // if the image is retrieving, then just add the block, no need to create a new operation. for (FlyImageRetrieveOperation* operation in _retrievingQueue.operations) { if ([operation.name isEqualToString:key]) { [operation addBlock:completed]; return; } } CGSize imageSize = drawSize; if (drawSize.width == 0 && drawSize.height == 0) { CGFloat imageWidth = [[imageInfo objectAtIndex:kImageInfoIndexWidth] floatValue]; CGFloat imageHeight = [[imageInfo objectAtIndex:kImageInfoIndexHeight] floatValue]; imageSize = CGSizeMake(imageWidth, imageHeight); } ImageContentType contentType = [[imageInfo objectAtIndex:kImageInfoIndexContentType] integerValue]; __weak __typeof__(self) weakSelf = self; FlyImageRetrieveOperation* operation = [[FlyImageRetrieveOperation alloc] initWithRetrieveBlock:^UIImage * { if ( ![dataFile open] ) { return nil; } return [weakSelf.decoder imageWithFile:(__bridge void *)(dataFile) contentType:contentType bytes:dataFile.address length:(size_t)dataFile.fileLength drawSize:CGSizeEqualToSize(drawSize, CGSizeZero) ? imageSize : drawSize contentsGravity:contentsGravity cornerRadius:cornerRadius]; }]; operation.name = key; [operation addBlock:completed]; [_retrievingQueue addOperation:operation]; } - (void)cancelGetImageWithKey:(NSString*)key { NSParameterAssert(key != nil); for (FlyImageRetrieveOperation* operation in _retrievingQueue.operations) { if (!operation.cancelled && !operation.finished && [operation.name isEqualToString:key]) { [operation cancel]; return; } } } - (void)purge { NSMutableArray* lockedFilenames = [NSMutableArray array]; @synchronized(_images) { NSMutableArray* lockedKeys = [NSMutableArray array]; for (NSString* key in _images) { NSArray* imageInfo = [_images objectForKey:key]; if ([imageInfo count] > kImageInfoIndexLock && [[imageInfo objectAtIndex:kImageInfoIndexLock] boolValue]) { [lockedFilenames addObject:[imageInfo objectAtIndex:kImageInfoIndexFileName]]; [lockedKeys addObject:key]; } } // remove unlock keys NSArray* allKeys = [_images allKeys]; for (NSString* key in allKeys) { if ([lockedKeys indexOfObject:key] == NSNotFound) { [_images removeObjectForKey:key]; } } } [_retrievingQueue cancelAllOperations]; @synchronized(_addingImages) { for (NSString* key in _addingImages) { NSArray* blocks = [_addingImages objectForKey:key]; dispatch_main_sync_safe(^{ for ( FlyImageCacheRetrieveBlock block in blocks) { block( key, nil ); } }); } [_addingImages removeAllObjects]; } // remove files [self.dataFileManager purgeWithExceptions:lockedFilenames toSize:0 completed:nil]; [self saveMetadata]; } - (NSString*)imagePathWithKey:(NSString*)key { NSParameterAssert(key != nil); @synchronized(_images) { NSArray* fileInfo = [_images objectForKey:key]; if ([fileInfo firstObject] == nil) { return nil; } NSString* filename = [fileInfo objectAtIndex:kImageInfoIndexFileName]; return [self.dataFileManager.folderPath stringByAppendingPathComponent:filename]; } } // 锁住文件,不能被回收 - (void)protectFileWithKey:(NSString*)key { NSParameterAssert(key != nil); if (![self isImageExistWithKey:key]) { return; } @synchronized(_images) { NSArray* fileInfo = [_images objectForKey:key]; if ([fileInfo firstObject] == nil) { return; } // alread locked if ([fileInfo count] > kImageInfoIndexLock && [[fileInfo objectAtIndex:kImageInfoIndexLock] boolValue]) { return; } // name, type, width, height, lock NSMutableArray* newFileInfo = [NSMutableArray arrayWithArray:[fileInfo subarrayWithRange:NSMakeRange(0, kImageInfoIndexLock)]]; [newFileInfo addObject:@(1)]; [_images setObject:newFileInfo forKey:key]; [self saveMetadata]; } } // 解锁文件,可以被回收 - (void)unProtectFileWithKey:(NSString*)key { NSParameterAssert(key != nil); if (![self isImageExistWithKey:key]) { return; } @synchronized(_images) { NSArray* fileInfo = [_images objectForKey:key]; if ([fileInfo firstObject] == nil) { return; } // alread unlocked if ([fileInfo count] <= kImageInfoIndexLock) { return; } // name, type, width, height NSArray* newFileInfo = [fileInfo subarrayWithRange:NSMakeRange(0, kImageInfoIndexLock)]; [_images setObject:newFileInfo forKey:key]; [self saveMetadata]; } } #pragma mark - Working with Metadata - (void)saveMetadata { static dispatch_queue_t __metadataQueue = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ NSString *name = [NSString stringWithFormat:@"com.flyimage.imagemeta.%@", [[NSUUID UUID] UUIDString]]; __metadataQueue = dispatch_queue_create([name cStringUsingEncoding:NSASCIIStringEncoding], NULL); }); dispatch_async(__metadataQueue, ^{ [_lock lock]; NSData *data = [NSJSONSerialization dataWithJSONObject:[_images copy] options:kNilOptions error:NULL]; BOOL fileWriteResult = [data writeToFile:_metaPath atomically:NO]; if (fileWriteResult == NO) { FlyImageErrorLog(@"couldn't save metadata"); } [_lock unlock]; }); } - (void)loadMetadata { // load content from index file NSError* error; NSData* metadataData = [NSData dataWithContentsOfURL:[NSURL fileURLWithPath:_metaPath] options:NSDataReadingMappedAlways error:&error]; if (error != nil || metadataData == nil) { [self createMetadata]; return; } NSDictionary* parsedObject = (NSDictionary*)[NSJSONSerialization JSONObjectWithData:metadataData options:kNilOptions error:&error]; if (error != nil || parsedObject == nil) { [self createMetadata]; return; } _images = [NSMutableDictionary dictionaryWithDictionary:parsedObject]; } - (void)createMetadata { _images = [NSMutableDictionary dictionaryWithCapacity:100]; } @end ================================================ FILE: FlyImage/Core/FlyImageCacheProtocol.h ================================================ // // FlyImageCacheProtocol.h // Demo // // Created by Norris Tong on 4/2/16. // Copyright © 2016 NorrisTong. All rights reserved. // #ifndef FlyImageCacheProtocol_h #define FlyImageCacheProtocol_h typedef void (^FlyImageCacheRetrieveBlock)(NSString* key, UIImage* image); /** * Common API for FlyImageCache and FlyImageIconCache. */ @protocol FlyImageCacheProtocol /** * Create an image cache with default meta path. */ + (instancetype)sharedInstance; /** * Create an image cache with a specific meta path. * * @param metaPath specific meta path, all the images will be saved into folder `metaPath/files` */ - (instancetype)initWithMetaPath:(NSString*)metaPath; /** * Get image from cache asynchronously. */ - (void)asyncGetImageWithKey:(NSString*)key completed:(FlyImageCacheRetrieveBlock)completed; /** * Cancel geting an image from cache if the image has not already got. */ - (void)cancelGetImageWithKey:(NSString*)key; /** * Check if image exists in cache synchronized. NO delay. */ - (BOOL)isImageExistWithKey:(NSString*)key; /** * Remove an image from cache. */ - (void)removeImageWithKey:(NSString*)key; /** * Change the old key with a new key */ - (void)changeImageKey:(NSString*)oldKey newKey:(NSString*)newKey; /** * Remove all the images from the cache. */ - (void)purge; @end #endif /* FlyImageCacheProtocol_h */ ================================================ FILE: FlyImage/Core/FlyImageDataFile.h ================================================ // // FlyImageDataFile.h // Demo // // Created by Ye Tong on 3/18/16. // Copyright © 2016 NorrisTong. All rights reserved. // #import /** * Wrapper of data file, map the disk file to the memory. * * Only support `append` operation, we can move the pointer to replace the data. * */ @interface FlyImageDataFile : NSObject @property (nonatomic, assign, readonly) void* address; @property (nonatomic, assign, readonly) off_t fileLength; // total length of the file. @property (nonatomic, assign, readonly) off_t pointer; // append the data after the pointer. Default is 0. @property (nonatomic, assign) size_t step; // Change the step value to increase the file length. Deafult is 1 byte. - (instancetype)initWithPath:(NSString*)path; - (BOOL)open; - (void)close; /** * Check the file length, if it is not big enough, then increase the file length with step. * * @param offset start position * @param length data length * * @return success or failed */ - (BOOL)prepareAppendDataWithOffset:(size_t)offset length:(size_t)length; /** * Append the data after pointer. * * Must execute `prepareAppendDataWithOffset:length` first. * * @param offset start position * @param length data length * * @return success or failed */ - (BOOL)appendDataWithOffset:(size_t)offset length:(size_t)length; @end ================================================ FILE: FlyImage/Core/FlyImageDataFile.m ================================================ // // FlyImageDataFile.m // Demo // // Created by Ye Tong on 3/18/16. // Copyright © 2016 NorrisTong. All rights reserved. // #import "FlyImageDataFile.h" #import "FlyImageCache.h" #import "FlyImageUtils.h" #import @implementation FlyImageDataFile { NSString* _filePath; int _fileDescriptor; size_t _maxLength; // default is 100Mb. NSRecursiveLock* _lock; } - (instancetype)initWithPath:(NSString*)path { if (self = [self init]) { _filePath = [path copy]; _maxLength = 1024 * 1024 * 100; _step = 1; _pointer = 0; _lock = [[NSRecursiveLock alloc] init]; _fileDescriptor = -1; } return self; } - (void)dealloc { // should close the file if it's not be used again. [self close]; } - (BOOL)open { _fileDescriptor = open([_filePath fileSystemRepresentation], O_RDWR | O_CREAT, 0666); if (_fileDescriptor < 0) { FlyImageErrorLog(@"can't file at %@", _filePath); return NO; } _fileLength = lseek(_fileDescriptor, 0, SEEK_END); if (_fileLength == 0) { [self increaseFileLength:_step]; } else { [self mmap]; } return YES; } - (void)close { if (_fileDescriptor < 0) { return; } [_lock lock]; close(_fileDescriptor); _fileDescriptor = -1; // 取消内存映射 [self munmap]; [_lock unlock]; } - (void)munmap { if (_address == NULL) { return; } munmap(_address, (size_t)_fileLength); _address = NULL; } - (void)mmap { _address = mmap(NULL, (size_t)_fileLength, (PROT_READ | PROT_WRITE), (MAP_FILE | MAP_SHARED), _fileDescriptor, 0); } - (BOOL)prepareAppendDataWithOffset:(size_t)offset length:(size_t)length { NSAssert(_fileDescriptor > -1, @"open this file first."); [_lock lock]; // can't exceed maxLength if (offset + length > _maxLength) { [_lock unlock]; return NO; } // Check the file length, if it is not big enough, then increase the file length with step. if (offset + length > _fileLength) { size_t correctLength = ceill((length * 1.0 / _step)) * _step; if (![self increaseFileLength:correctLength]) { [_lock unlock]; return NO; } } [_lock unlock]; return YES; } - (BOOL)appendDataWithOffset:(size_t)offset length:(size_t)length { NSAssert(_fileDescriptor > -1, @"open this file first."); [_lock lock]; int result = msync(_address + offset, length, MS_SYNC); if (result < 0) { FlyImageErrorLog(@"append data failed"); [_lock unlock]; return NO; } // move the pointer if (offset + length > _pointer) { _pointer = offset + length; } [_lock unlock]; return YES; } - (BOOL)increaseFileLength:(size_t)length { [_lock lock]; // cancel map first [self munmap]; // change file length int result = ftruncate(_fileDescriptor, _fileLength + length); if (result < 0) { FlyImageErrorLog(@"can't truncate data file"); [_lock unlock]; return NO; } // remap _fileLength = lseek(_fileDescriptor, 0, SEEK_END); [self mmap]; [_lock unlock]; return YES; } @end ================================================ FILE: FlyImage/Core/FlyImageDataFileManager.h ================================================ // // FlyImageDataFileManager.h // Demo // // Created by Ye Tong on 3/22/16. // Copyright © 2016 NorrisTong. All rights reserved. // #import #import "FlyImageDataFile.h" /** * Manager of FlyImageDataFile. In charge of creating and deleting data file. */ @interface FlyImageDataFileManager : NSObject @property (nonatomic, strong, readonly) NSString* folderPath; // folder saved data files. @property (nonatomic, assign, readonly) BOOL isDiskFull; // Default is NO. - (instancetype)initWithFolderPath:(NSString*)folderPath; /** * Create a `FlyImageDataFile` if it doesn't exist. */ - (FlyImageDataFile*)createFileWithName:(NSString*)name; /** * Add an exist file. */ - (void)addExistFileName:(NSString*)name; /** * Check the file whether exist or not, no delay. */ - (BOOL)isFileExistWithName:(NSString*)name; /** * Get a `FlyImageDataFile` if it exists. */ - (FlyImageDataFile*)retrieveFileWithName:(NSString*)name; /** * Remove data file */ - (void)removeFileWithName:(NSString*)name; /** * Create a `FlyImageDataFile` async. */ - (void)asyncCreateFileWithName:(NSString*)name completed:(void (^)(FlyImageDataFile* dataFile))completed; /** * Remove all the data files, except some special files. * * @param names except files' names * @param toSize expected size of the folder * @param completed callback */ - (void)purgeWithExceptions:(NSArray*)names toSize:(NSUInteger)toSize completed:(void (^)(NSUInteger fileCount, NSUInteger totalSize))completed; /** * Calculate the folder size. */ - (void)calculateSizeWithCompletionBlock:(void (^)(NSUInteger fileCount, NSUInteger totalSize))block; /** * Free space left in the system space. */ - (void)freeDiskSpaceWithCompletionBlock:(void (^)(NSUInteger freeSize))block; @end ================================================ FILE: FlyImage/Core/FlyImageDataFileManager.m ================================================ // // FlyImageDataFileManager.m // Demo // // Created by Ye Tong on 3/22/16. // Copyright © 2016 NorrisTong. All rights reserved. // #import "FlyImageDataFileManager.h" #import "FlyImageUtils.h" @implementation FlyImageDataFileManager { NSFileManager* _fileManager; dispatch_queue_t _fileQueue; NSMutableDictionary* _fileNames; NSMutableDictionary* _creatingFiles; } - (instancetype)initWithFolderPath:(NSString*)folderPath { if (self = [self init]) { _folderPath = [folderPath copy]; // create a unique queue NSString* queueName = [@"com.flyimage.filemanager." stringByAppendingString:[[NSUUID UUID] UUIDString]]; _fileQueue = dispatch_queue_create([queueName cStringUsingEncoding:NSASCIIStringEncoding], DISPATCH_QUEUE_SERIAL); dispatch_sync(_fileQueue, ^{ _fileManager = [NSFileManager new]; [self makeDirectory]; [self listDirectory]; [self checkDiskStatus]; }); } return self; } - (void)makeDirectory { BOOL isFolderExist = [_fileManager fileExistsAtPath:_folderPath]; if (!isFolderExist) { [_fileManager createDirectoryAtPath:_folderPath withIntermediateDirectories:YES attributes:nil error:nil]; } } - (void)listDirectory { _creatingFiles = [[NSMutableDictionary alloc] init]; NSArray* filenames = [_fileManager contentsOfDirectoryAtPath:_folderPath error:nil]; _fileNames = [[NSMutableDictionary alloc] initWithCapacity:[filenames count]]; for (NSString* filename in filenames) { [_fileNames setObject:@(1) forKey:filename]; } } // Execute in the _fileQueue - (void)checkDiskStatus { NSDictionary* fileAttributes = [_fileManager attributesOfFileSystemForPath:@"/" error:nil]; unsigned long long freeSize = [[fileAttributes objectForKey:NSFileSystemFreeSize] unsignedLongLongValue]; // set disk is full when free size is less than 20Mb _isDiskFull = freeSize < 1024 * 1024 * 20; } - (void)asyncCreateFileWithName:(NSString*)name completed:(void (^)(FlyImageDataFile* dataFile))completed { NSParameterAssert(name); NSParameterAssert(completed); // already exist NSString* filePath = [_folderPath stringByAppendingPathComponent:name]; if ([self isFileExistWithName:name]) { FlyImageDataFile* file = [[FlyImageDataFile alloc] initWithPath:filePath]; completed(file); return; } // can't add more if (_isDiskFull) { completed(nil); return; } // save all the blocks into _creatingFiles, waiting for callback @synchronized(_creatingFiles) { if ([_creatingFiles objectForKey:name] == nil) { [_creatingFiles setObject:[NSMutableArray arrayWithObject:completed] forKey:name]; } else { NSMutableArray* blocks = [_creatingFiles objectForKey:name]; [blocks addObject:completed]; } } dispatch_async(_fileQueue, ^{ NSMutableDictionary *attributes = [NSMutableDictionary dictionary]; [attributes setValue:NSFileProtectionCompleteUnlessOpen forKeyPath:NSFileProtectionKey]; BOOL success = [_fileManager createFileAtPath:filePath contents:nil attributes:attributes]; if ( !success ) { FlyImageErrorLog(@"can't create file at path %@", filePath); // check if the disk is full [self checkDiskStatus]; [self afterCreateFile:nil name:name]; return; } // update index @synchronized (_fileNames) { [_fileNames setObject:@(1) forKey:name]; } FlyImageDataFile *file = [[FlyImageDataFile alloc] initWithPath:filePath]; [self afterCreateFile:file name:name]; }); } - (void)afterCreateFile:(FlyImageDataFile*)file name:(NSString*)name { NSArray* blocks = nil; @synchronized(_creatingFiles) { blocks = [[_creatingFiles objectForKey:name] copy]; [_creatingFiles removeObjectForKey:name]; } dispatch_main_sync_safe(^{ for ( void (^block)(FlyImageDataFile *dataFile) in blocks) { block( file ); } }); } - (void)addExistFileName:(NSString*)name { NSParameterAssert(name); @synchronized(_fileNames) { [_fileNames setObject:@(1) forKey:name]; } } - (FlyImageDataFile*)createFileWithName:(NSString*)name { NSParameterAssert(name); // already exist NSString* filePath = [_folderPath stringByAppendingPathComponent:name]; if ([self isFileExistWithName:name]) { return [[FlyImageDataFile alloc] initWithPath:filePath]; } // can't add more if (_isDiskFull) { return nil; } NSMutableDictionary* attributes = [NSMutableDictionary dictionary]; [attributes setValue:NSFileProtectionCompleteUnlessOpen forKeyPath:NSFileProtectionKey]; BOOL success = [_fileManager createFileAtPath:filePath contents:nil attributes:attributes]; if (!success) { FlyImageErrorLog(@"can't create file at path %@", filePath); // check if the disk is full [self checkDiskStatus]; return nil; } // update index @synchronized(_fileNames) { [_fileNames setObject:@(1) forKey:name]; } return [[FlyImageDataFile alloc] initWithPath:filePath]; } - (BOOL)isFileExistWithName:(NSString*)name { NSParameterAssert(name); return [_fileNames objectForKey:name] != nil; } - (FlyImageDataFile*)retrieveFileWithName:(NSString*)name { NSParameterAssert(name); if (![self isFileExistWithName:name]) { return nil; } NSString* filePath = [_folderPath stringByAppendingPathComponent:name]; FlyImageDataFile* file = [[FlyImageDataFile alloc] initWithPath:filePath]; return file; } - (void)removeFileWithName:(NSString*)name { NSParameterAssert(name); if (![self isFileExistWithName:name]) { return; } // remove from the indexes first @synchronized(_fileNames) { [_fileNames removeObjectForKey:name]; } // delete file dispatch_async(_fileQueue, ^{ [_fileManager removeItemAtPath:[_folderPath stringByAppendingPathComponent:name] error:nil]; [self checkDiskStatus]; }); } - (void)purgeWithExceptions:(NSArray*)names toSize:(NSUInteger)toSize completed:(void (^)(NSUInteger fileCount, NSUInteger totalSize))completed { dispatch_async(_fileQueue, ^{ // from array to dictionary NSMutableDictionary *exceptions = [[NSMutableDictionary alloc] initWithCapacity:[names count]]; for (NSString *name in names) { [exceptions setObject:@(1) forKey:name]; } NSUInteger totalSize = (NSUInteger)[[_fileManager attributesOfItemAtPath:_folderPath error:nil] fileSize]; NSURL *folderURL = [NSURL fileURLWithPath:_folderPath isDirectory:YES]; NSArray *resourceKeys = @[NSURLIsDirectoryKey, NSURLNameKey, NSURLTotalFileAllocatedSizeKey]; // This enumerator prefetches useful properties for our cache files. NSDirectoryEnumerator *enumerator = [_fileManager enumeratorAtURL:folderURL includingPropertiesForKeys:resourceKeys options:NSDirectoryEnumerationSkipsHiddenFiles errorHandler:NULL]; // TODO SORT NSMutableArray *urlsToDelete = [[NSMutableArray alloc] init]; NSMutableArray *namesToDelete = [[NSMutableArray alloc] init]; for (NSURL *fileURL in enumerator) { NSNumber *isDirectory; [fileURL getResourceValue:&isDirectory forKey:NSURLIsDirectoryKey error:nil]; if ([isDirectory boolValue]) { continue; } NSString *fileName; [fileURL getResourceValue:&fileName forKey:NSURLNameKey error:nil]; // dont remove file in exceptions if ( [exceptions objectForKey:fileName] != nil ) { continue; } NSNumber *fileSize; [fileURL getResourceValue:&fileSize forKey:NSURLTotalFileAllocatedSizeKey error:nil]; // dont remove more files totalSize -= [fileSize unsignedLongValue]; if ( totalSize <= toSize ) { break; } [urlsToDelete addObject:fileURL]; [namesToDelete addObject:fileName]; } // remove file and index for (NSURL *fileURL in urlsToDelete) { [_fileManager removeItemAtURL:fileURL error:nil]; } @synchronized (_fileNames) { for (NSString *fileName in namesToDelete) { [_fileNames removeObjectForKey:fileName]; } } [self checkDiskStatus]; if ( completed != nil ) { NSUInteger fileCount = [_fileNames count]; completed( fileCount, fileCount == 0 ? 0 : totalSize ); } }); } - (void)calculateSizeWithCompletionBlock:(void (^)(NSUInteger fileCount, NSUInteger totalSize))block { NSParameterAssert(block); dispatch_async(_fileQueue, ^{ // dont count self folder NSUInteger fileCount = MAX(0, [[[_fileManager enumeratorAtPath:_folderPath] allObjects] count] - 1); NSUInteger totalSize = (NSUInteger)[[_fileManager attributesOfItemAtPath:_folderPath error:nil] fileSize]; dispatch_main_async_safe(^{ block( fileCount, fileCount == 0 ? 0 : totalSize ); }); }); } - (void)freeDiskSpaceWithCompletionBlock:(void (^)(NSUInteger freeSize))block { NSParameterAssert(block); dispatch_async(_fileQueue, ^{ NSDictionary *fileAttributes = [_fileManager attributesOfFileSystemForPath:@"/" error:nil]; NSUInteger freeSize = (NSUInteger)[[fileAttributes objectForKey:NSFileSystemFreeSize] unsignedLongLongValue]; dispatch_main_async_safe(^{ block( freeSize ); }); }); } @end ================================================ FILE: FlyImage/Core/FlyImageDecoder.h ================================================ // // FlyImageDecoder.h // Demo // // Created by Ye Tong on 3/22/16. // Copyright © 2016 NorrisTong. All rights reserved. // #import #import "FlyImageUtils.h" /** * Decode image file. */ @interface FlyImageDecoder : NSObject /** * Convert memory buffer to icon. * * @param bytes memory file * @param offset offset position at the memory file * @param length size of memory buffer * @param drawSize render size */ - (UIImage*)iconImageWithBytes:(void*)bytes offset:(size_t)offset length:(size_t)length drawSize:(CGSize)drawSize; /** * Decode a single image file. * * @param file FlyImageDataFile instance * @param contentType only support PNG/JPEG * @param bytes address of the memory * @param length file size * @param drawSize drawing size, not image size * @param contentsGravity contentsGravity of the image * @param cornerRadius cornerRadius of the image */ - (UIImage*)imageWithFile:(void*)file contentType:(ImageContentType)contentType bytes:(void*)bytes length:(size_t)length drawSize:(CGSize)drawSize contentsGravity:(NSString* const)contentsGravity cornerRadius:(CGFloat)cornerRadius; #ifdef FLYIMAGE_WEBP - (UIImage*)imageWithWebPData:(NSData*)imageData hasAlpha:(BOOL*)hasAlpha; #endif @end ================================================ FILE: FlyImage/Core/FlyImageDecoder.m ================================================ // // FlyImageDecoder.m // Demo // // Created by Ye Tong on 3/22/16. // Copyright © 2016 NorrisTong. All rights reserved. // #import "FlyImageDecoder.h" #ifdef FLYIMAGE_WEBP #import "webp/decode.h" #endif static void __ReleaseAsset(void* info, const void* data, size_t size) { if (info != NULL) { CFRelease(info); // will cause dealloc of FlyImageDataFile } } #ifdef FLYIMAGE_WEBP // This gets called when the UIImage gets collected and frees the underlying image. static void free_image_data(void* info, const void* data, size_t size) { if (info != NULL) { WebPFreeDecBuffer(&(((WebPDecoderConfig*)info)->output)); free(info); } if (data != NULL) { free((void*)data); } } #endif @implementation FlyImageDecoder - (UIImage*)iconImageWithBytes:(void*)bytes offset:(size_t)offset length:(size_t)length drawSize:(CGSize)drawSize { // Create CGImageRef whose backing store *is* the mapped image table entry. We avoid a memcpy this way. CGDataProviderRef dataProvider = CGDataProviderCreateWithData(NULL, bytes + offset, length, __ReleaseAsset); CGBitmapInfo bitmapInfo = kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host; NSInteger bitsPerComponent = 8; NSInteger bitsPerPixel = 4 * 8; CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); static NSInteger bytesPerPixel = 4; static float kAlignment = 64; CGFloat screenScale = [FlyImageUtils contentsScale]; size_t bytesPerRow = ceil((drawSize.width * screenScale * bytesPerPixel) / kAlignment) * kAlignment; CGImageRef imageRef = CGImageCreate(drawSize.width * screenScale, drawSize.height * screenScale, bitsPerComponent, bitsPerPixel, bytesPerRow, colorSpace, bitmapInfo, dataProvider, NULL, false, kCGRenderingIntentDefault); CGDataProviderRelease(dataProvider); CGColorSpaceRelease(colorSpace); if (imageRef == nil) { return nil; } UIImage* image = [[UIImage alloc] initWithCGImage:imageRef scale:screenScale orientation:UIImageOrientationUp]; CGImageRelease(imageRef); return image; } - (CGImageRef)imageRefWithFile:(void*)file contentType:(ImageContentType)contentType bytes:(void*)bytes length:(size_t)length { if (contentType == ImageContentTypeUnknown || contentType == ImageContentTypeGif || contentType == ImageContentTypeTiff) { return nil; } // Create CGImageRef whose backing store *is* the mapped image table entry. We avoid a memcpy this way. CGDataProviderRef dataProvider = nil; CGImageRef imageRef = nil; if (contentType == ImageContentTypeJPEG) { CFRetain(file); dataProvider = CGDataProviderCreateWithData(file, bytes, length, __ReleaseAsset); imageRef = CGImageCreateWithJPEGDataProvider(dataProvider, NULL, YES, kCGRenderingIntentDefault); } else if (contentType == ImageContentTypePNG) { CFRetain(file); dataProvider = CGDataProviderCreateWithData(file, bytes, length, __ReleaseAsset); imageRef = CGImageCreateWithPNGDataProvider(dataProvider, NULL, YES, kCGRenderingIntentDefault); } else if (contentType == ImageContentTypeWebP) { #ifdef FLYIMAGE_WEBP // `WebPGetInfo` weill return image width and height int width = 0, height = 0; if (!WebPGetInfo(bytes, length, &width, &height)) { return nil; } WebPDecoderConfig* config = malloc(sizeof(WebPDecoderConfig)); if (!WebPInitDecoderConfig(config)) { return nil; } config->options.no_fancy_upsampling = 1; config->options.bypass_filtering = 1; config->options.use_threads = 1; config->output.colorspace = MODE_RGBA; // Decode the WebP image data into a RGBA value array VP8StatusCode decodeStatus = WebPDecode(bytes, length, config); if (decodeStatus != VP8_STATUS_OK) { return nil; } // Construct UIImage from the decoded RGBA value array uint8_t* data = WebPDecodeRGBA(bytes, length, &width, &height); dataProvider = CGDataProviderCreateWithData(config, data, config->options.scaled_width * config->options.scaled_height * 4, free_image_data); CGColorSpaceRef colorSpaceRef = CGColorSpaceCreateDeviceRGB(); CGBitmapInfo bitmapInfo = kCGBitmapByteOrderDefault | kCGImageAlphaLast; CGColorRenderingIntent renderingIntent = kCGRenderingIntentDefault; imageRef = CGImageCreate(width, height, 8, 32, 4 * width, colorSpaceRef, bitmapInfo, dataProvider, NULL, YES, renderingIntent); #endif } if (dataProvider != nil) { CGDataProviderRelease(dataProvider); } return imageRef; } - (UIImage*)imageWithFile:(void*)file contentType:(ImageContentType)contentType bytes:(void*)bytes length:(size_t)length drawSize:(CGSize)drawSize contentsGravity:(NSString* const)contentsGravity cornerRadius:(CGFloat)cornerRadius { CGImageRef imageRef = [self imageRefWithFile:file contentType:contentType bytes:bytes length:length]; if (imageRef == nil) { return nil; } CGSize imageSize = CGSizeMake(CGImageGetWidth(imageRef), CGImageGetHeight(imageRef)); CGFloat contentsScale = 1; if (drawSize.width < imageSize.width && drawSize.height < imageSize.height) { contentsScale = [FlyImageUtils contentsScale]; } CGSize contextSize = CGSizeMake(drawSize.width * contentsScale, drawSize.height * contentsScale); CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); CGBitmapInfo bitmapInfo = CGImageGetBitmapInfo(imageRef); int infoMask = (bitmapInfo & kCGBitmapAlphaInfoMask); BOOL anyNonAlpha = (infoMask == kCGImageAlphaNone || infoMask == kCGImageAlphaNoneSkipFirst || infoMask == kCGImageAlphaNoneSkipLast); // CGBitmapContextCreate doesn't support kCGImageAlphaNone with RGB. // https://developer.apple.com/library/mac/#qa/qa1037/_index.html if (cornerRadius > 0) { bitmapInfo &= kCGImageAlphaPremultipliedLast; } else if (infoMask == kCGImageAlphaNone && CGColorSpaceGetNumberOfComponents(colorSpace) > 1) { // Unset the old alpha info. bitmapInfo &= ~kCGBitmapAlphaInfoMask; // Set noneSkipFirst. bitmapInfo |= kCGImageAlphaNoneSkipFirst; } // Some PNGs tell us they have alpha but only 3 components. Odd. else if (!anyNonAlpha && CGColorSpaceGetNumberOfComponents(colorSpace) == 3) { // Unset the old alpha info. bitmapInfo &= ~kCGBitmapAlphaInfoMask; bitmapInfo |= kCGImageAlphaPremultipliedFirst; } // It calculates the bytes-per-row based on the bitsPerComponent and width arguments. static NSInteger bytesPerPixel = 4; static float kAlignment = 64; size_t bytesPerRow = ceil((contextSize.width * bytesPerPixel) / kAlignment) * kAlignment; CGContextRef context = CGBitmapContextCreate(NULL, contextSize.width, contextSize.height, CGImageGetBitsPerComponent(imageRef), bytesPerRow, colorSpace, bitmapInfo); CGColorSpaceRelease(colorSpace); // If failed, return undecompressed image if (!context) { UIImage* image = [[UIImage alloc] initWithCGImage:imageRef scale:contentsScale orientation:UIImageOrientationUp]; CGImageRelease(imageRef); return image; } CGContextScaleCTM(context, contentsScale, contentsScale); CGContextSetInterpolationQuality(context, kCGInterpolationHigh); CGRect contextBounds = CGRectMake(0, 0, drawSize.width, drawSize.height); // Clip to a rounded rect if (cornerRadius > 0) { CGPathRef path = _FICDCreateRoundedRectPath(contextBounds, cornerRadius); CGContextAddPath(context, path); CFRelease(path); CGContextEOClip(context); } CGContextDrawImage(context, _FlyImageCalcDrawBounds(imageSize, drawSize, contentsGravity), imageRef); CGImageRef decompressedImageRef = CGBitmapContextCreateImage(context); CGContextRelease(context); UIImage* decompressedImage = [UIImage imageWithCGImage:decompressedImageRef scale:contentsScale orientation:UIImageOrientationUp]; CGImageRelease(decompressedImageRef); CGImageRelease(imageRef); return decompressedImage; } #ifdef FLYIMAGE_WEBP - (UIImage*)imageWithWebPData:(NSData*)imageData hasAlpha:(BOOL*)hasAlpha { // `WebPGetInfo` weill return image width and height int width = 0, height = 0; if (!WebPGetInfo(imageData.bytes, imageData.length, &width, &height)) { return nil; } WebPDecoderConfig* config = malloc(sizeof(WebPDecoderConfig)); if (!WebPInitDecoderConfig(config)) { return nil; } config->options.no_fancy_upsampling = 1; config->options.bypass_filtering = 1; config->options.use_threads = 1; config->output.colorspace = MODE_RGBA; // Decode the WebP image data into a RGBA value array VP8StatusCode decodeStatus = WebPDecode(imageData.bytes, imageData.length, config); if (decodeStatus != VP8_STATUS_OK) { return nil; } // set alpha value if (hasAlpha != nil) { *hasAlpha = config->input.has_alpha; } // Construct UIImage from the decoded RGBA value array uint8_t* data = WebPDecodeRGBA(imageData.bytes, imageData.length, &width, &height); CGDataProviderRef dataProvider = CGDataProviderCreateWithData(config, data, config->options.scaled_width * config->options.scaled_height * 4, free_image_data); CGColorSpaceRef colorSpaceRef = CGColorSpaceCreateDeviceRGB(); CGBitmapInfo bitmapInfo = kCGBitmapByteOrderDefault | kCGImageAlphaLast; CGColorRenderingIntent renderingIntent = kCGRenderingIntentDefault; CGImageRef imageRef = CGImageCreate(width, height, 8, 32, 4 * width, colorSpaceRef, bitmapInfo, dataProvider, NULL, YES, renderingIntent); UIImage* decodeImage = [UIImage imageWithCGImage:imageRef]; UIGraphicsBeginImageContextWithOptions(decodeImage.size, !config->input.has_alpha, 1); [decodeImage drawInRect:CGRectMake(0, 0, decodeImage.size.width, decodeImage.size.height)]; UIImage* decompressedImage = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); return decompressedImage; } #endif @end ================================================ FILE: FlyImage/Core/FlyImageDownloader.h ================================================ // // FlyImageDownloader.h // Demo // // Created by Ye Tong on 3/22/16. // Copyright © 2016 NorrisTong. All rights reserved. // #import typedef void (^FlyImageDownloadProgressBlock)(float progress); typedef void (^FlyImageDownloadSuccessBlock)(NSURLRequest* request, NSURL* filePath); typedef void (^FlyImageDownloadFailedBlock)(NSURLRequest* request, NSError* error); typedef NSUUID FlyImageDownloadHandlerId; // Unique ID of handler @class FlyImageDownloader; @protocol FlyImageDownloaderDelegate @optional /** * Callback before sending request. */ - (void)FlyImageDownloader:(FlyImageDownloader*)manager willSendRequest:(NSURLRequest*)request; /** * Callback after complete download. */ - (void)FlyImageDownloader:(FlyImageDownloader*)manager didReceiveResponse:(NSURLResponse*)response filePath:(NSURL*)filePath error:(NSError*)error request:(NSURLRequest*)request; /** * Callback after cancel some request. */ - (void)FlyImageDownloader:(FlyImageDownloader*)manager willCancelRequest:(NSURLRequest*)request; @end @interface FlyImageDownloader : NSObject @property (nonatomic, weak) id delegate; @property (nonatomic, copy) NSString* destinationPath; @property (nonatomic, assign) NSInteger maxDownloadingCount; // Default is 5; /** * Create a FlyImageDownloader with a default destination path. */ + (instancetype)sharedInstance; /** * Create a FlyImageDownloader with a specific destination path. */ - (instancetype)initWithDestinationPath:(NSString*)destinationPath; - (FlyImageDownloadHandlerId*)downloadImageForURLRequest:(NSURLRequest*)request; - (FlyImageDownloadHandlerId*)downloadImageForURLRequest:(NSURLRequest*)request success:(FlyImageDownloadSuccessBlock)success failed:(FlyImageDownloadFailedBlock)failed; /** * Send a download request with callbacks */ - (FlyImageDownloadHandlerId*)downloadImageForURLRequest:(NSURLRequest*)request progress:(FlyImageDownloadProgressBlock)progress success:(FlyImageDownloadSuccessBlock)success failed:(FlyImageDownloadFailedBlock)failed; /** * Cancel a downloading request. * * @param handlerId can't be nil */ - (void)cancelDownloadHandler:(FlyImageDownloadHandlerId*)handlerId; @end ================================================ FILE: FlyImage/Core/FlyImageDownloader.m ================================================ // // FlyImageDownloader.m // Demo // // Created by Ye Tong on 3/22/16. // Copyright © 2016 NorrisTong. All rights reserved. // #import "FlyImageDownloader.h" #import "AFNetworking.h" #import "FlyImageUtils.h" #import "FlyImageDataFileManager.h" #import "FlyImageCache.h" #import #import @interface FlyImageDownloaderResponseHandler : NSObject @property (nonatomic, strong) NSUUID* uuid; @property (nonatomic, strong) FlyImageDownloadProgressBlock processingBlock; @property (nonatomic, strong) FlyImageDownloadSuccessBlock successBlock; @property (nonatomic, strong) FlyImageDownloadFailedBlock failedBlock; @end @implementation FlyImageDownloaderResponseHandler - (instancetype)initWithUUID:(NSUUID*)uuid progress:(FlyImageDownloadProgressBlock)progress success:(FlyImageDownloadSuccessBlock)success failed:(FlyImageDownloadFailedBlock)failed { if (self = [self init]) { self.uuid = uuid; self.processingBlock = progress; self.successBlock = success; self.failedBlock = failed; } return self; } @end @interface FlyImageDownloaderMergedTask : NSObject @property (nonatomic, strong) NSString* identifier; @property (nonatomic, strong) NSMutableArray* handlers; @property (nonatomic, strong) NSURLSessionDownloadTask* task; @end @implementation FlyImageDownloaderMergedTask - (instancetype)initWithIdentifier:(NSString*)identifier task:(NSURLSessionDownloadTask*)task { if (self = [self init]) { self.identifier = identifier; self.task = task; self.handlers = [[NSMutableArray alloc] init]; } return self; } - (void)addResponseHandler:(FlyImageDownloaderResponseHandler*)handler { [self.handlers addObject:handler]; } - (void)removeResponseHandler:(FlyImageDownloaderResponseHandler*)handler { [self.handlers removeObject:handler]; } - (void)clearHandlers { [self.handlers removeAllObjects]; } @end @interface NSString (Extension) - (NSString*)md5; @end @implementation NSString (Extension) - (NSString*)md5 { const char* cStr = [self UTF8String]; unsigned char result[CC_MD5_DIGEST_LENGTH]; CC_MD5(cStr, (CC_LONG)strlen(cStr), result); // This is the md5 call return [NSString stringWithFormat:@"%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x", result[0], result[1], result[2], result[3], result[4], result[5], result[6], result[7], result[8], result[9], result[10], result[11], result[12], result[13], result[14], result[15]]; } @end @interface FlyImageDownloader () @property (nonatomic, strong) NSMutableDictionary* downloadFile; @property (nonatomic, strong) NSMutableDictionary* mergedTasks; @property (nonatomic, strong) NSMutableArray* queuedMergedTasks; @property (nonatomic, assign) NSInteger activeRequestCount; @property (nonatomic, strong) dispatch_queue_t synchronizationQueue; @property (nonatomic, strong) dispatch_queue_t responseQueue; @end @implementation FlyImageDownloader { AFURLSessionManager* _sessionManager; } + (instancetype)sharedInstance { static dispatch_once_t onceToken; static FlyImageDownloader* __instance = nil; dispatch_once(&onceToken, ^{ NSString *folderPath = [FlyImageCache sharedInstance].dataFileManager.folderPath; __instance = [[[self class] alloc] initWithDestinationPath:folderPath]; }); return __instance; } - (instancetype)initWithDestinationPath:(NSString*)destinationPath { if (self = [self init]) { _maxDownloadingCount = 5; _mergedTasks = [[NSMutableDictionary alloc] initWithCapacity:_maxDownloadingCount]; _queuedMergedTasks = [[NSMutableArray alloc] initWithCapacity:_maxDownloadingCount]; _destinationPath = [destinationPath copy]; NSString* name = [NSString stringWithFormat:@"com.flyimage.imagedownloader.synchronizationqueue-%@", [[NSUUID UUID] UUIDString]]; self.synchronizationQueue = dispatch_queue_create([name cStringUsingEncoding:NSASCIIStringEncoding], DISPATCH_QUEUE_SERIAL); name = [NSString stringWithFormat:@"com.flyimage.imagedownloader.responsequeue-%@", [[NSUUID UUID] UUIDString]]; self.responseQueue = dispatch_queue_create([name cStringUsingEncoding:NSASCIIStringEncoding], DISPATCH_QUEUE_CONCURRENT); NSString* configurationIdentifier = [NSString stringWithFormat:@"com.flyimage.downloadsession.%@", [[NSUUID UUID] UUIDString]]; NSURLSessionConfiguration* configuration = [FlyImageDownloader configurationWithIdentifier:configurationIdentifier]; _sessionManager = [[AFURLSessionManager alloc] initWithSessionConfiguration:configuration]; } return self; } + (NSURLSessionConfiguration*)configurationWithIdentifier:(NSString*)identifier { NSURLSessionConfiguration* configuration; #if (defined(__IPHONE_OS_VERSION_MIN_REQUIRED) && __IPHONE_OS_VERSION_MIN_REQUIRED >= 80000) || (defined(__MAC_OS_X_VERSION_MIN_REQUIRED) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 1100) configuration = [NSURLSessionConfiguration defaultSessionConfiguration]; #else configuration = [NSURLSessionConfiguration backgroundSessionConfiguration:identifier]; #endif configuration.HTTPShouldSetCookies = YES; configuration.HTTPShouldUsePipelining = NO; configuration.requestCachePolicy = NSURLRequestUseProtocolCachePolicy; configuration.allowsCellularAccess = YES; return configuration; } - (FlyImageDownloadHandlerId*)downloadImageForURLRequest:(NSURLRequest*)request { return [self downloadImageForURLRequest:request progress:nil success:nil failed:nil]; } - (FlyImageDownloadHandlerId*)downloadImageForURLRequest:(NSURLRequest*)request success:(FlyImageDownloadSuccessBlock)success failed:(FlyImageDownloadFailedBlock)failed { return [self downloadImageForURLRequest:request progress:nil success:success failed:failed]; } - (FlyImageDownloadHandlerId*)downloadImageForURLRequest:(NSURLRequest*)request progress:(FlyImageDownloadProgressBlock)progress success:(FlyImageDownloadSuccessBlock)success failed:(FlyImageDownloadFailedBlock)failed { NSParameterAssert(request != nil); __block FlyImageDownloadHandlerId* handlerId = nil; dispatch_sync(_synchronizationQueue, ^{ if (request.URL.absoluteString == nil) { if (failed) { NSError *error = [NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorBadURL userInfo:nil]; dispatch_main_sync_safe(^{ failed(request, error); }); } return; } NSString *identifier = [request.URL.absoluteString md5]; handlerId = [NSUUID UUID]; // 1) Append the success and failure blocks to a pre-existing request if it already exists FlyImageDownloaderMergedTask *existingMergedTask = self.mergedTasks[identifier]; if (existingMergedTask != nil) { FlyImageDownloaderResponseHandler *handler = [[FlyImageDownloaderResponseHandler alloc] initWithUUID:handlerId progress:progress success:success failed:failed]; [existingMergedTask addResponseHandler:handler]; return; } __weak __typeof__(self) weakSelf = self; NSURLSessionDownloadTask *task = [_sessionManager downloadTaskWithRequest:request progress:^(NSProgress * _Nonnull downloadProgress) { dispatch_async(weakSelf.responseQueue, ^{ FlyImageDownloaderMergedTask *existingMergedTask = weakSelf.mergedTasks[identifier]; for (FlyImageDownloaderResponseHandler *hanlder in existingMergedTask.handlers) { if ( hanlder.processingBlock != nil ) { dispatch_main_async_safe(^{ hanlder.processingBlock( downloadProgress.fractionCompleted ); }); } } }); } destination:^NSURL *(NSURL *targetPath, NSURLResponse *response) { return [NSURL fileURLWithPath:[_destinationPath stringByAppendingPathComponent:identifier]]; } completionHandler:^(NSURLResponse *response, NSURL *filePath, NSError *error) { dispatch_async(weakSelf.responseQueue, ^{ __strong __typeof__(weakSelf) strongSelf = weakSelf; if ( [_delegate respondsToSelector:@selector(FlyImageDownloader:didReceiveResponse:filePath:error:request:)] ) { dispatch_main_sync_safe(^{ [_delegate FlyImageDownloader:strongSelf didReceiveResponse:response filePath:filePath error:error request:request]; }); } FlyImageDownloaderMergedTask *mergedTask = strongSelf.mergedTasks[identifier]; if (error != nil) { for (FlyImageDownloaderResponseHandler *handler in mergedTask.handlers) { if (handler.failedBlock) { handler.failedBlock(request, error); } } // remove error file [[NSFileManager defaultManager] removeItemAtURL:filePath error:nil]; }else{ for (FlyImageDownloaderResponseHandler *handler in mergedTask.handlers) { if (handler.successBlock) { handler.successBlock(request, filePath); } } } // remove exist task [strongSelf.mergedTasks removeObjectForKey:identifier]; [strongSelf safelyDecrementActiveTaskCount]; [strongSelf safelyStartNextTaskIfNecessary]; }); }]; // 4) Store the response handler for use when the request completes existingMergedTask = [[FlyImageDownloaderMergedTask alloc] initWithIdentifier:identifier task:task]; self.mergedTasks[ identifier ] = existingMergedTask; FlyImageDownloaderResponseHandler *handler = [[FlyImageDownloaderResponseHandler alloc] initWithUUID:handlerId progress:progress success:success failed:failed]; [existingMergedTask addResponseHandler:handler]; // 5) Either start the request or enqueue it depending on the current active request count if ([self isActiveRequestCountBelowMaximumLimit]) { [self startMergedTask:existingMergedTask]; } else { [self enqueueMergedTask:existingMergedTask]; } }); return handlerId; } - (void)cancelDownloadHandler:(FlyImageDownloadHandlerId*)handlerId { NSParameterAssert(handlerId != nil); dispatch_sync(_synchronizationQueue, ^{ FlyImageDownloaderMergedTask *matchedTask = nil; FlyImageDownloaderResponseHandler *matchedHandler = nil; for (NSString *URLIdentifier in self.mergedTasks) { FlyImageDownloaderMergedTask *mergedTask = self.mergedTasks[ URLIdentifier ]; for (FlyImageDownloaderResponseHandler *handler in mergedTask.handlers) { if ( [handler.uuid isEqual:handlerId] ) { matchedHandler = handler; matchedTask = mergedTask; break; } } } if ( matchedTask == nil ) { for (FlyImageDownloaderMergedTask *mergedTask in _queuedMergedTasks) { for (FlyImageDownloaderResponseHandler *handler in mergedTask.handlers) { if ( [handler.uuid isEqual:handlerId] ) { matchedHandler = handler; matchedTask = mergedTask; break; } } } } if ( matchedTask == nil || matchedHandler == nil ) { return; } [matchedTask removeResponseHandler:matchedHandler]; NSError *error = [NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorCancelled userInfo:nil]; dispatch_main_sync_safe(^{ if ( matchedHandler.failedBlock != nil ){ matchedHandler.failedBlock(nil, error); } }); // remove this task from both merged and queued tasks if (matchedTask.handlers.count == 0) { if ( [_delegate respondsToSelector:@selector(FlyImageDownloader:willCancelRequest:)] ) { dispatch_main_sync_safe(^{ [_delegate FlyImageDownloader:self willCancelRequest:matchedTask.task.originalRequest]; }); } [matchedTask.task cancel]; [self.mergedTasks removeObjectForKey:matchedTask.identifier]; [_queuedMergedTasks removeObject:matchedTask]; } }); } - (BOOL)isActiveRequestCountBelowMaximumLimit { return self.activeRequestCount < self.maxDownloadingCount; } - (void)startMergedTask:(FlyImageDownloaderMergedTask*)mergedTask { if ([_delegate respondsToSelector:@selector(FlyImageDownloader:willSendRequest:)]) { dispatch_main_sync_safe(^{ [_delegate FlyImageDownloader:self willSendRequest:mergedTask.task.originalRequest]; }); } [mergedTask.task resume]; ++self.activeRequestCount; } - (void)enqueueMergedTask:(FlyImageDownloaderMergedTask*)mergedTask { // default is AFImageDownloadPrioritizationLIFO [_queuedMergedTasks insertObject:mergedTask atIndex:0]; } - (FlyImageDownloaderMergedTask*)dequeueMergedTask { FlyImageDownloaderMergedTask* mergedTask = nil; mergedTask = [_queuedMergedTasks lastObject]; [self.queuedMergedTasks removeObject:mergedTask]; return mergedTask; } - (void)safelyDecrementActiveTaskCount { dispatch_sync(_synchronizationQueue, ^{ if (self.activeRequestCount > 0) { self.activeRequestCount -= 1; } }); } - (void)safelyStartNextTaskIfNecessary { dispatch_sync(_synchronizationQueue, ^{ while ([self isActiveRequestCountBelowMaximumLimit] && [_queuedMergedTasks count] > 0 ) { FlyImageDownloaderMergedTask *mergedTask = [self dequeueMergedTask]; [self startMergedTask:mergedTask]; } }); } @end ================================================ FILE: FlyImage/Core/FlyImageEncoder.h ================================================ // // FlyImageEncoder.h // Demo // // Created by Ye Tong on 3/22/16. // Copyright © 2016 NorrisTong. All rights reserved. // #import typedef void (^FFlyImageEncoderDrawingBlock)(CGContextRef context, CGRect contextBounds); /** * Convert an image to bitmap format. */ @interface FlyImageEncoder : NSObject /** * Draw an image, and save the bitmap data into memory buffer. * * @param size image size * @param bytes memory buffer * @param drawingBlock drawing function */ - (void)encodeWithImageSize:(CGSize)size bytes:(void*)bytes drawingBlock:(FFlyImageEncoderDrawingBlock)drawingBlock; /** * Calculate buffer size with image size. * * @param size image size */ + (size_t)dataLengthWithImageSize:(CGSize)size; @end ================================================ FILE: FlyImage/Core/FlyImageEncoder.m ================================================ // // FlyImageEncoder.m // Demo // // Created by Ye Tong on 3/22/16. // Copyright © 2016 NorrisTong. All rights reserved. // #import "FlyImageEncoder.h" #import "FlyImageUtils.h" @implementation FlyImageEncoder static NSInteger __bytesPerPixel = 4; static NSInteger __bitsPerComponent = 8; static float __alignmentSize = 64; - (void)encodeWithImageSize:(CGSize)size bytes:(void*)bytes drawingBlock:(FFlyImageEncoderDrawingBlock)drawingBlock { CGFloat screenScale = [FlyImageUtils contentsScale]; CGSize pixelSize = CGSizeMake(size.width * screenScale, size.height * screenScale); // It calculates the bytes-per-row based on the __bitsPerComponent and width arguments. size_t bytesPerRow = ceil((pixelSize.width * __bytesPerPixel) / __alignmentSize) * __alignmentSize; CGBitmapInfo bitmapInfo = kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host; CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); CGContextRef context = CGBitmapContextCreate(bytes, pixelSize.width, pixelSize.height, __bitsPerComponent, bytesPerRow, colorSpace, bitmapInfo); CGColorSpaceRelease(colorSpace); CGContextTranslateCTM(context, 0, pixelSize.height); CGContextScaleCTM(context, 1, -1); // Call drawing block to allow client to draw into the context CGRect contextBounds = CGRectZero; contextBounds.size = pixelSize; CGContextClearRect(context, contextBounds); drawingBlock(context, contextBounds); CGContextRelease(context); } + (size_t)dataLengthWithImageSize:(CGSize)size { CGFloat screenScale = [FlyImageUtils contentsScale]; CGSize pixelSize = CGSizeMake(size.width * screenScale, size.height * screenScale); size_t bytesPerRow = ceil((pixelSize.width * __bytesPerPixel) / __alignmentSize) * __alignmentSize; CGFloat imageLength = bytesPerRow * (NSInteger)pixelSize.height; int pageSize = [FlyImageUtils pageSize]; size_t bytesToAppend = ceil(imageLength / pageSize) * pageSize; return bytesToAppend; } @end ================================================ FILE: FlyImage/Core/FlyImageIconCache.h ================================================ // // FlyImageIconCache.h // Demo // // Created by Norris Tong on 4/2/16. // Copyright © 2016 NorrisTong. All rights reserved. // #import #import "FlyImageCacheProtocol.h" /** * Draw the icon in a background thread. * * @param context drawing context * @param contextBounds image size */ typedef void (^FlyImageCacheDrawingBlock)(CGContextRef context, CGRect contextBounds); /** * FlyImageIconCache will draw icons into one big file, * and will get great performace when try to render multiple icons in one screen. */ @interface FlyImageIconCache : NSObject /** * Add an icon into the FlyImageIconCache. * * @param key unique key * @param size image size * @param drawingBlock * @param completed callback after add, can be nil */ - (void)addImageWithKey:(NSString*)key size:(CGSize)size drawingBlock:(FlyImageCacheDrawingBlock)drawingBlock completed:(FlyImageCacheRetrieveBlock)completed; /** * FlyImageIconCache not support remove an icon from the cache, but you can replace an icon with the same key. * But the new image must has the same size with the previous one. * * @param key unique key * @param drawingBlock * @param completed callback after replace, can be nil */ - (void)replaceImageWithKey:(NSString*)key drawingBlock:(FlyImageCacheDrawingBlock)drawingBlock completed:(FlyImageCacheRetrieveBlock)completed; @end ================================================ FILE: FlyImage/Core/FlyImageIconCache.m ================================================ // // FlyImageIconCache.m // Demo // // Created by Norris Tong on 4/2/16. // Copyright © 2016 NorrisTong. All rights reserved. // #import "FlyImageIconCache.h" #import "FlyImageDataFileManager.h" #import "FlyImageUtils.h" #import "FlyImageEncoder.h" #import "FlyImageDecoder.h" #import "FlyImageRetrieveOperation.h" static NSString* kFlyImageKeyVersion = @"v"; static NSString* kFlyImageKeyFile = @"f"; static NSString* kFlyImageKeyImages = @"i"; static NSString* kFlyImageKeyFilePointer = @"p"; #define kImageInfoIndexWidth 0 #define kImageInfoIndexHeight 1 #define kImageInfoIndexOffset 2 #define kImageInfoIndexLength 3 #define kImageInfoCount 4 @interface FlyImageIconCache () @property (nonatomic, strong) FlyImageEncoder* encoder; @property (nonatomic, strong) FlyImageDecoder* decoder; @property (nonatomic, strong) FlyImageDataFile* dataFile; @property (nonatomic, strong) FlyImageDataFileManager* dataFileManager; @end @implementation FlyImageIconCache { NSRecursiveLock* _lock; NSString* _metaPath; NSMutableDictionary* _metas; NSMutableDictionary* _images; NSMutableDictionary* _addingImages; NSOperationQueue* _retrievingQueue; } + (instancetype)sharedInstance { static dispatch_once_t onceToken; static FlyImageIconCache* __instance = nil; dispatch_once(&onceToken, ^{ NSString *metaPath = [[FlyImageUtils directoryPath] stringByAppendingPathComponent:@"/__icons"]; __instance = [[[self class] alloc] initWithMetaPath:metaPath]; }); return __instance; } - (instancetype)initWithMetaPath:(NSString*)metaPath { if (self = [self init]) { _lock = [[NSRecursiveLock alloc] init]; _addingImages = [[NSMutableDictionary alloc] init]; _retrievingQueue = [NSOperationQueue new]; _retrievingQueue.qualityOfService = NSQualityOfServiceUserInteractive; _retrievingQueue.maxConcurrentOperationCount = 6; _metaPath = [metaPath copy]; NSString* folderPath = [[_metaPath stringByDeletingLastPathComponent] stringByAppendingPathComponent:@"/files"]; self.dataFileManager = [[FlyImageDataFileManager alloc] initWithFolderPath:folderPath]; [self loadMetadata]; _decoder = [[FlyImageDecoder alloc] init]; _encoder = [[FlyImageEncoder alloc] init]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onWillTerminate) name:UIApplicationWillTerminateNotification object:nil]; } return self; } #pragma mark - LifeCircle - (void)onWillTerminate { // 取消内存映射 _dataFile = nil; [_retrievingQueue cancelAllOperations]; } - (void)dealloc { _dataFile = nil; [_retrievingQueue cancelAllOperations]; } #pragma mark - APIs - (void)addImageWithKey:(NSString*)key size:(CGSize)size drawingBlock:(FlyImageCacheDrawingBlock)drawingBlock completed:(FlyImageCacheRetrieveBlock)completed { NSParameterAssert(key != nil); NSParameterAssert(drawingBlock != nil); if ([self isImageExistWithKey:key]) { [self asyncGetImageWithKey:key completed:completed]; return; } size_t bytesToAppend = [FlyImageEncoder dataLengthWithImageSize:size]; [self doAddImageWithKey:key size:size offset:-1 length:bytesToAppend drawingBlock:drawingBlock completed:completed]; } - (void)doAddImageWithKey:(NSString*)key size:(CGSize)size offset:(size_t)offset length:(size_t)length drawingBlock:(FlyImageCacheDrawingBlock)drawingBlock completed:(FlyImageCacheRetrieveBlock)completed { NSParameterAssert(completed != nil); if (_dataFile == nil) { if (completed != nil) { completed(key, nil); } return; } if (completed != nil) { @synchronized(_addingImages) { if ([_addingImages objectForKey:key] == nil) { [_addingImages setObject:[NSMutableArray arrayWithObject:completed] forKey:key]; } else { NSMutableArray* blocks = [_addingImages objectForKey:key]; [blocks addObject:completed]; return; } } } static dispatch_queue_t __drawingQueue = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ NSString *name = [NSString stringWithFormat:@"com.flyimage.drawicon.%@", [[NSUUID UUID] UUIDString]]; __drawingQueue = dispatch_queue_create([name cStringUsingEncoding:NSASCIIStringEncoding], NULL); }); // 使用dispatch_sync 代替 dispatch_async,防止大规模写入时出现异常 dispatch_async(__drawingQueue, ^{ size_t newOffset = offset == -1 ? (size_t)_dataFile.pointer : offset; if ( ![_dataFile prepareAppendDataWithOffset:newOffset length:length] ) { [self afterAddImage:nil key:key]; return; } [_encoder encodeWithImageSize:size bytes:_dataFile.address + newOffset drawingBlock:drawingBlock]; BOOL success = [_dataFile appendDataWithOffset:newOffset length:length]; if ( !success ) { // TODO: consider rollback [self afterAddImage:nil key:key]; return; } // number of dataFile, width of image, height of image, offset, length @synchronized (_images) { NSArray *imageInfo = @[ @(size.width), @(size.height), @(newOffset), @(length) ]; [_images setObject:imageInfo forKey:key]; } // callback with image UIImage *image = [_decoder iconImageWithBytes:_dataFile.address offset:newOffset length:length drawSize:size]; [self afterAddImage:image key:key]; // save meta [self saveMetadata]; }); } - (void)afterAddImage:(UIImage*)image key:(NSString*)key { NSArray* blocks = nil; @synchronized(_addingImages) { blocks = [[_addingImages objectForKey:key] copy]; [_addingImages removeObjectForKey:key]; } dispatch_main_sync_safe(^{ for ( FlyImageCacheRetrieveBlock block in blocks) { block( key, image ); } }); } - (void)replaceImageWithKey:(NSString*)key drawingBlock:(FlyImageCacheDrawingBlock)drawingBlock completed:(FlyImageCacheRetrieveBlock)completed { NSParameterAssert(key != nil); NSParameterAssert(drawingBlock != nil); id imageInfo = nil; @synchronized(_images) { imageInfo = _images[key]; } if (imageInfo == nil) { if (completed != nil) { completed(key, nil); } return; } // width of image, height of image, offset, length CGFloat imageWidth = [[imageInfo objectAtIndex:kImageInfoIndexWidth] floatValue]; CGFloat imageHeight = [[imageInfo objectAtIndex:kImageInfoIndexHeight] floatValue]; size_t imageOffset = [[imageInfo objectAtIndex:kImageInfoIndexOffset] unsignedLongValue]; size_t imageLength = [[imageInfo objectAtIndex:kImageInfoIndexLength] unsignedLongValue]; CGSize size = CGSizeMake(imageWidth, imageHeight); [self doAddImageWithKey:key size:size offset:imageOffset length:imageLength drawingBlock:drawingBlock completed:completed]; } - (void)removeImageWithKey:(NSString*)key { @synchronized(_images) { [_images removeObjectForKey:key]; } } - (void)changeImageKey:(NSString*)oldKey newKey:(NSString*)newKey { @synchronized(_images) { id imageInfo = [_images objectForKey:oldKey]; if (imageInfo == nil) { return; } [_images setObject:imageInfo forKey:newKey]; [_images removeObjectForKey:oldKey]; } } - (BOOL)isImageExistWithKey:(NSString*)key { @synchronized(_images) { return [_images objectForKey:key] != nil; } } - (void)asyncGetImageWithKey:(NSString*)key completed:(FlyImageCacheRetrieveBlock)completed { NSParameterAssert(key != nil); NSParameterAssert(completed != nil); if (_dataFile == nil) { completed(key, nil); return; } NSArray* imageInfo; @synchronized(_images) { imageInfo = _images[key]; } if (imageInfo == nil || [imageInfo count] < kImageInfoCount) { completed(key, nil); return; } // width of image, height of image, offset, length CGFloat imageWidth = [[imageInfo objectAtIndex:kImageInfoIndexWidth] floatValue]; CGFloat imageHeight = [[imageInfo objectAtIndex:kImageInfoIndexHeight] floatValue]; size_t imageOffset = [[imageInfo objectAtIndex:kImageInfoIndexOffset] unsignedLongValue]; size_t imageLength = [[imageInfo objectAtIndex:kImageInfoIndexLength] unsignedLongValue]; __weak __typeof__(self) weakSelf = self; FlyImageRetrieveOperation* operation = [[FlyImageRetrieveOperation alloc] initWithRetrieveBlock:^UIImage * { return [weakSelf.decoder iconImageWithBytes:weakSelf.dataFile.address offset:imageOffset length:imageLength drawSize:CGSizeMake(imageWidth, imageHeight)]; }]; operation.name = key; [operation addBlock:completed]; [_retrievingQueue addOperation:operation]; } - (void)cancelGetImageWithKey:(NSString*)key { NSParameterAssert(key != nil); for (FlyImageRetrieveOperation* operation in _retrievingQueue.operations) { if (!operation.cancelled && !operation.finished && [operation.name isEqualToString:key]) { [operation cancel]; return; } } } - (void)purge { [self removeImages]; _dataFile = nil; NSString* fileName = [_metas objectForKey:kFlyImageKeyFile]; if (fileName != nil) { [self.dataFileManager removeFileWithName:fileName]; [self createDataFile:fileName]; } [self saveMetadata]; } - (void)removeImages { @synchronized(_images) { [_images removeAllObjects]; } [_retrievingQueue cancelAllOperations]; @synchronized(_addingImages) { for (NSString* key in _addingImages) { NSArray* blocks = [_addingImages objectForKey:key]; dispatch_main_sync_safe(^{ for ( FlyImageCacheRetrieveBlock block in blocks) { block( key, nil ); } }); } [_addingImages removeAllObjects]; } } #pragma mark - Working with Metadata - (void)saveMetadata { static dispatch_queue_t __metadataQueue = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ NSString *name = [NSString stringWithFormat:@"com.flyimage.iconmeta.%@", [[NSUUID UUID] UUIDString]]; __metadataQueue = dispatch_queue_create([name cStringUsingEncoding:NSASCIIStringEncoding], NULL); }); dispatch_async(__metadataQueue, ^{ [_lock lock]; NSData *data = [NSJSONSerialization dataWithJSONObject:[_metas copy] options:kNilOptions error:NULL]; BOOL fileWriteResult = [data writeToFile:_metaPath atomically:NO]; if (fileWriteResult == NO) { FlyImageErrorLog(@"couldn't save metadata"); } [_lock unlock]; }); } - (void)loadMetadata { // load content from index file NSError* error; NSData* metadataData = [NSData dataWithContentsOfURL:[NSURL fileURLWithPath:_metaPath] options:NSDataReadingMappedAlways error:&error]; if (error != nil || metadataData == nil) { [self createMetadata]; return; } NSDictionary* parsedObject = (NSDictionary*)[NSJSONSerialization JSONObjectWithData:metadataData options:kNilOptions error:&error]; if (error != nil || parsedObject == nil) { [self createMetadata]; return; } // 客户端升级后,图标极有可能发生变化,为了适应这种变化,自动清理本地缓存,所有图标都重新生成 NSString* lastVersion = [parsedObject objectForKey:kFlyImageKeyVersion]; NSString* currentVersion = [FlyImageUtils clientVersion]; if (lastVersion != nil && ![lastVersion isEqualToString:currentVersion]) { [self purge]; [self createMetadata]; return; } // load infos _metas = [NSMutableDictionary dictionaryWithDictionary:parsedObject]; _images = [NSMutableDictionary dictionaryWithDictionary:[_metas objectForKey:kFlyImageKeyImages]]; [_metas setObject:_images forKey:kFlyImageKeyImages]; NSString* fileName = [_metas objectForKey:kFlyImageKeyFile]; [self createDataFile:fileName]; } - (void)createMetadata { _metas = [NSMutableDictionary dictionaryWithCapacity:100]; // 记录当前版本号 NSString* currentVersion = [FlyImageUtils clientVersion]; if (currentVersion != nil) { [_metas setObject:currentVersion forKey:kFlyImageKeyVersion]; } // images _images = [NSMutableDictionary dictionary]; [_metas setObject:_images forKey:kFlyImageKeyImages]; // file NSString* fileName = [[NSUUID UUID] UUIDString]; [_metas setObject:fileName forKey:kFlyImageKeyFile]; [self createDataFile:fileName]; } - (void)createDataFile:(NSString*)fileName { _dataFile = [self.dataFileManager createFileWithName:fileName]; _dataFile.step = [FlyImageUtils pageSize] * 128; // 512KB [_dataFile open]; } @end ================================================ FILE: FlyImage/Core/FlyImageRetrieveOperation.h ================================================ // // FlyImageRetrieveOperation.h // FlyImage // // Created by Ye Tong on 8/11/16. // Copyright © 2016 Ye Tong. All rights reserved. // #import #import "FlyImageCacheProtocol.h" typedef UIImage* (^RetrieveOperationBlock)(void); /** * Internal class. In charge of retrieving and sending UIImage. */ @interface FlyImageRetrieveOperation : NSOperation /** * When the operation start running, the block will be executed, * and require an uncompressed UIImage. */ - (instancetype)initWithRetrieveBlock:(RetrieveOperationBlock)block; /** * Allow to add multiple blocks * * @param block */ - (void)addBlock:(FlyImageCacheRetrieveBlock)block; /** * Callback with result image, which can be nil. */ - (void)executeWithImage:(UIImage*)image; @end ================================================ FILE: FlyImage/Core/FlyImageRetrieveOperation.m ================================================ // // FlyImageRetrieveOperation.m // FlyImage // // Created by Ye Tong on 8/11/16. // Copyright © 2016 Ye Tong. All rights reserved. // #import "FlyImageRetrieveOperation.h" @implementation FlyImageRetrieveOperation { NSMutableArray* _blocks; RetrieveOperationBlock _retrieveBlock; } - (instancetype)initWithRetrieveBlock:(RetrieveOperationBlock)block { if (self = [self init]) { _retrieveBlock = block; } return self; } - (void)addBlock:(FlyImageCacheRetrieveBlock)block { if (_blocks == nil) { _blocks = [NSMutableArray new]; } [_blocks addObject:block]; } - (void)executeWithImage:(UIImage*)image { for (FlyImageCacheRetrieveBlock block in _blocks) { block(self.name, image); } [_blocks removeAllObjects]; } - (void)main { if (self.isCancelled) { return; } UIImage* image = _retrieveBlock(); [self executeWithImage:image]; } - (void)cancel { if (self.isFinished) return; [super cancel]; [self executeWithImage:nil]; } @end ================================================ FILE: FlyImage/Core/FlyImageUtils.h ================================================ // // FlyImageUtils.h // Demo // // Created by Ye Tong on 3/18/16. // Copyright © 2016 NorrisTong. All rights reserved. // #import typedef NS_ENUM(NSInteger, ImageContentType) { ImageContentTypeUnknown, ImageContentTypeJPEG, ImageContentTypePNG, ImageContentTypeWebP, ImageContentTypeGif, ImageContentTypeTiff }; @interface FlyImageUtils : NSObject + (NSString*)directoryPath; + (CGFloat)contentsScale; + (NSString*)clientVersion; /** * Memory page size, default is 4096 */ + (int)pageSize; /** * Compute the content type for an image data * * @param data image data * */ + (ImageContentType)contentTypeForImageData:(NSData*)data; @end /** * Copy from FastImageCache. * * @param rect draw area * @param cornerRadius * */ CGMutablePathRef _FICDCreateRoundedRectPath(CGRect rect, CGFloat cornerRadius); /** * calculate drawing bounds with original image size, target size and contentsGravity of layer. * * @param imageSize * @param targetSize * @param contentsGravity layer's attribute */ CGRect _FlyImageCalcDrawBounds(CGSize imageSize, CGSize targetSize, NSString* const contentsGravity); #define FlyImageErrorLog(fmt, ...) NSLog((@"FlyImage Error: " fmt), ##__VA_ARGS__) #define dispatch_main_sync_safe(block) \ if ([NSThread isMainThread]) { \ block(); \ } else { \ dispatch_sync(dispatch_get_main_queue(), block); \ } #define dispatch_main_async_safe(block) \ if ([NSThread isMainThread]) { \ block(); \ } else { \ dispatch_async(dispatch_get_main_queue(), block); \ } ================================================ FILE: FlyImage/Core/FlyImageUtils.m ================================================ // // FlyImageUtils.m // Demo // // Created by Ye Tong on 3/18/16. // Copyright © 2016 NorrisTong. All rights reserved. // #import "FlyImageUtils.h" @implementation FlyImageUtils + (NSString*)directoryPath { static NSString* __directoryPath = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES); __directoryPath = [[paths objectAtIndex:0] stringByAppendingPathComponent:@"flyImage"]; NSFileManager *fileManager = [[NSFileManager alloc] init]; BOOL directoryExists = [fileManager fileExistsAtPath:__directoryPath]; if (directoryExists == NO) { [fileManager createDirectoryAtPath:__directoryPath withIntermediateDirectories:YES attributes:nil error:nil]; } }); return __directoryPath; } + (CGFloat)contentsScale { static CGFloat __contentsScale = 1; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ __contentsScale = [UIScreen mainScreen].scale; }); return __contentsScale; } + (NSString*)clientVersion { static NSString* __clientVersion = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ NSString *version = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleShortVersionString"]; NSString *build = [[NSBundle mainBundle] infoDictionary][@"CFBundleVersion"]; __clientVersion = [version stringByAppendingString:build]; }); return __clientVersion; } + (int)pageSize { static int __pageSize = 0; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ __pageSize = getpagesize(); }); return __pageSize; } + (ImageContentType)contentTypeForImageData:(NSData*)data { uint8_t c; [data getBytes:&c length:1]; switch (c) { case 0xFF: return ImageContentTypeJPEG; case 0x89: return ImageContentTypePNG; case 0x47: return ImageContentTypeGif; case 0x49: case 0x4D: return ImageContentTypeTiff; case 0x52: // R as RIFF for WEBP if ([data length] < 12) { return ImageContentTypeUnknown; } NSString* testString = [[NSString alloc] initWithData:[data subdataWithRange:NSMakeRange(0, 12)] encoding:NSASCIIStringEncoding]; if ([testString hasPrefix:@"RIFF"] && [testString hasSuffix:@"WEBP"]) { return ImageContentTypeWebP; } return ImageContentTypeUnknown; } return ImageContentTypeUnknown; } // from FastImageCache CGMutablePathRef _FICDCreateRoundedRectPath(CGRect rect, CGFloat cornerRadius) { CGMutablePathRef path = CGPathCreateMutable(); CGFloat minX = CGRectGetMinX(rect); CGFloat midX = CGRectGetMidX(rect); CGFloat maxX = CGRectGetMaxX(rect); CGFloat minY = CGRectGetMinY(rect); CGFloat midY = CGRectGetMidY(rect); CGFloat maxY = CGRectGetMaxY(rect); CGPathMoveToPoint(path, NULL, minX, midY); CGPathAddArcToPoint(path, NULL, minX, maxY, midX, maxY, cornerRadius); CGPathAddArcToPoint(path, NULL, maxX, maxY, maxX, midY, cornerRadius); CGPathAddArcToPoint(path, NULL, maxX, minY, midX, minY, cornerRadius); CGPathAddArcToPoint(path, NULL, minX, minY, minX, midY, cornerRadius); return path; } CGRect _FlyImageCalcDrawBounds(CGSize imageSize, CGSize targetSize, NSString* const contentsGravity) { CGFloat x, y, width, height; if ([contentsGravity isEqualToString:kCAGravityCenter]) { x = (targetSize.width - imageSize.width) / 2; y = (targetSize.height - imageSize.height) / 2; width = imageSize.width; height = imageSize.height; } else if ([contentsGravity isEqualToString:kCAGravityTop]) { x = (targetSize.width - imageSize.width) / 2; y = targetSize.height - imageSize.height; width = imageSize.width; height = imageSize.height; } else if ([contentsGravity isEqualToString:kCAGravityBottom]) { x = (targetSize.width - imageSize.width) / 2; y = 0; width = imageSize.width; height = imageSize.height; } else if ([contentsGravity isEqualToString:kCAGravityLeft]) { x = 0; y = (targetSize.height - imageSize.height) / 2; width = imageSize.width; height = imageSize.height; } else if ([contentsGravity isEqualToString:kCAGravityRight]) { x = targetSize.width - imageSize.width; y = (targetSize.height - imageSize.height) / 2; width = imageSize.width; height = imageSize.height; } else if ([contentsGravity isEqualToString:kCAGravityTopLeft]) { x = 0; y = targetSize.height - imageSize.height; width = imageSize.width; height = imageSize.height; } else if ([contentsGravity isEqualToString:kCAGravityTopRight]) { x = targetSize.width - imageSize.width; y = targetSize.height - imageSize.height; width = imageSize.width; height = imageSize.height; } else if ([contentsGravity isEqualToString:kCAGravityBottomLeft]) { x = 0; y = 0; width = imageSize.width; height = imageSize.height; } else if ([contentsGravity isEqualToString:kCAGravityBottomRight]) { x = targetSize.width - imageSize.width; y = 0; width = imageSize.width; height = imageSize.height; } else if ([contentsGravity isEqualToString:kCAGravityResizeAspectFill]) { CGFloat scaleWidth = targetSize.width / imageSize.width; CGFloat scaleHeight = targetSize.height / imageSize.height; if (scaleWidth < scaleHeight) { y = 0; height = targetSize.height; width = scaleHeight * imageSize.width; x = (targetSize.width - width) / 2; } else { x = 0; width = targetSize.width; height = scaleWidth * imageSize.height; y = (targetSize.height - height) / 2; } } else if ([contentsGravity isEqualToString:kCAGravityResize]) { x = y = 0; width = targetSize.width; height = targetSize.height; } else { // kCAGravityResizeAspect CGFloat scaleWidth = targetSize.width / imageSize.width; CGFloat scaleHeight = targetSize.height / imageSize.height; if (scaleWidth > scaleHeight) { y = 0; height = targetSize.height; width = scaleHeight * imageSize.width; x = (targetSize.width - width) / 2; } else { x = 0; width = targetSize.width; height = scaleWidth * imageSize.height; y = (targetSize.height - height) / 2; } } return CGRectMake(x, y, width, height); } @end ================================================ FILE: FlyImage/FlyImage.h ================================================ // // FlyImage.h // FlyImage // // Created by Ye Tong on 4/18/16. // Copyright © 2016 Ye Tong. All rights reserved. // #import //! Project version number for FlyImage. FOUNDATION_EXPORT double FlyImageVersionNumber; //! Project version string for FlyImage. FOUNDATION_EXPORT const unsigned char FlyImageVersionString[]; #import #import #import #import #import #import #import #import #import #import ================================================ FILE: FlyImage/Info.plist ================================================ CFBundleDevelopmentRegion en CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIdentifier com.flyimage.image CFBundleInfoDictionaryVersion 6.0 CFBundleName $(PRODUCT_NAME) CFBundlePackageType FMWK CFBundleShortVersionString 1.0 CFBundleSignature ???? CFBundleVersion $(CURRENT_PROJECT_VERSION) NSPrincipalClass ================================================ FILE: FlyImage/UI/CALayer+FlyImageCache.h ================================================ // // FlyImageLayer.h // Demo // // Created by Ye Tong on 3/17/16. // Copyright © 2016 NorrisTong. All rights reserved. // #import #import "FlyImageCacheUIProtocol.h" @interface CALayer (FlyImageCache) @end ================================================ FILE: FlyImage/UI/CALayer+FlyImageCache.m ================================================ // // CALayer+FlyImageCache.m // Demo // // Created by Ye Tong on 3/17/16. // Copyright © 2016 NorrisTong. All rights reserved. // #import "CALayer+FlyImageCache.h" #import "FlyImageRenderer.h" #import "FlyImageUtils.h" #import "objc/runtime.h" @interface CALayer (__FlyImageCache) @end @implementation CALayer (FlyImageCache) static char kRendererKey; - (void)setImageURL:(NSURL*)url { [self setPlaceHolderImageName:nil thumbnailURL:nil originalURL:url]; } - (void)setPlaceHolderImageName:(NSString*)imageName thumbnailURL:(NSURL*)thumbnailURL originalURL:(NSURL*)originalURL { FlyImageRenderer* renderer = objc_getAssociatedObject(self, &kRendererKey); if (renderer == nil) { renderer = [[FlyImageRenderer alloc] init]; renderer.delegate = self; self.contentsScale = [FlyImageUtils contentsScale]; self.drawsAsynchronously = YES; objc_setAssociatedObject(self, &kRendererKey, renderer, OBJC_ASSOCIATION_RETAIN_NONATOMIC); } [renderer setPlaceHolderImageName:imageName thumbnailURL:thumbnailURL originalURL:originalURL drawSize:self.bounds.size contentsGravity:self.contentsGravity cornerRadius:self.cornerRadius]; } - (NSURL*)downloadingURL { return objc_getAssociatedObject(self, @selector(downloadingURL)); } - (void)setDownloadingURL:(NSURL*)url { objc_setAssociatedObject(self, @selector(downloadingURL), url, OBJC_ASSOCIATION_RETAIN_NONATOMIC); } - (float)downloadingPercentage { return [objc_getAssociatedObject(self, @selector(downloadingPercentage)) floatValue]; } - (void)setDownloadingPercentage:(float)progress { objc_setAssociatedObject(self, @selector(downloadingPercentage), @(progress), OBJC_ASSOCIATION_RETAIN_NONATOMIC); } #pragma mark - FlyImageRendererDelegate - (void)flyImageRenderer:(FlyImageRenderer*)render willRenderImage:(UIImage*)image { if (image == nil && self.contents == nil) { return; } [CATransaction begin]; [CATransaction setValue:(id)kCFBooleanTrue forKey:kCATransactionDisableActions]; self.contents = (__bridge id _Nullable)(image.CGImage); [CATransaction commit]; [self setNeedsLayout]; } - (void)flyImageRenderer:(FlyImageRenderer*)render didDownloadImageURL:(NSURL*)url progress:(float)progress { self.downloadingURL = url; self.downloadingPercentage = progress; } @end ================================================ FILE: FlyImage/UI/CALayer+FlyImageIconCache.h ================================================ // // CALayer+FlyImageIconCache.h // FlyImage // // Created by Ye Tong on 4/27/16. // Copyright © 2016 Ye Tong. All rights reserved. // #import #import "FlyImageIconCacheUIProtocol.h" @interface CALayer (FlyImageIconCache) @end ================================================ FILE: FlyImage/UI/CALayer+FlyImageIconCache.m ================================================ // // CALayer+FlyImageIconCache.m // FlyImage // // Created by Ye Tong on 4/27/16. // Copyright © 2016 Ye Tong. All rights reserved. // #import "CALayer+FlyImageIconCache.h" #import "FlyImageIconRenderer.h" #import "FlyImageUtils.h" #import "objc/runtime.h" @interface CALayer (__FlyImageIconCache) @end @implementation CALayer (FlyImageIconCache) static char kRendererKey; static char kDrawingBlockKey; - (void)setIconURL:(NSURL*)iconURL { [self setPlaceHolderImageName:nil iconURL:iconURL]; } - (void)setPlaceHolderImageName:(NSString*)imageName iconURL:(NSURL*)iconURL { FlyImageIconRenderer* renderer = objc_getAssociatedObject(self, &kRendererKey); if (renderer == nil) { renderer = [[FlyImageIconRenderer alloc] init]; renderer.delegate = self; self.contentsScale = [FlyImageUtils contentsScale]; self.drawsAsynchronously = YES; objc_setAssociatedObject(self, &kRendererKey, renderer, OBJC_ASSOCIATION_RETAIN_NONATOMIC); } [renderer setPlaceHolderImageName:imageName iconURL:iconURL drawSize:self.bounds.size]; } - (void)setIconDrawingBlock:(FlyImageIconDrawingBlock)block { objc_setAssociatedObject(self, &kDrawingBlockKey, block, OBJC_ASSOCIATION_RETAIN_NONATOMIC); } - (void)drawImage:(UIImage*)image inContext:(CGContextRef)context bounds:(CGRect)contextBounds { FlyImageIconDrawingBlock block = objc_getAssociatedObject(self, &kDrawingBlockKey); if (block != nil) { block(image, context, contextBounds); return; } // Clip to a rounded rect if (self.cornerRadius > 0) { CGPathRef path = _FICDCreateRoundedRectPath(contextBounds, self.cornerRadius * [FlyImageUtils contentsScale]); CGContextAddPath(context, path); CFRelease(path); CGContextEOClip(context); } UIGraphicsPushContext(context); CGRect drawRect = _FlyImageCalcDrawBounds(image.size, contextBounds.size, self.contentsGravity); [image drawInRect:drawRect]; UIGraphicsPopContext(); } #pragma mark - FlyImageIconRendererDelegate - (void)flyImageIconRenderer:(FlyImageIconRenderer*)render drawImage:(UIImage*)image context:(CGContextRef)context bounds:(CGRect)contextBounds { [self drawImage:image inContext:context bounds:contextBounds]; } - (void)flyImageIconRenderer:(FlyImageIconRenderer*)render willRenderImage:(UIImage*)image { if (image == nil && self.contents == nil) { return; } [CATransaction begin]; [CATransaction setValue:(id)kCFBooleanTrue forKey:kCATransactionDisableActions]; self.contents = (__bridge id _Nullable)(image.CGImage); [CATransaction commit]; [self setNeedsLayout]; } @end ================================================ FILE: FlyImage/UI/FlyImageCacheUIProtocol.h ================================================ // // FlyImageCacheUIProtocol.h // FlyImage // // Created by Ye Tong on 8/9/16. // Copyright © 2016 Ye Tong. All rights reserved. // #import @protocol FlyImageCacheUIProtocol @property (nonatomic, strong) NSURL* downloadingURL; // may be nil/thumbnailURL/originalURL. @property (nonatomic, assign) float downloadingPercentage; // 0-1, downloading progress per downloading URL. /** * Convenient method of setPlaceHolderImageName:thumbnailURL:originalURL * * @param url originalURL */ - (void)setImageURL:(NSURL*)url; /** * Download images and render them with the below order: * 1. PlaceHolder * 2. Thumbnail Image * 3. Original Image * * These images will be saved into [FlyImageCache shareInstance] */ - (void)setPlaceHolderImageName:(NSString*)imageName thumbnailURL:(NSURL*)thumbnailURL originalURL:(NSURL*)originalURL; @end ================================================ FILE: FlyImage/UI/FlyImageIconCacheUIProtocol.h ================================================ // // FlyImageIconCacheUIProtocol.h // FlyImage // // Created by Ye Tong on 8/9/16. // Copyright © 2016 Ye Tong. All rights reserved. // #import /** * Draw an image in the context with specific bounds. */ typedef void (^FlyImageIconDrawingBlock)(UIImage* image, CGContextRef context, CGRect contextBounds); @protocol FlyImageIconCacheUIProtocol /** * Convenient method of setPlaceHolderImageName:iconURL. */ - (void)setIconURL:(NSURL*)iconURL; /** * Download an icon, and save it using [FlyImageIconCache shareInstance]. */ - (void)setPlaceHolderImageName:(NSString*)imageName iconURL:(NSURL*)iconURL; /** * Set a customize drawing block. If not, it will use the default drawing method. */ - (void)setIconDrawingBlock:(FlyImageIconDrawingBlock)block; @end ================================================ FILE: FlyImage/UI/FlyImageIconRenderer.h ================================================ // // FlyImageIconRenderer.h // FlyImage // // Created by Ye Tong on 4/27/16. // Copyright © 2016 Ye Tong. All rights reserved. // #import #import "FlyImageIconCache.h" @class FlyImageIconRenderer; @protocol FlyImageIconRendererDelegate - (void)flyImageIconRenderer:(FlyImageIconRenderer*)render willRenderImage:(UIImage*)image; - (void)flyImageIconRenderer:(FlyImageIconRenderer*)render drawImage:(UIImage*)image context:(CGContextRef)context bounds:(CGRect)contextBounds; @end /** * Internal class to download, draw, and retrieve icons. */ @interface FlyImageIconRenderer : NSObject @property (nonatomic, weak) id delegate; - (void)setPlaceHolderImageName:(NSString*)imageName iconURL:(NSURL*)iconURL drawSize:(CGSize)drawSize; @end ================================================ FILE: FlyImage/UI/FlyImageIconRenderer.m ================================================ // // FlyImageIconRenderer.m // FlyImage // // Created by Ye Tong on 4/27/16. // Copyright © 2016 Ye Tong. All rights reserved. // #import "FlyImageIconRenderer.h" #import "FlyImageUtils.h" #import "FlyImageCache.h" #import "FlyImageDownloader.h" @interface FlyImageIconRenderer () @property (nonatomic, strong) NSURL* iconURL; @end @implementation FlyImageIconRenderer { CGSize _drawSize; FlyImageDownloadHandlerId* _downloadHandlerId; } - (void)dealloc { [self cancelDownload]; } - (void)cancelDownload { if (_downloadHandlerId != nil) { [[FlyImageDownloader sharedInstance] cancelDownloadHandler:_downloadHandlerId]; _downloadHandlerId = nil; } } - (void)setPlaceHolderImageName:(NSString*)imageName iconURL:(NSURL*)iconURL drawSize:(CGSize)drawSize { if (_iconURL != nil && [_iconURL.absoluteString isEqualToString:iconURL.absoluteString]) { return; } [self cancelDownload]; _iconURL = iconURL; _drawSize = CGSizeMake(round(drawSize.width), round(drawSize.height)); [self renderWithPlaceHolderImageName:imageName]; } - (void)renderWithPlaceHolderImageName:(NSString*)imageName { NSString* key = _iconURL.absoluteString; // if has already downloaded image if (key != nil && [[FlyImageIconCache sharedInstance] isImageExistWithKey:key]) { __weak __typeof__(self) weakSelf = self; [[FlyImageIconCache sharedInstance] asyncGetImageWithKey:key completed:^(NSString* key, UIImage* image) { [weakSelf renderImage:image key:key ]; }]; return; } if (imageName != nil) { UIImage* placeHolderImage = [UIImage imageNamed:imageName]; [self doRenderImage:placeHolderImage]; } else if (key != nil) { // clear [self doRenderImage:nil]; } if (key == nil) { return; } if ([[FlyImageCache sharedInstance] isImageExistWithKey:key]) { NSString* imagePath = [[FlyImageCache sharedInstance] imagePathWithKey:key]; if (imagePath != nil) { NSURL* url = [NSURL fileURLWithPath:imagePath]; [self drawIconWithKey:key url:url]; return; } } [self downloadImage]; } - (void)downloadImage { __weak __typeof__(self) weakSelf = self; NSMutableURLRequest* request = [NSMutableURLRequest requestWithURL:_iconURL]; request.timeoutInterval = 30; // Default 30 seconds _downloadHandlerId = [[FlyImageDownloader sharedInstance] downloadImageForURLRequest:request success:^(NSURLRequest* request, NSURL* filePath) { NSString *downloadedKey = request.URL.absoluteString; [[FlyImageCache sharedInstance] addImageWithKey:downloadedKey filename:[filePath lastPathComponent] completed:nil]; // In case downloaded image is not equal with the new url if ( ![downloadedKey isEqualToString:weakSelf.iconURL.absoluteString] ) { return; } _downloadHandlerId = nil; [weakSelf drawIconWithKey:downloadedKey url:filePath]; } failed:^(NSURLRequest* request, NSError* error) { _downloadHandlerId = nil; }]; } - (void)drawIconWithKey:(NSString*)key url:(NSURL*)url { __weak __typeof__(self) weakSelf = self; [[FlyImageIconCache sharedInstance] addImageWithKey:key size:_drawSize drawingBlock:^(CGContextRef context, CGRect contextBounds) { NSData *data = [NSData dataWithContentsOfURL:url]; UIImage *image = [UIImage imageWithData:data]; [weakSelf.delegate flyImageIconRenderer:weakSelf drawImage:image context:context bounds:contextBounds]; } completed:^(NSString* key, UIImage* image) { [weakSelf renderImage:image key:key]; }]; } - (void)renderImage:(UIImage*)image key:(NSString*)key { dispatch_main_sync_safe(^{ if ( ![_iconURL.absoluteString isEqualToString:key] ) { return; } [self doRenderImage:image]; }); } - (void)doRenderImage:(UIImage*)image { [_delegate flyImageIconRenderer:self willRenderImage:image]; } @end ================================================ FILE: FlyImage/UI/FlyImageRenderer.h ================================================ // // FlyImageRenderer.h // Demo // // Created by Norris Tong on 4/11/16. // Copyright © 2016 NorrisTong. All rights reserved. // #import @class FlyImageRenderer; @protocol FlyImageRendererDelegate /** * Callback before download image. */ - (void)flyImageRenderer:(FlyImageRenderer*)render willRenderImage:(UIImage*)image; @optional /** * Callback after download image. * * @param render * @param url * @param progress 0...1 */ - (void)flyImageRenderer:(FlyImageRenderer*)render didDownloadImageURL:(NSURL*)url progress:(float)progress; @end /** * Internal class to download, draw, and retrieve images. */ @interface FlyImageRenderer : NSObject @property (nonatomic, weak) id delegate; - (void)setPlaceHolderImageName:(NSString*)imageName thumbnailURL:(NSURL*)thumbnailURL originalURL:(NSURL*)originalURL drawSize:(CGSize)drawSize contentsGravity:(NSString* const)contentsGravity cornerRadius:(CGFloat)cornerRadius; @end ================================================ FILE: FlyImage/UI/FlyImageRenderer.m ================================================ // // FlyImageRenderer.m // Demo // // Created by Norris Tong on 4/11/16. // Copyright © 2016 NorrisTong. All rights reserved. // #import "FlyImageRenderer.h" #import "FlyImageCache.h" #import "FlyImageUtils.h" #import "FlyImageDownloader.h" @implementation FlyImageRenderer { NSString* _placeHolderImageName; NSURL* _thumbnailURL; NSURL* _originalURL; CGSize _drawSize; NSString* _contentsGravity; CGFloat _cornerRadius; FlyImageDownloadHandlerId* _downloadHandlerId; } - (instancetype)init { if (self = [super init]) { // event if ([FlyImageCache sharedInstance].autoDismissImage) { [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationWillEnterForeground:) name:UIApplicationWillEnterForegroundNotification object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationDidEnterBackground:) name:UIApplicationDidEnterBackgroundNotification object:nil]; } } return self; } - (void)dealloc { [self cancelDownload]; if ([FlyImageCache sharedInstance].autoDismissImage) { [[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationWillEnterForegroundNotification object:nil]; [[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationDidEnterBackgroundNotification object:nil]; } } - (void)applicationDidEnterBackground:(UIApplication*)application { [self cancelDownload]; // clear image data to reduce memory [self renderImage:nil]; } - (void)applicationWillEnterForeground:(UIApplication*)application { // repaint [self render]; } - (void)cancelDownload { if (_downloadHandlerId != nil) { [[FlyImageDownloader sharedInstance] cancelDownloadHandler:_downloadHandlerId]; _downloadHandlerId = nil; } // try to cancel getting image operation. if (_originalURL) { [[FlyImageCache sharedInstance] cancelGetImageWithKey:_originalURL.absoluteString]; } if (_thumbnailURL) { [[FlyImageCache sharedInstance] cancelGetImageWithKey:_thumbnailURL.absoluteString]; } } - (void)setPlaceHolderImageName:(NSString*)imageName thumbnailURL:(NSURL*)thumbnailURL originalURL:(NSURL*)originalURL drawSize:(CGSize)drawSize contentsGravity:(NSString* const)contentsGravity cornerRadius:(CGFloat)cornerRadius { if (_originalURL != nil && [_originalURL.absoluteString isEqualToString:originalURL.absoluteString]) { return; } [self cancelDownload]; _placeHolderImageName = imageName; _thumbnailURL = thumbnailURL; _originalURL = originalURL; _drawSize = drawSize; _contentsGravity = contentsGravity; _cornerRadius = cornerRadius; [self render]; } - (void)render { // 0. clear [self renderImage:nil]; // if has already downloaded original image NSString* originalKey = _originalURL.absoluteString; if (originalKey != nil && [[FlyImageCache sharedInstance] isImageExistWithKey:originalKey]) { __weak __typeof__(self) weakSelf = self; [[FlyImageCache sharedInstance] asyncGetImageWithKey:originalKey drawSize:_drawSize contentsGravity:_contentsGravity cornerRadius:_cornerRadius completed:^(NSString* key, UIImage* image) { [weakSelf renderOriginalImage:image key:key]; }]; return; } // if there is no thumbnail, then render original image NSString* thumbnailKey = _thumbnailURL.absoluteString; if (thumbnailKey != nil && [[FlyImageCache sharedInstance] isImageExistWithKey:thumbnailKey]) { __weak __typeof__(self) weakSelf = self; [[FlyImageCache sharedInstance] asyncGetImageWithKey:thumbnailKey drawSize:_drawSize contentsGravity:_contentsGravity cornerRadius:_cornerRadius completed:^(NSString* key, UIImage* image) { [weakSelf renderThumbnailImage:image key:key]; }]; return; } if (_placeHolderImageName != nil) { UIImage* placeHolderImage = [UIImage imageNamed:_placeHolderImageName]; [self renderImage:placeHolderImage]; } if (_thumbnailURL == nil && _originalURL != nil) { [self downloadOriginal]; return; } if (_thumbnailURL == nil) { return; } [self downloadThumbnail]; } - (void)downloadThumbnail { __weak __typeof__(self) weakSelf = self; __block NSURL* downloadingURL = _thumbnailURL; __block NSString* downloadingKey = downloadingURL.absoluteString; NSMutableURLRequest* request = [NSMutableURLRequest requestWithURL:downloadingURL]; request.timeoutInterval = 30; // Default 30 seconds _downloadHandlerId = [[FlyImageDownloader sharedInstance] downloadImageForURLRequest:request progress:^(float progress) { if ( [_delegate respondsToSelector:@selector(flyImageRenderer:didDownloadImageURL:progress:)] ){ [_delegate flyImageRenderer:weakSelf didDownloadImageURL:downloadingURL progress:progress]; } } success:^(NSURLRequest* request, NSURL* filePath) { _downloadHandlerId = nil; [[FlyImageCache sharedInstance] addImageWithKey:downloadingKey filename:filePath.lastPathComponent drawSize:_drawSize contentsGravity:_contentsGravity cornerRadius:_cornerRadius completed:^(NSString *key, UIImage *image) { [weakSelf renderThumbnailImage:image key:key]; }]; } failed:^(NSURLRequest* request, NSError* error) { _downloadHandlerId = nil; // if error code is cancelled, no need to download original image. if ( error.code != NSURLErrorCancelled && _originalURL != nil ){ [weakSelf downloadOriginal]; } }]; } - (void)downloadOriginal { __weak __typeof__(self) weakSelf = self; __block NSURL* downloadingURL = _originalURL; __block NSString* downloadingKey = downloadingURL.absoluteString; NSMutableURLRequest* request = [NSMutableURLRequest requestWithURL:downloadingURL]; request.timeoutInterval = 30; // Default 30 seconds _downloadHandlerId = [[FlyImageDownloader sharedInstance] downloadImageForURLRequest:request progress:^(float progress) { if ( [_delegate respondsToSelector:@selector(flyImageRenderer:didDownloadImageURL:progress:)] ){ [_delegate flyImageRenderer:weakSelf didDownloadImageURL:downloadingURL progress:progress]; } } success:^(NSURLRequest* request, NSURL* filePath) { _downloadHandlerId = nil; [[FlyImageCache sharedInstance] addImageWithKey:downloadingKey filename:filePath.lastPathComponent drawSize:_drawSize contentsGravity:_contentsGravity cornerRadius:_cornerRadius completed:^(NSString *key, UIImage *image) { [weakSelf renderOriginalImage:image key:key]; }]; } failed:^(NSURLRequest* request, NSError* error) { _downloadHandlerId = nil; }]; } - (void)renderThumbnailImage:(UIImage*)image key:(NSString*)key { dispatch_main_sync_safe(^{ if ( ![key isEqualToString:_thumbnailURL.absoluteString] ) { return; } [self renderImage:image]; if ( _originalURL != nil ){ [self downloadOriginal]; } }); } - (void)renderOriginalImage:(UIImage*)image key:(NSString*)key { dispatch_main_sync_safe(^{ if ( ![key isEqualToString:_originalURL.absoluteString] ) { return; } [self renderImage:image]; }); } - (void)renderImage:(UIImage*)image { [_delegate flyImageRenderer:self willRenderImage:image]; } @end ================================================ FILE: FlyImage/UI/UIImageView+FlyImageCache.h ================================================ // // UIImageView+FlyImageCache.h // Demo // // Created by Ye Tong on 3/17/16. // Copyright © 2016 NorrisTong. All rights reserved. // #import #import "FlyImageCacheUIProtocol.h" @interface UIImageView (FlyImageCache) @end ================================================ FILE: FlyImage/UI/UIImageView+FlyImageCache.m ================================================ // // UIImageView+FlyImageCache.m // Demo // // Created by Ye Tong on 3/17/16. // Copyright © 2016 NorrisTong. All rights reserved. // #import "UIImageView+FlyImageCache.h" #import "FlyImageRenderer.h" #import "objc/runtime.h" @interface UIImageView (__FlyImageCache) @end @implementation UIImageView (FlyImageCache) static char kRendererKey; - (void)setImageURL:(NSURL*)url { [self setPlaceHolderImageName:nil thumbnailURL:nil originalURL:url]; } - (void)setPlaceHolderImageName:(NSString*)imageName thumbnailURL:(NSURL*)thumbnailURL originalURL:(NSURL*)originalURL { FlyImageRenderer* renderer = objc_getAssociatedObject(self, &kRendererKey); if (renderer == nil) { renderer = [[FlyImageRenderer alloc] init]; renderer.delegate = self; objc_setAssociatedObject(self, &kRendererKey, renderer, OBJC_ASSOCIATION_RETAIN_NONATOMIC); } [renderer setPlaceHolderImageName:imageName thumbnailURL:thumbnailURL originalURL:originalURL drawSize:self.bounds.size contentsGravity:self.layer.contentsGravity cornerRadius:self.layer.cornerRadius]; } - (NSURL*)downloadingURL { return objc_getAssociatedObject(self, @selector(downloadingURL)); } - (void)setDownloadingURL:(NSURL*)url { objc_setAssociatedObject(self, @selector(downloadingURL), url, OBJC_ASSOCIATION_RETAIN_NONATOMIC); } - (float)downloadingPercentage { return [objc_getAssociatedObject(self, @selector(downloadingPercentage)) floatValue]; } - (void)setDownloadingPercentage:(float)progress { objc_setAssociatedObject(self, @selector(downloadingPercentage), @(progress), OBJC_ASSOCIATION_RETAIN_NONATOMIC); } #pragma mark - FlyImageRendererDelegate - (void)flyImageRenderer:(FlyImageRenderer*)render willRenderImage:(UIImage*)image { if (image == nil && self.image == nil) { return; } self.image = image; [self setNeedsDisplay]; } - (void)flyImageRenderer:(FlyImageRenderer*)render didDownloadImageURL:(NSURL*)url progress:(float)progress { self.downloadingURL = url; self.downloadingPercentage = progress; } @end ================================================ FILE: FlyImage/UI/UIImageView+FlyImageIconCache.h ================================================ // // UIImageView+FlyImageIconCache.h // FlyImage // // Created by Ye Tong on 5/3/16. // Copyright © 2016 Ye Tong. All rights reserved. // #import #import "FlyImageIconCacheUIProtocol.h" @interface UIImageView (FlyImageIconCache) @end ================================================ FILE: FlyImage/UI/UIImageView+FlyImageIconCache.m ================================================ // // UIImageView+FlyImageIconCache.m // FlyImage // // Created by Ye Tong on 5/3/16. // Copyright © 2016 Ye Tong. All rights reserved. // #import "UIImageView+FlyImageIconCache.h" #import "FlyImageIconRenderer.h" #import "FlyImageUtils.h" #import "objc/runtime.h" @interface UIImageView (__FlyImageIconCache) @end @implementation UIImageView (FlyImageIconCache) static char kRendererKey; static char kDrawingBlockKey; - (void)setIconURL:(NSURL*)iconURL { [self setPlaceHolderImageName:nil iconURL:iconURL]; } - (void)setPlaceHolderImageName:(NSString*)imageName iconURL:(NSURL*)iconURL { FlyImageIconRenderer* renderer = objc_getAssociatedObject(self, &kRendererKey); if (renderer == nil) { renderer = [[FlyImageIconRenderer alloc] init]; renderer.delegate = self; objc_setAssociatedObject(self, &kRendererKey, renderer, OBJC_ASSOCIATION_RETAIN_NONATOMIC); } [renderer setPlaceHolderImageName:imageName iconURL:iconURL drawSize:self.bounds.size]; } - (void)setIconDrawingBlock:(FlyImageIconDrawingBlock)block { objc_setAssociatedObject(self, &kDrawingBlockKey, block, OBJC_ASSOCIATION_RETAIN_NONATOMIC); } - (void)drawImage:(UIImage*)image inContext:(CGContextRef)context bounds:(CGRect)contextBounds { FlyImageIconDrawingBlock block = objc_getAssociatedObject(self, &kDrawingBlockKey); if (block != nil) { block(image, context, contextBounds); return; } // Clip to a rounded rect if (self.layer.cornerRadius > 0) { CGPathRef path = _FICDCreateRoundedRectPath(contextBounds, self.layer.cornerRadius * [FlyImageUtils contentsScale]); CGContextAddPath(context, path); CFRelease(path); CGContextEOClip(context); } UIGraphicsPushContext(context); CGRect drawRect = _FlyImageCalcDrawBounds(image.size, contextBounds.size, self.layer.contentsGravity); [image drawInRect:drawRect]; UIGraphicsPopContext(); } #pragma mark - FlyImageIconRendererDelegate - (void)flyImageIconRenderer:(FlyImageIconRenderer*)render drawImage:(UIImage*)image context:(CGContextRef)context bounds:(CGRect)contextBounds { [self drawImage:image inContext:context bounds:contextBounds]; } - (void)flyImageIconRenderer:(FlyImageIconRenderer*)render willRenderImage:(UIImage*)image { if (image == nil && self.image == nil) { return; } self.image = image; [self setNeedsLayout]; } @end ================================================ FILE: FlyImage.podspec ================================================ Pod::Spec.new do |s| s.name = "FlyImage" s.version = "1.1" s.summary = "Download, cache, render small images with UIImageView category" s.description = 'FlyImage takes the advantages of SDWebImage, FastImageCache and AFNetworking, ' \ 'is a simple and high performance image library.Features: ' \ 'High Performance, reduce memory operations while rendering, avoid Memory warning caused by image; ' \ 'Store and retrieve different size of small images in one memory file, smooth scrolling; ' \ 'Simple, support UIImageView, CALayer category; ' \ 'An asynchronous image downloader; ' \ 'Support WebP format; ' \ 'Support mmap to improve I/O performace;' s.homepage = "https://github.com/northwind/FlyImage" s.license = "MIT" s.author = { "norristong" => "norristong_x@qq.com" } s.platform = :ios, "8.0" s.source = { :git => 'https://github.com/northwind/FlyImage.git', :tag => s.version.to_s } s.source_files = "FlyImage", "FlyImage/**/*.{h,m}" s.frameworks = "ImageIO", 'UIKit' s.requires_arc = true s.dependency 'AFNetworking', '~> 3.1' s.default_subspec = 'Core' s.subspec 'Core' do |core| core.source_files = "FlyImage", 'FlyImage/**/*.{h,m}' end s.subspec 'WebP' do |webp| webp.xcconfig = { 'GCC_PREPROCESSOR_DEFINITIONS' => '$(inherited) FLYIMAGE_WEBP=1', 'USER_HEADER_SEARCH_PATHS' => '$(inherited) $(SRCROOT)/libwebp/src' } webp.watchos.xcconfig = { 'GCC_PREPROCESSOR_DEFINITIONS' => '$(inherited) FLYIMAGE_WEBP=1', 'USER_HEADER_SEARCH_PATHS' => '$(inherited) $(SRCROOT)/libwebp/src' } webp.dependency 'FlyImage/Core' webp.dependency 'libwebp' end end ================================================ FILE: FlyImage.xcodeproj/project.pbxproj ================================================ // !$*UTF8*$! { archiveVersion = 1; classes = { }; objectVersion = 46; objects = { /* Begin PBXBuildFile section */ 63D1D241E0B38FBE0700B2B2 /* libPods-FlyImage.a in Frameworks */ = {isa = PBXBuildFile; fileRef = E7D2BCD141491AB1301A46CC /* libPods-FlyImage.a */; }; DD4D2C511D5C678E001F5D77 /* FlyImageRetrieveOperation.h in Headers */ = {isa = PBXBuildFile; fileRef = DD4D2C4F1D5C678E001F5D77 /* FlyImageRetrieveOperation.h */; }; DD4D2C521D5C678E001F5D77 /* FlyImageRetrieveOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = DD4D2C501D5C678E001F5D77 /* FlyImageRetrieveOperation.m */; }; DD4D2C531D5C678E001F5D77 /* FlyImageRetrieveOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = DD4D2C501D5C678E001F5D77 /* FlyImageRetrieveOperation.m */; }; DD9D96E01CC50336008F279E /* FlyImage.h in Headers */ = {isa = PBXBuildFile; fileRef = DD9D96DF1CC50336008F279E /* FlyImage.h */; settings = {ATTRIBUTES = (Public, ); }; }; DD9D96E71CC50336008F279E /* FlyImage.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DD9D96DC1CC50336008F279E /* FlyImage.framework */; }; DD9D97141CC5034A008F279E /* FlyImageCache.h in Headers */ = {isa = PBXBuildFile; fileRef = DD9D96F71CC5034A008F279E /* FlyImageCache.h */; settings = {ATTRIBUTES = (Public, ); }; }; DD9D97151CC5034A008F279E /* FlyImageCache.m in Sources */ = {isa = PBXBuildFile; fileRef = DD9D96F81CC5034A008F279E /* FlyImageCache.m */; }; DD9D97161CC5034A008F279E /* FlyImageCacheProtocol.h in Headers */ = {isa = PBXBuildFile; fileRef = DD9D96F91CC5034A008F279E /* FlyImageCacheProtocol.h */; settings = {ATTRIBUTES = (Public, ); }; }; DD9D97171CC5034A008F279E /* FlyImageDataFile.h in Headers */ = {isa = PBXBuildFile; fileRef = DD9D96FA1CC5034A008F279E /* FlyImageDataFile.h */; }; DD9D97181CC5034A008F279E /* FlyImageDataFile.m in Sources */ = {isa = PBXBuildFile; fileRef = DD9D96FB1CC5034A008F279E /* FlyImageDataFile.m */; }; DD9D97191CC5034A008F279E /* FlyImageDataFileManager.h in Headers */ = {isa = PBXBuildFile; fileRef = DD9D96FC1CC5034A008F279E /* FlyImageDataFileManager.h */; }; DD9D971A1CC5034A008F279E /* FlyImageDataFileManager.m in Sources */ = {isa = PBXBuildFile; fileRef = DD9D96FD1CC5034A008F279E /* FlyImageDataFileManager.m */; }; DD9D971B1CC5034A008F279E /* FlyImageDecoder.h in Headers */ = {isa = PBXBuildFile; fileRef = DD9D96FE1CC5034A008F279E /* FlyImageDecoder.h */; }; DD9D971C1CC5034A008F279E /* FlyImageDecoder.m in Sources */ = {isa = PBXBuildFile; fileRef = DD9D96FF1CC5034A008F279E /* FlyImageDecoder.m */; }; DD9D971D1CC5034A008F279E /* FlyImageDownloader.h in Headers */ = {isa = PBXBuildFile; fileRef = DD9D97001CC5034A008F279E /* FlyImageDownloader.h */; settings = {ATTRIBUTES = (Public, ); }; }; DD9D971E1CC5034A008F279E /* FlyImageDownloader.m in Sources */ = {isa = PBXBuildFile; fileRef = DD9D97011CC5034A008F279E /* FlyImageDownloader.m */; }; DD9D971F1CC5034A008F279E /* FlyImageEncoder.h in Headers */ = {isa = PBXBuildFile; fileRef = DD9D97021CC5034A008F279E /* FlyImageEncoder.h */; }; DD9D97201CC5034A008F279E /* FlyImageEncoder.m in Sources */ = {isa = PBXBuildFile; fileRef = DD9D97031CC5034A008F279E /* FlyImageEncoder.m */; }; DD9D97211CC5034A008F279E /* FlyImageIconCache.h in Headers */ = {isa = PBXBuildFile; fileRef = DD9D97041CC5034A008F279E /* FlyImageIconCache.h */; settings = {ATTRIBUTES = (Public, ); }; }; DD9D97221CC5034A008F279E /* FlyImageIconCache.m in Sources */ = {isa = PBXBuildFile; fileRef = DD9D97051CC5034A008F279E /* FlyImageIconCache.m */; }; DD9D97231CC5034A008F279E /* FlyImageUtils.h in Headers */ = {isa = PBXBuildFile; fileRef = DD9D97061CC5034A008F279E /* FlyImageUtils.h */; }; DD9D97241CC5034A008F279E /* FlyImageUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = DD9D97071CC5034A008F279E /* FlyImageUtils.m */; }; DD9D97341CC503A2008F279E /* FlyImageCacheTests.m in Sources */ = {isa = PBXBuildFile; fileRef = DD9D972E1CC503A2008F279E /* FlyImageCacheTests.m */; }; DD9D97351CC503A2008F279E /* FlyImageDataFileManagerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = DD9D972F1CC503A2008F279E /* FlyImageDataFileManagerTests.m */; }; DD9D97361CC503A2008F279E /* FlyImageDataFileTests.m in Sources */ = {isa = PBXBuildFile; fileRef = DD9D97301CC503A2008F279E /* FlyImageDataFileTests.m */; }; DD9D97371CC503A2008F279E /* FlyImageDownloadManagerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = DD9D97311CC503A2008F279E /* FlyImageDownloadManagerTests.m */; }; DD9D97381CC503A2008F279E /* FlyImageIconCacheTests.m in Sources */ = {isa = PBXBuildFile; fileRef = DD9D97321CC503A2008F279E /* FlyImageIconCacheTests.m */; }; DDD8FB5B1D59D0F900821392 /* FlyImageIconCacheUIProtocol.h in Headers */ = {isa = PBXBuildFile; fileRef = DDD8FB5A1D59D0F900821392 /* FlyImageIconCacheUIProtocol.h */; settings = {ATTRIBUTES = (Public, ); }; }; DDD8FB5E1D59D24900821392 /* FlyImageCacheUIProtocol.h in Headers */ = {isa = PBXBuildFile; fileRef = DDD8FB5D1D59D24900821392 /* FlyImageCacheUIProtocol.h */; settings = {ATTRIBUTES = (Public, ); }; }; DDDAE6AE1CE6E10C00B3CCCC /* CALayer+FlyImageCache.h in Headers */ = {isa = PBXBuildFile; fileRef = DDDAE6A21CE6E10C00B3CCCC /* CALayer+FlyImageCache.h */; settings = {ATTRIBUTES = (Public, ); }; }; DDDAE6AF1CE6E10C00B3CCCC /* CALayer+FlyImageCache.m in Sources */ = {isa = PBXBuildFile; fileRef = DDDAE6A31CE6E10C00B3CCCC /* CALayer+FlyImageCache.m */; }; DDDAE6B01CE6E10C00B3CCCC /* CALayer+FlyImageIconCache.h in Headers */ = {isa = PBXBuildFile; fileRef = DDDAE6A41CE6E10C00B3CCCC /* CALayer+FlyImageIconCache.h */; settings = {ATTRIBUTES = (Public, ); }; }; DDDAE6B11CE6E10C00B3CCCC /* CALayer+FlyImageIconCache.m in Sources */ = {isa = PBXBuildFile; fileRef = DDDAE6A51CE6E10C00B3CCCC /* CALayer+FlyImageIconCache.m */; }; DDDAE6B21CE6E10C00B3CCCC /* FlyImageIconRenderer.h in Headers */ = {isa = PBXBuildFile; fileRef = DDDAE6A61CE6E10C00B3CCCC /* FlyImageIconRenderer.h */; }; DDDAE6B31CE6E10C00B3CCCC /* FlyImageIconRenderer.m in Sources */ = {isa = PBXBuildFile; fileRef = DDDAE6A71CE6E10C00B3CCCC /* FlyImageIconRenderer.m */; }; DDDAE6B41CE6E10C00B3CCCC /* FlyImageRenderer.h in Headers */ = {isa = PBXBuildFile; fileRef = DDDAE6A81CE6E10C00B3CCCC /* FlyImageRenderer.h */; }; DDDAE6B51CE6E10C00B3CCCC /* FlyImageRenderer.m in Sources */ = {isa = PBXBuildFile; fileRef = DDDAE6A91CE6E10C00B3CCCC /* FlyImageRenderer.m */; }; DDDAE6B61CE6E10C00B3CCCC /* UIImageView+FlyImageCache.h in Headers */ = {isa = PBXBuildFile; fileRef = DDDAE6AA1CE6E10C00B3CCCC /* UIImageView+FlyImageCache.h */; settings = {ATTRIBUTES = (Public, ); }; }; DDDAE6B71CE6E10C00B3CCCC /* UIImageView+FlyImageCache.m in Sources */ = {isa = PBXBuildFile; fileRef = DDDAE6AB1CE6E10C00B3CCCC /* UIImageView+FlyImageCache.m */; }; DDDAE6B81CE6E10C00B3CCCC /* UIImageView+FlyImageIconCache.h in Headers */ = {isa = PBXBuildFile; fileRef = DDDAE6AC1CE6E10C00B3CCCC /* UIImageView+FlyImageIconCache.h */; settings = {ATTRIBUTES = (Public, ); }; }; DDDAE6B91CE6E10C00B3CCCC /* UIImageView+FlyImageIconCache.m in Sources */ = {isa = PBXBuildFile; fileRef = DDDAE6AD1CE6E10C00B3CCCC /* UIImageView+FlyImageIconCache.m */; }; DDF65B111CC70DE50066B12F /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DDF65B101CC70DE50066B12F /* UIKit.framework */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ DD9D96E81CC50336008F279E /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = DD9D96D31CC50336008F279E /* Project object */; proxyType = 1; remoteGlobalIDString = DD9D96DB1CC50336008F279E; remoteInfo = FlyImage; }; /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ 2054736E2EDA981DF80AD030 /* Pods-FlyImage.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-FlyImage.release.xcconfig"; path = "Pods/Target Support Files/Pods-FlyImage/Pods-FlyImage.release.xcconfig"; sourceTree = ""; }; C05497892C966A58859B6B98 /* Pods-FlyImage.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-FlyImage.debug.xcconfig"; path = "Pods/Target Support Files/Pods-FlyImage/Pods-FlyImage.debug.xcconfig"; sourceTree = ""; }; DD4C0C911CCEF78700FF7E28 /* libwebp.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = libwebp.framework; path = "../../../Library/Developer/Xcode/DerivedData/FlyImage-dsmditzgpqdvcjaeenutizhwdsdm/Build/Products/Debug-iphonesimulator/libwebp.framework"; sourceTree = ""; }; DD4D2C4F1D5C678E001F5D77 /* FlyImageRetrieveOperation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = FlyImageRetrieveOperation.h; path = FlyImage/Core/FlyImageRetrieveOperation.h; sourceTree = SOURCE_ROOT; }; DD4D2C501D5C678E001F5D77 /* FlyImageRetrieveOperation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = FlyImageRetrieveOperation.m; path = FlyImage/Core/FlyImageRetrieveOperation.m; sourceTree = SOURCE_ROOT; }; DD9D96DC1CC50336008F279E /* FlyImage.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = FlyImage.framework; sourceTree = BUILT_PRODUCTS_DIR; }; DD9D96DF1CC50336008F279E /* FlyImage.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FlyImage.h; sourceTree = ""; }; DD9D96E11CC50336008F279E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; DD9D96E61CC50336008F279E /* FlyImageTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = FlyImageTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; DD9D96ED1CC50336008F279E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; DD9D96F71CC5034A008F279E /* FlyImageCache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FlyImageCache.h; sourceTree = ""; }; DD9D96F81CC5034A008F279E /* FlyImageCache.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FlyImageCache.m; sourceTree = ""; }; DD9D96F91CC5034A008F279E /* FlyImageCacheProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FlyImageCacheProtocol.h; sourceTree = ""; }; DD9D96FA1CC5034A008F279E /* FlyImageDataFile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FlyImageDataFile.h; sourceTree = ""; }; DD9D96FB1CC5034A008F279E /* FlyImageDataFile.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FlyImageDataFile.m; sourceTree = ""; }; DD9D96FC1CC5034A008F279E /* FlyImageDataFileManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FlyImageDataFileManager.h; sourceTree = ""; }; DD9D96FD1CC5034A008F279E /* FlyImageDataFileManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FlyImageDataFileManager.m; sourceTree = ""; }; DD9D96FE1CC5034A008F279E /* FlyImageDecoder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FlyImageDecoder.h; sourceTree = ""; }; DD9D96FF1CC5034A008F279E /* FlyImageDecoder.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FlyImageDecoder.m; sourceTree = ""; }; DD9D97001CC5034A008F279E /* FlyImageDownloader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FlyImageDownloader.h; sourceTree = ""; }; DD9D97011CC5034A008F279E /* FlyImageDownloader.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FlyImageDownloader.m; sourceTree = ""; }; DD9D97021CC5034A008F279E /* FlyImageEncoder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FlyImageEncoder.h; sourceTree = ""; }; DD9D97031CC5034A008F279E /* FlyImageEncoder.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FlyImageEncoder.m; sourceTree = ""; }; DD9D97041CC5034A008F279E /* FlyImageIconCache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FlyImageIconCache.h; sourceTree = ""; }; DD9D97051CC5034A008F279E /* FlyImageIconCache.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FlyImageIconCache.m; sourceTree = ""; }; DD9D97061CC5034A008F279E /* FlyImageUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FlyImageUtils.h; sourceTree = ""; }; DD9D97071CC5034A008F279E /* FlyImageUtils.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FlyImageUtils.m; sourceTree = ""; }; DD9D972E1CC503A2008F279E /* FlyImageCacheTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FlyImageCacheTests.m; sourceTree = ""; }; DD9D972F1CC503A2008F279E /* FlyImageDataFileManagerTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FlyImageDataFileManagerTests.m; sourceTree = ""; }; DD9D97301CC503A2008F279E /* FlyImageDataFileTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FlyImageDataFileTests.m; sourceTree = ""; }; DD9D97311CC503A2008F279E /* FlyImageDownloadManagerTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FlyImageDownloadManagerTests.m; sourceTree = ""; }; DD9D97321CC503A2008F279E /* FlyImageIconCacheTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FlyImageIconCacheTests.m; sourceTree = ""; }; DDD8FB5A1D59D0F900821392 /* FlyImageIconCacheUIProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FlyImageIconCacheUIProtocol.h; sourceTree = ""; }; DDD8FB5D1D59D24900821392 /* FlyImageCacheUIProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FlyImageCacheUIProtocol.h; sourceTree = ""; }; DDDAE6A21CE6E10C00B3CCCC /* CALayer+FlyImageCache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "CALayer+FlyImageCache.h"; sourceTree = ""; }; DDDAE6A31CE6E10C00B3CCCC /* CALayer+FlyImageCache.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "CALayer+FlyImageCache.m"; sourceTree = ""; }; DDDAE6A41CE6E10C00B3CCCC /* CALayer+FlyImageIconCache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "CALayer+FlyImageIconCache.h"; sourceTree = ""; }; DDDAE6A51CE6E10C00B3CCCC /* CALayer+FlyImageIconCache.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "CALayer+FlyImageIconCache.m"; sourceTree = ""; }; DDDAE6A61CE6E10C00B3CCCC /* FlyImageIconRenderer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FlyImageIconRenderer.h; sourceTree = ""; }; DDDAE6A71CE6E10C00B3CCCC /* FlyImageIconRenderer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FlyImageIconRenderer.m; sourceTree = ""; }; DDDAE6A81CE6E10C00B3CCCC /* FlyImageRenderer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FlyImageRenderer.h; sourceTree = ""; }; DDDAE6A91CE6E10C00B3CCCC /* FlyImageRenderer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FlyImageRenderer.m; sourceTree = ""; }; DDDAE6AA1CE6E10C00B3CCCC /* UIImageView+FlyImageCache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIImageView+FlyImageCache.h"; sourceTree = ""; }; DDDAE6AB1CE6E10C00B3CCCC /* UIImageView+FlyImageCache.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIImageView+FlyImageCache.m"; sourceTree = ""; }; DDDAE6AC1CE6E10C00B3CCCC /* UIImageView+FlyImageIconCache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIImageView+FlyImageIconCache.h"; sourceTree = ""; }; DDDAE6AD1CE6E10C00B3CCCC /* UIImageView+FlyImageIconCache.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIImageView+FlyImageIconCache.m"; sourceTree = ""; }; DDF65B101CC70DE50066B12F /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; E7D2BCD141491AB1301A46CC /* libPods-FlyImage.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-FlyImage.a"; sourceTree = BUILT_PRODUCTS_DIR; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ DD9D96D81CC50336008F279E /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( DDF65B111CC70DE50066B12F /* UIKit.framework in Frameworks */, 63D1D241E0B38FBE0700B2B2 /* libPods-FlyImage.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; DD9D96E31CC50336008F279E /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( DD9D96E71CC50336008F279E /* FlyImage.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ DD9D96D21CC50336008F279E = { isa = PBXGroup; children = ( DD9D96DE1CC50336008F279E /* FlyImage */, DD9D96EA1CC50336008F279E /* FlyImageTests */, DD9D96DD1CC50336008F279E /* Products */, E115FD25013B3B36CF3058C3 /* Frameworks */, F9DB01F8FD4F9561A9D733F0 /* Pods */, ); sourceTree = ""; }; DD9D96DD1CC50336008F279E /* Products */ = { isa = PBXGroup; children = ( DD9D96DC1CC50336008F279E /* FlyImage.framework */, DD9D96E61CC50336008F279E /* FlyImageTests.xctest */, ); name = Products; sourceTree = ""; }; DD9D96DE1CC50336008F279E /* FlyImage */ = { isa = PBXGroup; children = ( DD9D96F61CC5034A008F279E /* Core */, DDDAE6A11CE6E10C00B3CCCC /* UI */, DD9D96DF1CC50336008F279E /* FlyImage.h */, DD9D96E11CC50336008F279E /* Info.plist */, ); path = FlyImage; sourceTree = ""; }; DD9D96EA1CC50336008F279E /* FlyImageTests */ = { isa = PBXGroup; children = ( DD9D972E1CC503A2008F279E /* FlyImageCacheTests.m */, DD9D972F1CC503A2008F279E /* FlyImageDataFileManagerTests.m */, DD9D97301CC503A2008F279E /* FlyImageDataFileTests.m */, DD9D97311CC503A2008F279E /* FlyImageDownloadManagerTests.m */, DD9D97321CC503A2008F279E /* FlyImageIconCacheTests.m */, DD9D96ED1CC50336008F279E /* Info.plist */, ); path = FlyImageTests; sourceTree = ""; }; DD9D96F61CC5034A008F279E /* Core */ = { isa = PBXGroup; children = ( DD9D96F71CC5034A008F279E /* FlyImageCache.h */, DD9D96F81CC5034A008F279E /* FlyImageCache.m */, DD9D96F91CC5034A008F279E /* FlyImageCacheProtocol.h */, DD9D96FA1CC5034A008F279E /* FlyImageDataFile.h */, DD9D96FB1CC5034A008F279E /* FlyImageDataFile.m */, DD9D96FC1CC5034A008F279E /* FlyImageDataFileManager.h */, DD9D96FD1CC5034A008F279E /* FlyImageDataFileManager.m */, DD9D96FE1CC5034A008F279E /* FlyImageDecoder.h */, DD9D96FF1CC5034A008F279E /* FlyImageDecoder.m */, DD9D97001CC5034A008F279E /* FlyImageDownloader.h */, DD9D97011CC5034A008F279E /* FlyImageDownloader.m */, DD9D97021CC5034A008F279E /* FlyImageEncoder.h */, DD9D97031CC5034A008F279E /* FlyImageEncoder.m */, DD9D97041CC5034A008F279E /* FlyImageIconCache.h */, DD9D97051CC5034A008F279E /* FlyImageIconCache.m */, DD9D97061CC5034A008F279E /* FlyImageUtils.h */, DD9D97071CC5034A008F279E /* FlyImageUtils.m */, DD4D2C4F1D5C678E001F5D77 /* FlyImageRetrieveOperation.h */, DD4D2C501D5C678E001F5D77 /* FlyImageRetrieveOperation.m */, ); name = Core; path = core; sourceTree = ""; }; DDDAE6A11CE6E10C00B3CCCC /* UI */ = { isa = PBXGroup; children = ( DDDAE6A21CE6E10C00B3CCCC /* CALayer+FlyImageCache.h */, DDDAE6A31CE6E10C00B3CCCC /* CALayer+FlyImageCache.m */, DDDAE6A41CE6E10C00B3CCCC /* CALayer+FlyImageIconCache.h */, DDDAE6A51CE6E10C00B3CCCC /* CALayer+FlyImageIconCache.m */, DDDAE6A61CE6E10C00B3CCCC /* FlyImageIconRenderer.h */, DDDAE6A71CE6E10C00B3CCCC /* FlyImageIconRenderer.m */, DDDAE6A81CE6E10C00B3CCCC /* FlyImageRenderer.h */, DDDAE6A91CE6E10C00B3CCCC /* FlyImageRenderer.m */, DDDAE6AA1CE6E10C00B3CCCC /* UIImageView+FlyImageCache.h */, DDDAE6AB1CE6E10C00B3CCCC /* UIImageView+FlyImageCache.m */, DDDAE6AC1CE6E10C00B3CCCC /* UIImageView+FlyImageIconCache.h */, DDDAE6AD1CE6E10C00B3CCCC /* UIImageView+FlyImageIconCache.m */, DDD8FB5A1D59D0F900821392 /* FlyImageIconCacheUIProtocol.h */, DDD8FB5D1D59D24900821392 /* FlyImageCacheUIProtocol.h */, ); path = UI; sourceTree = ""; }; E115FD25013B3B36CF3058C3 /* Frameworks */ = { isa = PBXGroup; children = ( DD4C0C911CCEF78700FF7E28 /* libwebp.framework */, DDF65B101CC70DE50066B12F /* UIKit.framework */, E7D2BCD141491AB1301A46CC /* libPods-FlyImage.a */, ); name = Frameworks; sourceTree = ""; }; F9DB01F8FD4F9561A9D733F0 /* Pods */ = { isa = PBXGroup; children = ( C05497892C966A58859B6B98 /* Pods-FlyImage.debug.xcconfig */, 2054736E2EDA981DF80AD030 /* Pods-FlyImage.release.xcconfig */, ); name = Pods; sourceTree = ""; }; /* End PBXGroup section */ /* Begin PBXHeadersBuildPhase section */ DD9D96D91CC50336008F279E /* Headers */ = { isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; files = ( DD9D97231CC5034A008F279E /* FlyImageUtils.h in Headers */, DD9D97141CC5034A008F279E /* FlyImageCache.h in Headers */, DD4D2C511D5C678E001F5D77 /* FlyImageRetrieveOperation.h in Headers */, DD9D971D1CC5034A008F279E /* FlyImageDownloader.h in Headers */, DD9D97211CC5034A008F279E /* FlyImageIconCache.h in Headers */, DDDAE6B01CE6E10C00B3CCCC /* CALayer+FlyImageIconCache.h in Headers */, DDD8FB5B1D59D0F900821392 /* FlyImageIconCacheUIProtocol.h in Headers */, DDDAE6B61CE6E10C00B3CCCC /* UIImageView+FlyImageCache.h in Headers */, DDDAE6B81CE6E10C00B3CCCC /* UIImageView+FlyImageIconCache.h in Headers */, DDDAE6AE1CE6E10C00B3CCCC /* CALayer+FlyImageCache.h in Headers */, DDD8FB5E1D59D24900821392 /* FlyImageCacheUIProtocol.h in Headers */, DD9D97191CC5034A008F279E /* FlyImageDataFileManager.h in Headers */, DDDAE6B21CE6E10C00B3CCCC /* FlyImageIconRenderer.h in Headers */, DDDAE6B41CE6E10C00B3CCCC /* FlyImageRenderer.h in Headers */, DD9D97161CC5034A008F279E /* FlyImageCacheProtocol.h in Headers */, DD9D97171CC5034A008F279E /* FlyImageDataFile.h in Headers */, DD9D971B1CC5034A008F279E /* FlyImageDecoder.h in Headers */, DD9D96E01CC50336008F279E /* FlyImage.h in Headers */, DD9D971F1CC5034A008F279E /* FlyImageEncoder.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXHeadersBuildPhase section */ /* Begin PBXNativeTarget section */ DD9D96DB1CC50336008F279E /* FlyImage */ = { isa = PBXNativeTarget; buildConfigurationList = DD9D96F01CC50336008F279E /* Build configuration list for PBXNativeTarget "FlyImage" */; buildPhases = ( 682C7851251E962AD95E8BD5 /* [CP] Check Pods Manifest.lock */, DD9D96D71CC50336008F279E /* Sources */, DD9D96D81CC50336008F279E /* Frameworks */, DD9D96D91CC50336008F279E /* Headers */, DD9D96DA1CC50336008F279E /* Resources */, 5B8B2D959E77A65AC0CAB76B /* [CP] Copy Pods Resources */, ); buildRules = ( ); dependencies = ( ); name = FlyImage; productName = FlyImage; productReference = DD9D96DC1CC50336008F279E /* FlyImage.framework */; productType = "com.apple.product-type.framework"; }; DD9D96E51CC50336008F279E /* FlyImageTests */ = { isa = PBXNativeTarget; buildConfigurationList = DD9D96F31CC50336008F279E /* Build configuration list for PBXNativeTarget "FlyImageTests" */; buildPhases = ( DD9D96E21CC50336008F279E /* Sources */, DD9D96E31CC50336008F279E /* Frameworks */, DD9D96E41CC50336008F279E /* Resources */, ); buildRules = ( ); dependencies = ( DD9D96E91CC50336008F279E /* PBXTargetDependency */, ); name = FlyImageTests; productName = FlyImageTests; productReference = DD9D96E61CC50336008F279E /* FlyImageTests.xctest */; productType = "com.apple.product-type.bundle.unit-test"; }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ DD9D96D31CC50336008F279E /* Project object */ = { isa = PBXProject; attributes = { LastUpgradeCheck = 0830; ORGANIZATIONNAME = Augmn; TargetAttributes = { DD9D96DB1CC50336008F279E = { CreatedOnToolsVersion = 7.3; }; DD9D96E51CC50336008F279E = { CreatedOnToolsVersion = 7.3; }; }; }; buildConfigurationList = DD9D96D61CC50336008F279E /* Build configuration list for PBXProject "FlyImage" */; compatibilityVersion = "Xcode 3.2"; developmentRegion = English; hasScannedForEncodings = 0; knownRegions = ( en, ); mainGroup = DD9D96D21CC50336008F279E; productRefGroup = DD9D96DD1CC50336008F279E /* Products */; projectDirPath = ""; projectRoot = ""; targets = ( DD9D96DB1CC50336008F279E /* FlyImage */, DD9D96E51CC50336008F279E /* FlyImageTests */, ); }; /* End PBXProject section */ /* Begin PBXResourcesBuildPhase section */ DD9D96DA1CC50336008F279E /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( ); runOnlyForDeploymentPostprocessing = 0; }; DD9D96E41CC50336008F279E /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ 5B8B2D959E77A65AC0CAB76B /* [CP] Copy Pods Resources */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputPaths = ( ); name = "[CP] Copy Pods Resources"; outputPaths = ( ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-FlyImage/Pods-FlyImage-resources.sh\"\n"; showEnvVarsInLog = 0; }; 682C7851251E962AD95E8BD5 /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputPaths = ( ); name = "[CP] Check Pods Manifest.lock"; outputPaths = ( ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n"; showEnvVarsInLog = 0; }; /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ DD9D96D71CC50336008F279E /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( DDDAE6B31CE6E10C00B3CCCC /* FlyImageIconRenderer.m in Sources */, DD9D971C1CC5034A008F279E /* FlyImageDecoder.m in Sources */, DD9D97241CC5034A008F279E /* FlyImageUtils.m in Sources */, DD9D97151CC5034A008F279E /* FlyImageCache.m in Sources */, DD9D971E1CC5034A008F279E /* FlyImageDownloader.m in Sources */, DD9D97181CC5034A008F279E /* FlyImageDataFile.m in Sources */, DDDAE6B71CE6E10C00B3CCCC /* UIImageView+FlyImageCache.m in Sources */, DDDAE6B51CE6E10C00B3CCCC /* FlyImageRenderer.m in Sources */, DD4D2C521D5C678E001F5D77 /* FlyImageRetrieveOperation.m in Sources */, DDDAE6B11CE6E10C00B3CCCC /* CALayer+FlyImageIconCache.m in Sources */, DD9D97201CC5034A008F279E /* FlyImageEncoder.m in Sources */, DDDAE6B91CE6E10C00B3CCCC /* UIImageView+FlyImageIconCache.m in Sources */, DD9D971A1CC5034A008F279E /* FlyImageDataFileManager.m in Sources */, DD9D97221CC5034A008F279E /* FlyImageIconCache.m in Sources */, DDDAE6AF1CE6E10C00B3CCCC /* CALayer+FlyImageCache.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; DD9D96E21CC50336008F279E /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( DD9D97341CC503A2008F279E /* FlyImageCacheTests.m in Sources */, DD9D97381CC503A2008F279E /* FlyImageIconCacheTests.m in Sources */, DD9D97361CC503A2008F279E /* FlyImageDataFileTests.m in Sources */, DD9D97351CC503A2008F279E /* FlyImageDataFileManagerTests.m in Sources */, DD9D97371CC503A2008F279E /* FlyImageDownloadManagerTests.m in Sources */, DD4D2C531D5C678E001F5D77 /* FlyImageRetrieveOperation.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXSourcesBuildPhase section */ /* Begin PBXTargetDependency section */ DD9D96E91CC50336008F279E /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = DD9D96DB1CC50336008F279E /* FlyImage */; targetProxy = DD9D96E81CC50336008F279E /* PBXContainerItemProxy */; }; /* End PBXTargetDependency section */ /* Begin XCBuildConfiguration section */ DD9D96EE1CC50336008F279E /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_NONNULL = YES; 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_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; COPY_PHASE_STRIP = NO; CURRENT_PROJECT_VERSION = 1; 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 = 8.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; TARGETED_DEVICE_FAMILY = "1,2"; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; }; name = Debug; }; DD9D96EF1CC50336008F279E /* Release */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_NONNULL = YES; 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_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; COPY_PHASE_STRIP = NO; CURRENT_PROJECT_VERSION = 1; 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 = 8.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; TARGETED_DEVICE_FAMILY = "1,2"; VALIDATE_PRODUCT = YES; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; }; name = Release; }; DD9D96F11CC50336008F279E /* Debug */ = { isa = XCBuildConfiguration; baseConfigurationReference = C05497892C966A58859B6B98 /* Pods-FlyImage.debug.xcconfig */; buildSettings = { ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; FRAMEWORK_SEARCH_PATHS = "$(inherited)"; INFOPLIST_FILE = FlyImage/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 8.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; LIBRARY_SEARCH_PATHS = "$(inherited)"; PRODUCT_BUNDLE_IDENTIFIER = com.flyimage.imagecache; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; }; name = Debug; }; DD9D96F21CC50336008F279E /* Release */ = { isa = XCBuildConfiguration; baseConfigurationReference = 2054736E2EDA981DF80AD030 /* Pods-FlyImage.release.xcconfig */; buildSettings = { ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; FRAMEWORK_SEARCH_PATHS = "$(inherited)"; INFOPLIST_FILE = FlyImage/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 8.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; LIBRARY_SEARCH_PATHS = "$(inherited)"; PRODUCT_BUNDLE_IDENTIFIER = com.flyimage.imagecache; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; }; name = Release; }; DD9D96F41CC50336008F279E /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { INFOPLIST_FILE = FlyImageTests/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = com.flyimage.imagecache.FlyImageTests; PRODUCT_NAME = "$(TARGET_NAME)"; }; name = Debug; }; DD9D96F51CC50336008F279E /* Release */ = { isa = XCBuildConfiguration; buildSettings = { INFOPLIST_FILE = FlyImageTests/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = com.flyimage.imagecache.FlyImageTests; PRODUCT_NAME = "$(TARGET_NAME)"; }; name = Release; }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ DD9D96D61CC50336008F279E /* Build configuration list for PBXProject "FlyImage" */ = { isa = XCConfigurationList; buildConfigurations = ( DD9D96EE1CC50336008F279E /* Debug */, DD9D96EF1CC50336008F279E /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; DD9D96F01CC50336008F279E /* Build configuration list for PBXNativeTarget "FlyImage" */ = { isa = XCConfigurationList; buildConfigurations = ( DD9D96F11CC50336008F279E /* Debug */, DD9D96F21CC50336008F279E /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; DD9D96F31CC50336008F279E /* Build configuration list for PBXNativeTarget "FlyImageTests" */ = { isa = XCConfigurationList; buildConfigurations = ( DD9D96F41CC50336008F279E /* Debug */, DD9D96F51CC50336008F279E /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; /* End XCConfigurationList section */ }; rootObject = DD9D96D31CC50336008F279E /* Project object */; } ================================================ FILE: FlyImage.xcworkspace/contents.xcworkspacedata ================================================ ================================================ FILE: FlyImage.xcworkspace/xcshareddata/xcschemes/FlyImage.xcscheme ================================================ ================================================ FILE: FlyImage.xcworkspace/xcshareddata/xcschemes/FlyImageTests.xcscheme ================================================ ================================================ FILE: FlyImageTests/FlyImageCacheTests.m ================================================ // // FlyImageCacheTests.m // Demo // // Created by Norris Tong on 4/3/16. // Copyright © 2016 NorrisTong. All rights reserved. // #import #import "FlyImageCache.h" #import "FlyImageDataFIleManager.h" @interface FlyImageCacheTests : XCTestCase @end static FlyImageCache* _imageCache; static CGFloat imageWidth = 1920.0; static CGFloat imageHeight = 1200.0; static FlyImageDataFileManager* _fileManager; static int kMultipleTimes = 15; @implementation FlyImageCacheTests - (void)setUp { [super setUp]; // Put setup code here. This method is called before the invocation of each test method in the class. if (_imageCache == nil) { _imageCache = [FlyImageCache sharedInstance]; _fileManager = [_imageCache valueForKey:@"dataFileManager"]; } } - (void)tearDown { // Put teardown code here. This method is called after the invocation of each test method in the class. [super tearDown]; } - (void)addImageFile:(NSString*)name { // generate an image with special size CGRect rect = CGRectMake(0.0f, 0.0f, imageWidth, imageHeight); UIGraphicsBeginImageContext(rect.size); CGContextRef context = UIGraphicsGetCurrentContext(); CGContextSetFillColorWithColor(context, [[UIColor redColor] CGColor]); CGContextFillRect(context, rect); UIImage* image = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); NSData* imageData = UIImagePNGRepresentation(image); NSString* directoryPath = [_fileManager folderPath]; NSString* imagePath = [directoryPath stringByAppendingPathComponent:name]; [imageData writeToFile:imagePath atomically:YES]; [_fileManager addExistFileName:name]; } - (void)drawALineInContext:(CGContextRef)context rect:(CGRect)rect { UIGraphicsPushContext(context); CGContextSetStrokeColorWithColor(context, [UIColor redColor].CGColor); CGContextSetLineWidth(context, 10.0); CGContextMoveToPoint(context, 0.0, 0.0); CGContextAddLineToPoint(context, rect.size.width, rect.size.height); UIGraphicsPopContext(); } - (void)test10AddImage { XCTestExpectation* expectation = [self expectationWithDescription:@"test10AddImage"]; NSString* filename = @"10"; [self addImageFile:filename]; [_imageCache addImageWithKey:filename filename:filename completed:^(NSString* key, UIImage* image) { XCTAssert( image.size.width == imageWidth ); XCTAssert( image.size.height == imageHeight ); [expectation fulfill]; }]; [self waitForExpectationsWithTimeout:10 handler:^(NSError* error) { XCTAssert(YES, @"Pass"); }]; } - (void)test11AddMultipleTimes { XCTestExpectation* expectation = [self expectationWithDescription:@"test11AddMultipleTimes"]; NSString* filename = @"11"; [self addImageFile:filename]; __block int sum = 0; for (int i = 0; i < kMultipleTimes; i++) { [_imageCache addImageWithKey:filename filename:filename completed:^(NSString* key, UIImage* image) { XCTAssert( image.size.width == imageWidth ); XCTAssert( image.size.height == imageHeight ); sum++; if ( sum == kMultipleTimes ){ [expectation fulfill]; } }]; } [self waitForExpectationsWithTimeout:30 handler:^(NSError* error) { XCTAssert(YES, @"Pass"); }]; } - (void)test13AddMultipleKeys { XCTestExpectation* expectation = [self expectationWithDescription:@"test13AddMultipleKeys"]; __block int sum = 0; for (int i = 1; i <= kMultipleTimes; i++) { NSString* filename = [NSString stringWithFormat:@"%d", i]; [self addImageFile:filename]; [_imageCache addImageWithKey:filename filename:filename completed:^(NSString* key, UIImage* image) { XCTAssert( image.size.width == imageWidth ); XCTAssert( image.size.height == imageHeight ); sum++; if ( sum == kMultipleTimes ){ [expectation fulfill]; } }]; } [self waitForExpectationsWithTimeout:100 handler:^(NSError* error) { XCTAssert(YES, @"Pass"); }]; } - (void)test30AsyncGetImage { XCTestExpectation* expectation = [self expectationWithDescription:@"test30AsyncGetImage"]; [_imageCache asyncGetImageWithKey:@"10" completed:^(NSString* key, UIImage* image) { XCTAssert( image.size.width == imageWidth ); XCTAssert( image.size.height == imageHeight ); [expectation fulfill]; }]; [self waitForExpectationsWithTimeout:10 handler:^(NSError* error) { XCTAssert(YES, @"Pass"); }]; } - (void)test30AsyncGetImageMultipleTimes { XCTestExpectation* expectation = [self expectationWithDescription:@"test30AsyncGetImageMultipleTimes"]; NSString* filename = @"10"; __block int sum = 0; for (int i = 0; i < kMultipleTimes; i++) { [_imageCache asyncGetImageWithKey:filename drawSize:CGSizeMake(500, 800) contentsGravity:kCAGravityResizeAspect cornerRadius:0 completed:^(NSString* key, UIImage* image) { XCTAssert( image.size.width == 500 ); XCTAssert( image.size.height == 800 ); sum++; if ( sum == kMultipleTimes ){ [expectation fulfill]; } }]; } [self waitForExpectationsWithTimeout:30 handler:^(NSError* error) { XCTAssert(YES, @"Pass"); }]; } - (void)test50RemoveImage { NSString* imageKey = @"11"; [_imageCache removeImageWithKey:imageKey]; XCTAssert(![_imageCache isImageExistWithKey:imageKey]); } - (void)test60ImagePath { XCTAssert([_imageCache imagePathWithKey:@"10"] != nil); XCTAssert([_imageCache imagePathWithKey:@"11"] == nil); } - (void)test80ChangeImageKey { XCTestExpectation* expectation = [self expectationWithDescription:@"test80ChangeImageKey"]; [_imageCache changeImageKey:@"10" newKey:@"newKey"]; XCTAssert(![_imageCache isImageExistWithKey:@"10"]); XCTAssert([_imageCache isImageExistWithKey:@"newKey"]); [_imageCache asyncGetImageWithKey:@"newKey" completed:^(NSString* key, UIImage* image) { XCTAssert( image.size.width == imageWidth ); XCTAssert( image.size.height == imageHeight ); [expectation fulfill]; }]; [self waitForExpectationsWithTimeout:10 handler:^(NSError* error) { XCTAssert(YES, @"Pass"); }]; } - (void)test90Purge { [_imageCache purge]; XCTAssert(![_imageCache isImageExistWithKey:@"10"]); } @end ================================================ FILE: FlyImageTests/FlyImageDataFileManagerTests.m ================================================ // // FlyImageDataFileManagerTests.m // Demo // // Created by Norris Tong on 4/2/16. // Copyright © 2016 NorrisTong. All rights reserved. // #import #import "FlyImageDataFileManager.h" @interface FlyImageDataFileManagerTests : XCTestCase @end static FlyImageDataFileManager* _fileManager; @implementation FlyImageDataFileManagerTests - (void)setUp { [super setUp]; // Put setup code here. This method is called before the invocation of each test method in the class. // Put setup code here. This method is called before the invocation of each test method in the class. if (_fileManager == nil) { NSString* directoryPath = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES)[0]; NSString* folderPath = [directoryPath stringByAppendingPathComponent:@"flyimage2/files"]; _fileManager = [[FlyImageDataFileManager alloc] initWithFolderPath:folderPath]; } } - (void)tearDown { // Put teardown code here. This method is called after the invocation of each test method in the class. [super tearDown]; } - (void)test10Create { XCTestExpectation* expectation = [self expectationWithDescription:@"test10Create"]; [_fileManager asyncCreateFileWithName:@"10" completed:^(FlyImageDataFile* dataFile) { XCTAssert( dataFile != nil ); [expectation fulfill]; }]; [self waitForExpectationsWithTimeout:10 handler:^(NSError* error) { XCTAssert(YES, @"Pass"); }]; } - (void)test11CreateMultipleTimes { XCTestExpectation* expectation = [self expectationWithDescription:@"test11CreateMultipleTimes"]; __block int sum = 0; for (int i = 0; i < 100; i++) { [_fileManager asyncCreateFileWithName:@"11" completed:^(FlyImageDataFile* dataFile) { XCTAssert( dataFile != nil ); sum++; if ( sum == 100 ){ [expectation fulfill]; } }]; } [self waitForExpectationsWithTimeout:10 handler:^(NSError* error) { XCTAssert(YES, @"Pass"); }]; } - (void)test12CreateSameName { XCTestExpectation* expectation = [self expectationWithDescription:@"test12CreateSameName"]; [_fileManager asyncCreateFileWithName:@"10" completed:^(FlyImageDataFile* dataFile) { XCTAssert( dataFile != nil ); [expectation fulfill]; }]; [self waitForExpectationsWithTimeout:10 handler:^(NSError* error) { XCTAssert(YES, @"Pass"); }]; } - (void)test13CreateMultipleNames { XCTestExpectation* expectation = [self expectationWithDescription:@"test13CreateMultipleNames"]; __block int sum = 0; for (int i = 1; i <= 100; i++) { [_fileManager asyncCreateFileWithName:[NSString stringWithFormat:@"%d", i] completed:^(FlyImageDataFile* dataFile) { XCTAssert( dataFile != nil ); sum++; if ( sum == 100 ){ [expectation fulfill]; } }]; } [self waitForExpectationsWithTimeout:10 handler:^(NSError* error) { XCTAssert(YES, @"Pass"); }]; } - (void)test15SyncCreate { id dataFile = [_fileManager createFileWithName:@"100"]; XCTAssert(dataFile != nil); } - (void)test20IsExist { XCTAssert([_fileManager isFileExistWithName:@"10"]); XCTAssert([_fileManager isFileExistWithName:@"11"]); XCTAssert(![_fileManager isFileExistWithName:@"NotExist"]); } - (void)test30Retrieve { FlyImageDataFile* file10 = [_fileManager retrieveFileWithName:@"10"]; XCTAssert(file10 != nil); FlyImageDataFile* file11 = [_fileManager retrieveFileWithName:@"11"]; XCTAssert(file11 != nil); FlyImageDataFile* fileNotExist = [_fileManager retrieveFileWithName:@"NotExist"]; XCTAssert(fileNotExist == nil); } - (void)test50Remove { [_fileManager removeFileWithName:@"10"]; XCTAssert(![_fileManager isFileExistWithName:@"10"]); } - (void)test90Purge { XCTestExpectation* expectation = [self expectationWithDescription:@"test90Purge"]; [_fileManager purgeWithExceptions:@[ @"11", @"10" ] toSize:0 completed:^(NSUInteger fileCount, NSUInteger totalSize) { XCTAssert( fileCount > 0 ); XCTAssert( totalSize > 0 ); [expectation fulfill]; }]; [self waitForExpectationsWithTimeout:10 handler:^(NSError* error) { XCTAssert(YES, @"Pass"); }]; } - (void)test99PurgeAll { XCTestExpectation* expectation = [self expectationWithDescription:@"test99PurgeAll"]; [_fileManager purgeWithExceptions:nil toSize:0 completed:^(NSUInteger fileCount, NSUInteger totalSize) { XCTAssert( fileCount == 0 ); XCTAssert( totalSize == 0 ); [expectation fulfill]; }]; [self waitForExpectationsWithTimeout:1000 handler:^(NSError* error) { XCTAssert(YES, @"Pass"); }]; } @end ================================================ FILE: FlyImageTests/FlyImageDataFileTests.m ================================================ // // FlyImageDataFileTests.m // Demo // // Created by Norris Tong on 16/3/20. // Copyright © 2016年 NorrisTong. All rights reserved. // #import #import "FlyImageDataFile.h" #import "FlyImageUtils.h" #import @interface FlyImageDataFileTests : XCTestCase @end static FlyImageDataFile* _dataFile; static size_t kTestLength = 4096 * 10; static int kTestCount = 50; static CGFloat imageWidth = 1920.0; static CGFloat imageHeight = 1200.0; static NSString* testDataFileName = @"testDataFile"; @implementation FlyImageDataFileTests - (void)setUp { [super setUp]; // Put setup code here. This method is called before the invocation of each test method in the class. if (_dataFile == nil) { NSString* filePath = [[FlyImageUtils directoryPath] stringByAppendingPathComponent:testDataFileName]; BOOL isFileExist = [[NSFileManager defaultManager] fileExistsAtPath:filePath]; if (!isFileExist) { [[NSFileManager defaultManager] createFileAtPath:filePath contents:nil attributes:nil]; } _dataFile = [[FlyImageDataFile alloc] initWithPath:filePath]; } } - (void)tearDown { // Put teardown code here. This method is called after the invocation of each test method in the class. [super tearDown]; } - (void)test1Open { XCTAssert([_dataFile open]); } - (void)test20PrepareAppendDataWrongLength { XCTAssert(![_dataFile prepareAppendDataWithOffset:0 length:200 * 1024 * 1024]); } - (void)test30AppendData { BOOL ret = YES; for (int i = 0; i < kTestCount; i++) { ret &= [_dataFile prepareAppendDataWithOffset:_dataFile.pointer length:kTestLength]; memset(_dataFile.address, 1, kTestLength); ret &= [_dataFile appendDataWithOffset:_dataFile.pointer length:kTestLength]; if (!ret) { break; } } XCTAssert(ret, @"Pass"); } - (void)createImageAtPath:(NSString*)path { // generate an image with special size CGRect rect = CGRectMake(0.0f, 0.0f, imageWidth, imageHeight); UIGraphicsBeginImageContext(rect.size); CGContextRef context = UIGraphicsGetCurrentContext(); CGContextSetFillColorWithColor(context, [[UIColor redColor] CGColor]); CGContextFillRect(context, rect); UIImage* image = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); NSData* imageData = UIImagePNGRepresentation(image); [imageData writeToFile:path atomically:YES]; } - (void)test50Memcpy { XCTestExpectation* expectation = [self expectationWithDescription:@"memcpy"]; NSString* directoryPath = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES)[0]; NSString* imagePath = [directoryPath stringByAppendingPathComponent:@"image"]; [self createImageAtPath:imagePath]; NSData* data = [NSData dataWithContentsOfFile:imagePath]; UIImage* image = [UIImage imageWithData:data]; CGSize imageSize = image.size; [data enumerateByteRangesUsingBlock:^(const void* _Nonnull bytes, NSRange byteRange, BOOL* _Nonnull stop) { size_t length = byteRange.length; ssize_t pageSize = [FlyImageUtils pageSize]; size_t correctLength = ceil((length / pageSize )) * pageSize; BOOL ret = YES; ret &= [_dataFile prepareAppendDataWithOffset:0 length:correctLength]; memcpy(_dataFile.address, bytes, correctLength); ret &= [_dataFile appendDataWithOffset:0 length:correctLength]; XCTAssert( ret, @"Pass" ); // Create CGImageRef whose backing store *is* the mapped image table entry. We avoid a memcpy this way. CGDataProviderRef dataProvider = CGDataProviderCreateWithData(NULL, _dataFile.address, correctLength, nil); CGBitmapInfo bitmapInfo = kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host; NSInteger bitsPerComponent = 8; NSInteger bitsPerPixel = 4 * 8; CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); static NSInteger bytesPerPixel = 4; static float kAlignment = 64; size_t bytesPerRow = ceil((imageSize.width * bytesPerPixel) / kAlignment) * kAlignment; CGImageRef imageRef = CGImageCreate(imageSize.width, imageSize.height, bitsPerComponent, bitsPerPixel, bytesPerRow, colorSpace, bitmapInfo, dataProvider, NULL, false, (CGColorRenderingIntent)0); CGDataProviderRelease(dataProvider); CGColorSpaceRelease(colorSpace); UIImage *revertImage = [UIImage imageWithCGImage:imageRef]; XCTAssert( revertImage != nil, @"Pass" ); CGSize revertSize = revertImage.size; XCTAssert( imageSize.width == revertSize.width && imageSize.height == imageSize.height, @"Pass" ); [expectation fulfill]; }]; [self waitForExpectationsWithTimeout:10 handler:^(NSError* error) { [[NSFileManager defaultManager] removeItemAtPath:imagePath error:nil]; XCTAssert(YES, @"Pass"); }]; } - (void)test99Remove { [_dataFile close]; _dataFile = nil; NSString* filePath = [[FlyImageUtils directoryPath] stringByAppendingPathComponent:testDataFileName]; [[NSFileManager defaultManager] removeItemAtPath:filePath error:nil]; XCTAssert(YES, @"Pass"); } @end ================================================ FILE: FlyImageTests/FlyImageDownloadManagerTests.m ================================================ // // FlyImageDownloaderTests.m // Demo // // Created by Norris Tong on 4/4/16. // Copyright © 2016 NorrisTong. All rights reserved. // #import #import "FlyImageDownloader.h" @interface DownloadManagerClient : NSObject @property (nonatomic, strong) XCTestExpectation* expectation; @end static const NSString* kAssetHost = @"https://flyimage.oss-us-west-1.aliyuncs.com/"; static int kMultipleTimes = 15; @implementation DownloadManagerClient - (void)FlyImageDownloader:(FlyImageDownloader*)manager willSendRequest:(NSURLRequest*)request { NSAssert(request != nil, nil); } - (void)FlyImageDownloader:(FlyImageDownloader*)manager didReceiveResponse:(NSURLResponse*)response filePath:(NSURL*)filePath error:(NSError*)error request:(NSURLRequest*)request { NSAssert(request != nil, nil); [self.expectation fulfill]; } - (void)FlyImageDownloader:(FlyImageDownloader*)manager willCancelRequest:(NSURLRequest*)request { NSAssert(request != nil, nil); [self.expectation fulfill]; } @end @interface FlyImageDownloaderTests : XCTestCase @end static FlyImageDownloader* _downloadManager; @implementation FlyImageDownloaderTests - (void)setUp { [super setUp]; // Put setup code here. This method is called before the invocation of each test method in the class. if (_downloadManager == nil) { _downloadManager = [FlyImageDownloader sharedInstance]; } } - (void)tearDown { // Put teardown code here. This method is called after the invocation of each test method in the class. [super tearDown]; _downloadManager.delegate = nil; } - (void)test10Success { XCTestExpectation* expectation = [self expectationWithDescription:@"test10AddImage"]; NSString* imagePath = [kAssetHost stringByAppendingPathComponent:@"10.jpg"]; NSURL* url = [NSURL URLWithString:imagePath]; NSMutableURLRequest* request = [NSMutableURLRequest requestWithURL:url]; request.timeoutInterval = 30; [_downloadManager downloadImageForURLRequest:request progress:^(float percentage) { XCTAssert( percentage >= 0 && percentage <= 1 ); } success:^(NSURLRequest* request, NSURL* filePath) { NSData *data = [[NSData alloc] initWithContentsOfURL:filePath]; UIImage *image = [UIImage imageWithData:data]; XCTAssert( image.size.width == 1024 ); [expectation fulfill]; } failed:^(NSURLRequest* request, NSError* error) { XCTAssert( NO ); [expectation fulfill]; }]; [self waitForExpectationsWithTimeout:60 handler:^(NSError* error) { XCTAssert(YES, @"Pass"); }]; } - (void)test30Failed { XCTestExpectation* expectation = [self expectationWithDescription:@"test30Failed"]; NSString* imagePath = [kAssetHost stringByAppendingPathComponent:@"xxx"]; NSURL* url = [NSURL URLWithString:imagePath]; NSMutableURLRequest* request = [NSMutableURLRequest requestWithURL:url]; request.timeoutInterval = 30; [_downloadManager downloadImageForURLRequest:request progress:nil success:^(NSURLRequest* request, NSURL* filePath) { XCTAssert( NO ); [expectation fulfill]; } failed:^(NSURLRequest* request, NSError* error) { XCTAssert( error != nil ); [expectation fulfill]; }]; [self waitForExpectationsWithTimeout:60 handler:^(NSError* error) { XCTAssert(YES, @"Pass"); }]; } - (void)test31FailedMultiple { XCTestExpectation* expectation = [self expectationWithDescription:@"test31FailedMultiple"]; NSString* imagePath = [kAssetHost stringByAppendingPathComponent:@"xxx"]; __block int sum = 0; for (int i = 0; i < kMultipleTimes; i++) { NSURL* url = [NSURL URLWithString:imagePath]; NSMutableURLRequest* request = [NSMutableURLRequest requestWithURL:url]; request.timeoutInterval = 5; [_downloadManager downloadImageForURLRequest:request progress:nil success:^(NSURLRequest* request, NSURL* filePath) { XCTAssert( NO ); } failed:^(NSURLRequest* request, NSError* error) { XCTAssert( error != nil ); sum++; if ( sum == kMultipleTimes ){ [expectation fulfill]; } }]; } [self waitForExpectationsWithTimeout:60 handler:^(NSError* error) { XCTAssert(YES, @"Pass"); }]; } - (void)test50CancelTask { XCTestExpectation* expectation = [self expectationWithDescription:@"test90Cancel"]; NSString* imagePath = [kAssetHost stringByAppendingPathComponent:@"11.jpg"]; NSURL* url = [NSURL URLWithString:imagePath]; NSURLRequest* request = [[NSURLRequest alloc] initWithURL:url]; FlyImageDownloadHandlerId* handlerId = [_downloadManager downloadImageForURLRequest:request progress:nil success:^(NSURLRequest* request, NSURL* filePath) { XCTAssert( NO ); [expectation fulfill]; } failed:^(NSURLRequest* request, NSError* error) { XCTAssert( error != nil ); [expectation fulfill]; }]; [_downloadManager cancelDownloadHandler:handlerId]; [self waitForExpectationsWithTimeout:60 handler:^(NSError* error) { XCTAssert(YES, @"Pass"); }]; } - (void)test51CancelHandler { XCTestExpectation* expectation = [self expectationWithDescription:@"test91CancelMultipleTimes"]; NSString* imagePath = [kAssetHost stringByAppendingPathComponent:@"12.jpg"]; NSURL* url = [NSURL URLWithString:imagePath]; NSURLRequest* request = [[NSURLRequest alloc] initWithURL:url]; FlyImageDownloadHandlerId* handlerId = [_downloadManager downloadImageForURLRequest:request progress:nil success:^(NSURLRequest* request, NSURL* filePath) { XCTAssert( NO ); [expectation fulfill]; } failed:^(NSURLRequest* request, NSError* error) { XCTAssert( error != nil ); [expectation fulfill]; }]; for (int i = 0; i < kMultipleTimes; i++) { [_downloadManager downloadImageForURLRequest:request progress:nil success:^(NSURLRequest* request, NSURL* filePath) { XCTAssert( NO ); [expectation fulfill]; } failed:^(NSURLRequest* request, NSError* error) { XCTAssert( NO ); [expectation fulfill]; }]; } [_downloadManager cancelDownloadHandler:handlerId]; [self waitForExpectationsWithTimeout:60 handler:^(NSError* error) { XCTAssert(YES, @"Pass"); }]; } - (void)test90DelegateSend { XCTestExpectation* expectation = [self expectationWithDescription:@"test50Delegate"]; DownloadManagerClient* client = [[DownloadManagerClient alloc] init]; client.expectation = expectation; _downloadManager.delegate = client; NSString* imagePath = [kAssetHost stringByAppendingPathComponent:@"13.jpg"]; NSURL* url = [NSURL URLWithString:imagePath]; NSURLRequest* request = [[NSURLRequest alloc] initWithURL:url]; [_downloadManager downloadImageForURLRequest:request]; [self waitForExpectationsWithTimeout:60 handler:^(NSError* error) { XCTAssert(YES, @"Pass"); }]; } - (void)test91DelegateCancel { XCTestExpectation* expectation = [self expectationWithDescription:@"test50Delegate"]; DownloadManagerClient* client = [[DownloadManagerClient alloc] init]; client.expectation = expectation; _downloadManager.delegate = client; NSString* imagePath = [kAssetHost stringByAppendingPathComponent:@"14.jpg"]; NSURL* url = [NSURL URLWithString:imagePath]; NSURLRequest* request = [[NSURLRequest alloc] initWithURL:url]; FlyImageDownloadHandlerId* handlerId = [_downloadManager downloadImageForURLRequest:request]; [_downloadManager cancelDownloadHandler:handlerId]; [self waitForExpectationsWithTimeout:60 handler:^(NSError* error) { XCTAssert(YES, @"Pass"); }]; } @end ================================================ FILE: FlyImageTests/FlyImageIconCacheTests.m ================================================ // // FlyImageIconCacheTests.m // Demo // // Created by Norris Tong on 4/3/16. // Copyright © 2016 NorrisTong. All rights reserved. // #import #import "FlyImageIconCache.h" @interface FlyImageIconCacheTests : XCTestCase @end #define kImageWidth 30 #define kImageHeight 40 @implementation FlyImageIconCacheTests - (void)setUp { [super setUp]; // Put setup code here. This method is called before the invocation of each test method in the class. } - (void)tearDown { // Put teardown code here. This method is called after the invocation of each test method in the class. [super tearDown]; } - (void)drawALineInContext:(CGContextRef)context rect:(CGRect)rect { UIGraphicsPushContext(context); CGContextSetStrokeColorWithColor(context, [UIColor redColor].CGColor); CGContextSetLineWidth(context, 10.0); CGContextMoveToPoint(context, 0.0, 0.0); CGContextAddLineToPoint(context, rect.size.width, rect.size.height); UIGraphicsPopContext(); } - (void)test10AddImage { XCTestExpectation* expectation = [self expectationWithDescription:@"test10AddImage"]; [[FlyImageIconCache sharedInstance] addImageWithKey:@"10" size:CGSizeMake(kImageWidth, kImageHeight) drawingBlock:^(CGContextRef context, CGRect contextBounds) { [self drawALineInContext:context rect:contextBounds]; } completed:^(NSString* key, UIImage* image) { XCTAssert( image.size.width == kImageWidth ); XCTAssert( image.size.height == kImageHeight ); [expectation fulfill]; }]; [self waitForExpectationsWithTimeout:10 handler:^(NSError* error) { XCTAssert(YES, @"Pass"); }]; } - (void)test11AddMultipleTimes { XCTestExpectation* expectation = [self expectationWithDescription:@"test11AddMultipleTimes"]; __block int sum = 0; for (int i = 0; i < 100; i++) { [[FlyImageIconCache sharedInstance] addImageWithKey:@"11" size:CGSizeMake(kImageWidth, kImageHeight) drawingBlock:^(CGContextRef context, CGRect contextBounds) { [self drawALineInContext:context rect:contextBounds]; } completed:^(NSString* key, UIImage* image) { XCTAssert( image.size.width == kImageWidth ); XCTAssert( image.size.height == kImageHeight ); sum++; if ( sum == 100 ){ [expectation fulfill]; } }]; } [self waitForExpectationsWithTimeout:30 handler:^(NSError* error) { XCTAssert(YES, @"Pass"); }]; } - (void)test12AddSameImage { XCTestExpectation* expectation = [self expectationWithDescription:@"test12AddSameImage"]; [[FlyImageIconCache sharedInstance] addImageWithKey:@"10" size:CGSizeMake(kImageWidth, kImageHeight) drawingBlock:^(CGContextRef context, CGRect contextBounds) { [self drawALineInContext:context rect:contextBounds]; } completed:^(NSString* key, UIImage* image) { [[FlyImageIconCache sharedInstance] addImageWithKey:@"10" size:CGSizeMake(kImageWidth, kImageHeight) drawingBlock:^(CGContextRef context, CGRect contextBounds) { [self drawALineInContext:context rect:contextBounds]; } completed:^(NSString* key, UIImage* image) { XCTAssert( image.size.width == kImageWidth ); XCTAssert( image.size.height == kImageHeight ); [expectation fulfill]; }]; }]; [self waitForExpectationsWithTimeout:10 handler:^(NSError* error) { XCTAssert(YES, @"Pass"); }]; } - (void)test13AddMultipleKeys { XCTestExpectation* expectation = [self expectationWithDescription:@"test13AddMultipleKeys"]; __block int sum = 0; for (int i = 1; i <= 100; i++) { [[FlyImageIconCache sharedInstance] addImageWithKey:[NSString stringWithFormat:@"%d", i] size:CGSizeMake(kImageWidth, kImageHeight) drawingBlock:^(CGContextRef context, CGRect contextBounds) { [self drawALineInContext:context rect:contextBounds]; } completed:^(NSString* key, UIImage* image) { XCTAssert( image.size.width == kImageWidth ); XCTAssert( image.size.height == kImageHeight ); sum++; if ( sum == 100 ){ [expectation fulfill]; } }]; } [self waitForExpectationsWithTimeout:30 handler:^(NSError* error) { XCTAssert(YES, @"Pass"); }]; } - (void)test20ReplaceImage { XCTestExpectation* expectation = [self expectationWithDescription:@"test20ReplaceImage"]; [[FlyImageIconCache sharedInstance] addImageWithKey:@"20" size:CGSizeMake(kImageWidth, kImageHeight) drawingBlock:^(CGContextRef context, CGRect contextBounds) { [self drawALineInContext:context rect:contextBounds]; } completed:^(NSString* key, UIImage* image) { [[FlyImageIconCache sharedInstance] replaceImageWithKey:@"20" drawingBlock:^(CGContextRef context, CGRect contextBounds) { [self drawALineInContext:context rect:contextBounds]; } completed:^(NSString* key, UIImage* image) { XCTAssert( image.size.width == kImageWidth ); XCTAssert( image.size.height == kImageHeight ); [expectation fulfill]; }]; }]; [self waitForExpectationsWithTimeout:10 handler:^(NSError* error) { XCTAssert(YES, @"Pass"); }]; } - (void)test30AsyncGetImage { XCTestExpectation* expectation = [self expectationWithDescription:@"test30AsyncGetImage"]; [[FlyImageIconCache sharedInstance] addImageWithKey:@"30" size:CGSizeMake(kImageWidth, kImageHeight) drawingBlock:^(CGContextRef context, CGRect contextBounds) { [self drawALineInContext:context rect:contextBounds]; } completed:^(NSString* key, UIImage* image) { [[FlyImageIconCache sharedInstance] asyncGetImageWithKey:@"30" completed:^(NSString* key, UIImage* image) { XCTAssert( image.size.width == kImageWidth ); XCTAssert( image.size.height == kImageHeight ); [expectation fulfill]; }]; }]; [self waitForExpectationsWithTimeout:10 handler:^(NSError* error) { XCTAssert(YES, @"Pass"); }]; } - (void)test31AsyncGetImageMultipleTimes { XCTestExpectation* expectation = [self expectationWithDescription:@"test31AsyncGetImageMultipleTimes"]; [[FlyImageIconCache sharedInstance] addImageWithKey:@"31" size:CGSizeMake(kImageWidth, kImageHeight) drawingBlock:^(CGContextRef context, CGRect contextBounds) { [self drawALineInContext:context rect:contextBounds]; } completed:^(NSString* key, UIImage* image) { __block int sum = 0; for (int i = 0; i < 100; i++) { [[FlyImageIconCache sharedInstance] asyncGetImageWithKey:@"31" completed:^(NSString* key, UIImage* image) { XCTAssert( image.size.width == kImageWidth ); XCTAssert( image.size.height == kImageHeight ); sum++; if ( sum == 100 ){ [expectation fulfill]; } }]; } }]; [self waitForExpectationsWithTimeout:30 handler:^(NSError* error) { XCTAssert(YES, @"Pass"); }]; } - (void)test40DifferentSize { XCTestExpectation* expectation = [self expectationWithDescription:@"test40DifferentSize"]; [[FlyImageIconCache sharedInstance] addImageWithKey:@"400" size:CGSizeMake(27, 57) drawingBlock:^(CGContextRef context, CGRect contextBounds) { [self drawALineInContext:context rect:contextBounds]; } completed:^(NSString* key, UIImage* image) { XCTAssert( image.size.width == 27 ); XCTAssert( image.size.height == 57 ); [[FlyImageIconCache sharedInstance] addImageWithKey:@"401" size:CGSizeMake(88, 99) drawingBlock:^(CGContextRef context, CGRect contextBounds) { [self drawALineInContext:context rect:contextBounds]; } completed:^(NSString* key, UIImage* image) { XCTAssert( image.size.width == 88 ); XCTAssert( image.size.height == 99 ); [expectation fulfill]; }]; }]; [self waitForExpectationsWithTimeout:10 handler:^(NSError* error) { XCTAssert(YES, @"Pass"); }]; } - (void)test70RemoveImage { XCTestExpectation* expectation = [self expectationWithDescription:@"test70RemoveImage"]; [[FlyImageIconCache sharedInstance] addImageWithKey:@"70" size:CGSizeMake(kImageWidth, kImageHeight) drawingBlock:^(CGContextRef context, CGRect contextBounds) { [self drawALineInContext:context rect:contextBounds]; } completed:^(NSString* key, UIImage* image) { [[FlyImageIconCache sharedInstance] removeImageWithKey:@"70"]; XCTAssert(![[FlyImageIconCache sharedInstance] isImageExistWithKey:@"70"]); [expectation fulfill]; }]; [self waitForExpectationsWithTimeout:10 handler:^(NSError* error) { XCTAssert(YES, @"Pass"); }]; } - (void)test80ChangeImageKey { XCTestExpectation* expectation = [self expectationWithDescription:@"test80ChangeImageKey"]; [[FlyImageIconCache sharedInstance] addImageWithKey:@"70" size:CGSizeMake(kImageWidth, kImageHeight) drawingBlock:^(CGContextRef context, CGRect contextBounds) { [self drawALineInContext:context rect:contextBounds]; } completed:^(NSString* key, UIImage* image) { [[FlyImageIconCache sharedInstance] changeImageKey:@"80" newKey:@"newKey"]; XCTAssert(![[FlyImageIconCache sharedInstance] isImageExistWithKey:@"80"]); XCTAssert([[FlyImageIconCache sharedInstance] isImageExistWithKey:@"newKey"]); [[FlyImageIconCache sharedInstance] asyncGetImageWithKey:@"newKey" completed:^(NSString* key, UIImage* image) { XCTAssert( image.size.width == kImageWidth ); XCTAssert( image.size.height == kImageHeight ); [expectation fulfill]; }]; }]; [self waitForExpectationsWithTimeout:10 handler:^(NSError* error) { XCTAssert(YES, @"Pass"); }]; } - (void)test90Purge { [[FlyImageIconCache sharedInstance] purge]; XCTAssert(![[FlyImageIconCache sharedInstance] isImageExistWithKey:@"10"]); } @end ================================================ FILE: FlyImageTests/Info.plist ================================================ CFBundleDevelopmentRegion en CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIdentifier $(PRODUCT_BUNDLE_IDENTIFIER) CFBundleInfoDictionaryVersion 6.0 CFBundleName $(PRODUCT_NAME) CFBundlePackageType BNDL CFBundleShortVersionString 1.0 CFBundleSignature ???? CFBundleVersion 1 ================================================ FILE: LICENSE ================================================ Copyright (c) 2016 Olivier Poitrey rs@dailymotion.com 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: Podfile ================================================ platform :ios, '8.0' # ignore all warnings from all pods inhibit_all_warnings! target "FlyImage" do pod 'AFNetworking', '~> 3.1' pod 'libwebp', '0.6' end ================================================ FILE: README.md ================================================ ![FlyImage Logo](Docs/logo.png) FlyImage ========= [![Build Status](https://travis-ci.org/northwind/FlyImage.svg?branch=master)](https://travis-ci.org/northwind/FlyImage) [![Pod Version](http://img.shields.io/cocoapods/v/FlyImage.svg?style=flat)](http://cocoadocs.org/docsets/FlyImage/) [![Pod Platform](http://img.shields.io/cocoapods/p/FlyImage.svg?style=flat)](http://cocoadocs.org/docsets/FlyImage/) [![Pod License](http://img.shields.io/cocoapods/l/FlyImage.svg?style=flat)](https://www.apache.org/licenses/LICENSE-2.0.html) [![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/northwind/FlyImage) FlyImage takes the advantages of [SDWebImage](https://github.com/rs/SDWebImage), [FastImageCache](https://github.com/path/FastImageCache) and [AFNetworking](https://github.com/AFNetworking/AFNetworking), is a simple and high performance image library. Features: - High Performance, reduce memory operations while rendering, avoid `Memory warning` caused by image - Store and retrieve different size of small images in one memory file, smooth scrolling - Simple, support `UIImageView`, `CALayer` category - An asynchronous image downloader - Support `WebP` format - Support `mmap` to improve I/O performace ## Installation with CocoaPods Set the Podfile like this: ``` platform :ios, '8.0' pod 'FlyImage', '~>1.0' ``` If you are using Swift, be sure to add `use_frameworks!`: ``` platform :ios, '8.0' use_frameworks! pod 'FlyImage', '~>1.0' ``` If you want to support `WebP`, just change the Podfile: ``` platform :ios, '8.0' pod 'FlyImage/WebP', '~>1.0' ``` ## How To Use Using UIImageView and CALayer Category ```objective-c #import "FlyImage.h" ... UIImageView *imageView = [[UIImageView alloc] initWithFrame:frame]; [imageView setPlaceHolderImageName:@"default" thumbnailURL:[NSURL urlWithString:@"http://thumbnail"] originalURL:[NSURL urlWithString:@"http://original"]]; []self.view addSubview:imageView]; ... UIImageView *iconView = [[UIImageView alloc] initWithFrame:frame]; [iconView setIconURL:[NSURL urlWithString:@"http://original"]]; []self.view addSubview:iconView]; ... ``` Using FlyImageCache ```objective-c // retrieve a specific key, and get callback [[FlyImageCache sharedInstance] asyncGetImageWithKey:key completed:^(NSString *key, UIImage *image) { imageView.image = image; }]; // remove a image from the cache [[FlyImageCache sharedInstance] removeImageWithKey:key]; // delete all images [[FlyImageCache sharedInstance] purge]; ``` Using FlyImageIconCache ```objective-c // add a new icon with a specific key [[FlyImageIconCache sharedInstance] addImageWithKey:key size:drawSize drawingBlock:^(CGContextRef context, CGRect contextBounds) { UIImage *image = [UIImage imageWithName:@"imageName"]; UIGraphicsPushContext(context); [image drawInRect:contextBounds]; UIGraphicsPopContext(); } completed:nil]; // retrieve a specific key, and get callback [[FlyImageCache sharedInstance] asyncGetImageWithKey:key completed:^(NSString *key, UIImage *image) { imageView.image = image; }]; ``` More demos in folder [Examples](https://github.com/northwind/FlyImage/tree/master/Examples) ## Performance Mesure #### Memory when scrolling images > Demo Target: FlyImageView / Device: iPhone6 Plus Memory | FlyImage | SDWebImage | UIKit ------------ | ------------ | ------------- | ------------ All Heap Allocations | 2~7M | 2~4M | 2~5M All Anonymous VM | 17~30M | 310M | 17~30M #### FPS when more than 170 small images in the same screen > Demo Target: FlyImageIconView / Device: iPhone6 Plus FlyImage | SDWebImage | UIKit ------------ | ------------- | ------------ 58~60FPS | 6~7FPS | 6~7FPS ![FPS](Docs/iconcache.jpg) ## Architecture ![FlyImage Struct](Docs/classes.jpg) ## License FlyImage is made available under the [MIT license](http://opensource.org/licenses/MIT)