MWPhoto.m 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320
  1. //
  2. // MWPhoto.m
  3. // MWPhotoBrowser
  4. //
  5. // Created by Michael Waterfall on 17/10/2010.
  6. // Copyright 2010 d3i. All rights reserved.
  7. //
  8. #import <AssetsLibrary/AssetsLibrary.h>
  9. #import "MWPhoto.h"
  10. #import "MWPhotoBrowser.h"
  11. @interface MWPhoto () {
  12. BOOL _loadingInProgress;
  13. PHImageRequestID _assetRequestID;
  14. }
  15. @property (nonatomic, strong) UIImage *image;
  16. @property (nonatomic, strong) NSURL *photoURL;
  17. @property (nonatomic, strong) PHAsset *asset;
  18. @property (nonatomic) CGSize assetTargetSize;
  19. - (void)imageLoadingComplete;
  20. @end
  21. @implementation MWPhoto
  22. @synthesize underlyingImage = _underlyingImage; // synth property from protocol
  23. #pragma mark - Class Methods
  24. + (MWPhoto *)photoWithImage:(UIImage *)image {
  25. return [[MWPhoto alloc] initWithImage:image];
  26. }
  27. + (MWPhoto *)photoWithURL:(NSURL *)url {
  28. return [[MWPhoto alloc] initWithURL:url];
  29. }
  30. + (MWPhoto *)photoWithAsset:(PHAsset *)asset targetSize:(CGSize)targetSize {
  31. return [[MWPhoto alloc] initWithAsset:asset targetSize:targetSize];
  32. }
  33. + (MWPhoto *)videoWithURL:(NSURL *)url {
  34. return [[MWPhoto alloc] initWithVideoURL:url];
  35. }
  36. #pragma mark - Init
  37. - (id)init {
  38. if ((self = [super init])) {
  39. self.emptyImage = YES;
  40. }
  41. return self;
  42. }
  43. - (id)initWithImage:(UIImage *)image {
  44. if ((self = [super init])) {
  45. self.image = image;
  46. }
  47. return self;
  48. }
  49. - (id)initWithURL:(NSURL *)url {
  50. if ((self = [super init])) {
  51. self.photoURL = url;
  52. }
  53. return self;
  54. }
  55. - (id)initWithAsset:(PHAsset *)asset targetSize:(CGSize)targetSize {
  56. if ((self = [super init])) {
  57. self.asset = asset;
  58. self.assetTargetSize = targetSize;
  59. self.isVideo = asset.mediaType == PHAssetMediaTypeVideo;
  60. }
  61. return self;
  62. }
  63. - (id)initWithVideoURL:(NSURL *)url {
  64. if ((self = [super init])) {
  65. self.videoURL = url;
  66. self.isVideo = YES;
  67. self.emptyImage = YES;
  68. }
  69. return self;
  70. }
  71. #pragma mark - Video
  72. - (void)setVideoURL:(NSURL *)videoURL {
  73. _videoURL = videoURL;
  74. self.isVideo = YES;
  75. }
  76. - (void)getVideoURL:(void (^)(NSURL *url))completion {
  77. if (_videoURL) {
  78. completion(_videoURL);
  79. } else if (_asset && _asset.mediaType == PHAssetMediaTypeVideo) {
  80. PHVideoRequestOptions *options = [PHVideoRequestOptions new];
  81. options.networkAccessAllowed = YES;
  82. [[PHImageManager defaultManager] requestAVAssetForVideo:_asset options:options resultHandler:^(AVAsset *asset, AVAudioMix *audioMix, NSDictionary *info) {
  83. if ([asset isKindOfClass:[AVURLAsset class]]) {
  84. completion(((AVURLAsset *)asset).URL);
  85. } else {
  86. completion(nil);
  87. }
  88. }];
  89. }
  90. return completion(nil);
  91. }
  92. #pragma mark - MWPhoto Protocol Methods
  93. - (UIImage *)underlyingImage {
  94. return _underlyingImage;
  95. }
  96. - (void)loadUnderlyingImageAndNotify {
  97. NSAssert([[NSThread currentThread] isMainThread], @"This method must be called on the main thread.");
  98. if (_loadingInProgress) return;
  99. _loadingInProgress = YES;
  100. @try {
  101. if (self.underlyingImage) {
  102. [self imageLoadingComplete];
  103. } else {
  104. [self performLoadUnderlyingImageAndNotify];
  105. }
  106. }
  107. @catch (NSException *exception) {
  108. self.underlyingImage = nil;
  109. _loadingInProgress = NO;
  110. [self imageLoadingComplete];
  111. }
  112. @finally {
  113. }
  114. }
  115. // Set the underlyingImage
  116. - (void)performLoadUnderlyingImageAndNotify {
  117. // Get underlying image
  118. if (_image) {
  119. // We have UIImage!
  120. self.underlyingImage = _image;
  121. [self imageLoadingComplete];
  122. } else if (_photoURL) {
  123. // Check what type of url it is
  124. if ([[[_photoURL scheme] lowercaseString] isEqualToString:@"assets-library"]) {
  125. // Load from assets library
  126. [self _performLoadUnderlyingImageAndNotifyWithAssetsLibraryURL: _photoURL];
  127. } else if ([_photoURL isFileReferenceURL]) {
  128. // Load from local file async
  129. [self _performLoadUnderlyingImageAndNotifyWithLocalFileURL: _photoURL];
  130. } else {
  131. // Load async from web (using SDWebImage)
  132. [self _performLoadUnderlyingImageAndNotifyWithWebURL: _photoURL];
  133. }
  134. } else if (_asset) {
  135. // Load from photos asset
  136. [self _performLoadUnderlyingImageAndNotifyWithAsset: _asset targetSize:_assetTargetSize];
  137. } else {
  138. // Image is empty
  139. [self imageLoadingComplete];
  140. }
  141. }
  142. // Load from local file
  143. - (void)_performLoadUnderlyingImageAndNotifyWithWebURL:(NSURL *)url {
  144. /*
  145. @try {
  146. SDWebImageManager *manager = [SDWebImageManager sharedManager];
  147. _webImageOperation = [manager downloadImageWithURL:url
  148. options:0
  149. progress:^(NSInteger receivedSize, NSInteger expectedSize) {
  150. if (expectedSize > 0) {
  151. float progress = receivedSize / (float)expectedSize;
  152. NSDictionary* dict = [NSDictionary dictionaryWithObjectsAndKeys:
  153. [NSNumber numberWithFloat:progress], @"progress",
  154. self, @"photo", nil];
  155. [[NSNotificationCenter defaultCenter] postNotificationName:MWPHOTO_PROGRESS_NOTIFICATION object:dict];
  156. }
  157. }
  158. completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL) {
  159. if (error) {
  160. MWLog(@"SDWebImage failed to download image: %@", error);
  161. }
  162. _webImageOperation = nil;
  163. self.underlyingImage = image;
  164. dispatch_async(dispatch_get_main_queue(), ^{
  165. [self imageLoadingComplete];
  166. });
  167. }];
  168. } @catch (NSException *e) {
  169. MWLog(@"Photo from web: %@", e);
  170. _webImageOperation = nil;
  171. [self imageLoadingComplete];
  172. }
  173. */
  174. }
  175. // Load from local file
  176. - (void)_performLoadUnderlyingImageAndNotifyWithLocalFileURL:(NSURL *)url {
  177. dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
  178. @autoreleasepool {
  179. @try {
  180. self.underlyingImage = [UIImage imageWithContentsOfFile:url.path];
  181. if (!_underlyingImage) {
  182. MWLog(@"Error loading photo from path: %@", url.path);
  183. }
  184. } @finally {
  185. [self performSelectorOnMainThread:@selector(imageLoadingComplete) withObject:nil waitUntilDone:NO];
  186. }
  187. }
  188. });
  189. }
  190. // Load from asset library async
  191. - (void)_performLoadUnderlyingImageAndNotifyWithAssetsLibraryURL:(NSURL *)url {
  192. dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
  193. @autoreleasepool {
  194. @try {
  195. ALAssetsLibrary *assetslibrary = [[ALAssetsLibrary alloc] init];
  196. [assetslibrary assetForURL:url
  197. resultBlock:^(ALAsset *asset){
  198. ALAssetRepresentation *rep = [asset defaultRepresentation];
  199. CGImageRef iref = [rep fullScreenImage];
  200. if (iref) {
  201. self.underlyingImage = [UIImage imageWithCGImage:iref];
  202. }
  203. [self performSelectorOnMainThread:@selector(imageLoadingComplete) withObject:nil waitUntilDone:NO];
  204. }
  205. failureBlock:^(NSError *error) {
  206. self.underlyingImage = nil;
  207. MWLog(@"Photo from asset library error: %@",error);
  208. [self performSelectorOnMainThread:@selector(imageLoadingComplete) withObject:nil waitUntilDone:NO];
  209. }];
  210. } @catch (NSException *e) {
  211. MWLog(@"Photo from asset library error: %@", e);
  212. [self performSelectorOnMainThread:@selector(imageLoadingComplete) withObject:nil waitUntilDone:NO];
  213. }
  214. }
  215. });
  216. }
  217. // Load from photos library
  218. - (void)_performLoadUnderlyingImageAndNotifyWithAsset:(PHAsset *)asset targetSize:(CGSize)targetSize {
  219. PHImageManager *imageManager = [PHImageManager defaultManager];
  220. PHImageRequestOptions *options = [PHImageRequestOptions new];
  221. options.networkAccessAllowed = YES;
  222. options.resizeMode = PHImageRequestOptionsResizeModeFast;
  223. options.deliveryMode = PHImageRequestOptionsDeliveryModeHighQualityFormat;
  224. options.synchronous = false;
  225. options.progressHandler = ^(double progress, NSError *error, BOOL *stop, NSDictionary *info) {
  226. NSDictionary* dict = [NSDictionary dictionaryWithObjectsAndKeys:
  227. [NSNumber numberWithDouble: progress], @"progress",
  228. self, @"photo", nil];
  229. [[NSNotificationCenter defaultCenter] postNotificationName:MWPHOTO_PROGRESS_NOTIFICATION object:dict];
  230. };
  231. _assetRequestID = [imageManager requestImageForAsset:asset targetSize:targetSize contentMode:PHImageContentModeAspectFit options:options resultHandler:^(UIImage *result, NSDictionary *info) {
  232. dispatch_async(dispatch_get_main_queue(), ^{
  233. self.underlyingImage = result;
  234. [self imageLoadingComplete];
  235. });
  236. }];
  237. }
  238. // Release if we can get it again from path or url
  239. - (void)unloadUnderlyingImage {
  240. _loadingInProgress = NO;
  241. self.underlyingImage = nil;
  242. }
  243. - (void)imageLoadingComplete {
  244. NSAssert([[NSThread currentThread] isMainThread], @"This method must be called on the main thread.");
  245. // Complete so notify
  246. _loadingInProgress = NO;
  247. // Notify on next run loop
  248. [self performSelector:@selector(postCompleteNotification) withObject:nil afterDelay:0];
  249. }
  250. - (void)postCompleteNotification {
  251. [[NSNotificationCenter defaultCenter] postNotificationName:MWPHOTO_LOADING_DID_END_NOTIFICATION
  252. object:self];
  253. }
  254. - (void)cancelAnyLoading {
  255. /*
  256. if (_webImageOperation != nil) {
  257. [_webImageOperation cancel];
  258. _loadingInProgress = NO;
  259. } else if (_assetRequestID != PHInvalidImageRequestID) {
  260. [[PHImageManager defaultManager] cancelImageRequest:_assetRequestID];
  261. _assetRequestID = PHInvalidImageRequestID;
  262. }
  263. */
  264. }
  265. @end