【问题标题】:Get last image from Photos.app?从 Photos.app 获取最后一张图片?
【发布时间】:2012-02-10 15:43:57
【问题描述】:

我见过其他应用程序这样做,您可以从“照片”应用程序导入最后一张照片以供快速使用,但据我所知,我只知道如何获取 A 图像而不是最后一张(最近的一张)。谁能告诉我如何获取最后一张图片?

【问题讨论】:

    标签: ios image camera photos


    【解决方案1】:

    好吧,这里有一个解决方案,如何使用 Swift 3 人从图库中加载最后一张图片:

    func loadLastImageThumb(completion: @escaping (UIImage) -> ()) {
        let imgManager = PHImageManager.default()
        let fetchOptions = PHFetchOptions()
        fetchOptions.fetchLimit = 1
        fetchOptions.sortDescriptors = [NSSortDescriptor(key:"creationDate", ascending: true)]
    
        let fetchResult = PHAsset.fetchAssets(with: PHAssetMediaType.image, options: fetchOptions)
    
        if let last = fetchResult.lastObject {
            let scale = UIScreen.main.scale
            let size = CGSize(width: 100 * scale, height: 100 * scale)
            let options = PHImageRequestOptions()
    
    
            imgManager.requestImage(for: last, targetSize: size, contentMode: PHImageContentMode.aspectFill, options: options, resultHandler: { (image, _) in
                if let image = image {
                    completion(image)
                }
            })
        }
    
    }
    

    如果您需要更快的速度,您也可以使用PHImageRequestOptions 并设置这些:

    options.deliveryMode = .fastFormat
    options.resizeMode = .fast
    

    这就是你在 viewController 中获取它的方式(你应该用你的类替换 GalleryManager.manager):

    GalleryManager.manager.loadLastImageThumb { [weak self] (image) in
          DispatchQueue.main.async {
               self?.galleryButton.setImage(image, for: .normal)
          }
    }
    

    【讨论】:

      【解决方案2】:

      这是 Swift 中的一个版本,它请求数据并将其转换为 UIImage,因为提供的版本每次都返回一个空的 UIImage

          let fetchOptions = PHFetchOptions()
          fetchOptions.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: true)]
      
          let fetchResult = PHAsset.fetchAssetsWithMediaType(PHAssetMediaType.Image, options: fetchOptions)
      
          if let lastAsset: PHAsset = fetchResult.lastObject as? PHAsset {
              let manager = PHImageManager.defaultManager()
              let imageRequestOptions = PHImageRequestOptions()
      
              manager.requestImageDataForAsset(lastAsset, options: imageRequestOptions) {
                  (let imageData: NSData?, let dataUTI: String?,
                   let orientation: UIImageOrientation,
                   let info: [NSObject : AnyObject]?) -> Void in
      
                   if let imageDataUnwrapped = imageData, lastImageRetrieved = UIImage(data: imageDataUnwrapped) {
                      // do stuff with image
      
                   }
              }
          }
      

      【讨论】:

        【解决方案3】:

        以下代码适用于 iOS7 和 iOS8。它还检查过滤器中是否有图像。在执行代码之前,您应该检查相册权限:

        // get the latest image from the album
        -(void)getLatestPhoto
        {
            NSLog(@"MMM TGCameraViewController - getLatestPhoto");
            ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init];
        
            // Enumerate just the photos and videos group by using ALAssetsGroupSavedPhotos.
            [library enumerateGroupsWithTypes:ALAssetsGroupSavedPhotos usingBlock:^(ALAssetsGroup *group, BOOL *stop) {
        
                // Within the group enumeration block, filter to enumerate just photos.
                [group setAssetsFilter:[ALAssetsFilter allPhotos]];
        
                // For this example, we're only interested in the last item [group numberOfAssets]-1 = last.
                if ([group numberOfAssets] > 0) {
        
                    [group enumerateAssetsAtIndexes:[NSIndexSet indexSetWithIndex:[group numberOfAssets]-1]
                                            options:0
                                         usingBlock:^(ALAsset *alAsset, NSUInteger index, BOOL *innerStop) {
        
                                             // The end of the enumeration is signaled by asset == nil.
                                             if (alAsset) {
                                                 ALAssetRepresentation *representation = [alAsset defaultRepresentation];
                                                 // Do something interesting with the AV asset.
                                                 UIImage *img = [UIImage imageWithCGImage:[representation fullScreenImage]];
        
                                                 // use the photo here ...
        
        
                                                 // we only need the first (most recent) photo -- stop the enumeration
                                                 *innerStop = YES;
                                             }
                                         }];
                }
            }
            failureBlock: ^(NSError *error) {
               // Typically you should handle an error more gracefully than this.
               NSLog(@"No groups");
            }];
        }  
        

        (此代码是here的修改版。)

        【讨论】:

          【解决方案4】:

          此代码 sn-p 将从相机胶卷中获取最新图像(iOS 7 及以下)

          ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init];
          
          // Enumerate just the photos and videos group by using ALAssetsGroupSavedPhotos.
          [library enumerateGroupsWithTypes:ALAssetsGroupSavedPhotos usingBlock:^(ALAssetsGroup *group, BOOL *stop) {
          
              // Within the group enumeration block, filter to enumerate just photos.
              [group setAssetsFilter:[ALAssetsFilter allPhotos]];
          
              // Chooses the photo at the last index
              [group enumerateAssetsWithOptions:NSEnumerationReverse usingBlock:^(ALAsset *alAsset, NSUInteger index, BOOL *innerStop) {
          
                  // The end of the enumeration is signaled by asset == nil.
                  if (alAsset) {
                      ALAssetRepresentation *representation = [alAsset defaultRepresentation];
                      UIImage *latestPhoto = [UIImage imageWithCGImage:[representation fullScreenImage]];
          
                      // Stop the enumerations
                      *stop = YES; *innerStop = YES;
          
                      // Do something interesting with the AV asset.
                      [self sendTweet:latestPhoto];
                  }
              }];
          } failureBlock: ^(NSError *error) {
              // Typically you should handle an error more gracefully than this.
              NSLog(@"No groups");
          }];
          

          iOS 8 及更高版本:

          PHFetchOptions *fetchOptions = [[PHFetchOptions alloc] init];
          fetchOptions.sortDescriptors = @[[NSSortDescriptor sortDescriptorWithKey:@"creationDate" ascending:YES]];
          PHFetchResult *fetchResult = [PHAsset fetchAssetsWithMediaType:PHAssetMediaTypeImage options:fetchOptions];
          PHAsset *lastAsset = [fetchResult lastObject];
          [[PHImageManager defaultManager] requestImageForAsset:lastAsset
                                                    targetSize:self.photoLibraryButton.bounds.size
                                                   contentMode:PHImageContentModeAspectFill
                                                       options:PHImageRequestOptionsVersionCurrent
                                                 resultHandler:^(UIImage *result, NSDictionary *info) {
          
                                                     dispatch_async(dispatch_get_main_queue(), ^{
          
                                                         [[self photoLibraryButton] setImage:result forState:UIControlStateNormal];
          
                                                     });
                                                 }];
          

          【讨论】:

          • 注意:如果相机胶卷中没有照片,此代码将崩溃。在现实世界中不常见,但仍然是您想要检查的东西。我在第一个块级别中添加了if ([group numberOfAssets] < 1) return; 来防御这种情况。
          • @iBradApps enumerateAssetsAtIndexes:options:usingBlock: 已替换为 enumerateAssetsWithOptions:usingBlock: 和选项 NSEnumerationReverse。添加了使用 stopinnerStop 布尔变量,因此一旦找到资产,我们仍将停止枚举。您可以在stackoverflow.com/posts/8872425/revisions 看到差异
          • 哦 - 嗯 - iOS 8 代码是否经过测试?我不认为 PHFetchResult.lastObject 返回 UIImage?
          • PHAsset 对象没有图像数据,它只有图像的“元数据”!
          • 那个 fetchResult.lastObject 为你返回一个图像是很奇怪的。文档建议它应该返回一个 PHAsset,然后应该使用它来抓取图像并支持各种选项(大小、版本等)。我不知道这如何为您返回 UIImage 并为我返回 PHAsset。我添加了一个答案,其中包括适用于相关方的 PHAsset 方法 (@an0)
          【解决方案5】:

          iBrad 的示例包括一个显然有效的 iOS8 sn-p,但我发现自己对他描述的返回类型感到困惑。这是一个抓取最后一张图像的 sn-p,包括版本和大小要求的选项。

          值得注意的是能够请求特定版本(原始版本、当前版本)和大小。在我的情况下,由于我希望将返回的图像应用到按钮,我要求它调整大小并缩放以适合我应用它的按钮:

          PHFetchOptions *fetchOptions = [[PHFetchOptions alloc] init];
          fetchOptions.sortDescriptors = @[[NSSortDescriptor sortDescriptorWithKey:@"creationDate" ascending:YES]];
          PHFetchResult *fetchResult = [PHAsset fetchAssetsWithMediaType:PHAssetMediaTypeImage options:fetchOptions];
          PHAsset *lastAsset = [fetchResult lastObject];
          [[PHImageManager defaultManager] requestImageForAsset:lastAsset
                                                    targetSize:self.photoLibraryButton.bounds.size
                                                   contentMode:PHImageContentModeAspectFill
                                                       options:PHImageRequestOptionsVersionCurrent
                                                 resultHandler:^(UIImage *result, NSDictionary *info) {
          
                                                     dispatch_async(dispatch_get_main_queue(), ^{
          
                                                         [[self photoLibraryButton] setImage:result forState:UIControlStateNormal];
          
                                                     });
                                                 }];
          

          【讨论】:

          • PHImageRequestOptions 是一个对象。您实际上可以传递一个枚举值“PHImageRequestOptionsVersionCurrent”吗?我想你想要的是: PHImageRequestOptions* options = [PHImageRequestOptions new]; options.version = PHImageRequestOptionsVersionCurrent;
          【解决方案6】:

          基于 iBrad 的回答,这里有一个快速而肮脏的 Swift 版本,适用于 iOS 8.1:

          let imgManager = PHImageManager.defaultManager()
          var fetchOptions = PHFetchOptions()
          fetchOptions.sortDescriptors = [NSSortDescriptor(key:"creationDate", ascending: true)]
          if let fetchResult = PHAsset.fetchAssetsWithMediaType(PHAssetMediaType.Image, options: fetchOptions) {
              imgManager.requestImageForAsset(fetchResult.lastObject as PHAsset, targetSize: self.destinationImageView.frame.size, contentMode: PHImageContentMode.AspectFill, options: nil, resultHandler: { (image, _) in
                  self.destinationImageView.image = image
              })
          }
          

          注意:这需要 iOS 8.0+。请务必链接照片框架并在您的文件中添加“导入照片”。

          【讨论】:

            【解决方案7】:

            这是 iBrad 和 Javier 的答案的组合(效果很好),但我得到的是缩略图资产而不是全分辨率图像。其他一些人可能会觉得这很方便。

            - (void)setCameraRollImage {
                ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init];
                [library enumerateGroupsWithTypes:ALAssetsGroupSavedPhotos usingBlock:^(ALAssetsGroup *group, BOOL *stop) {
                    [group setAssetsFilter:[ALAssetsFilter allPhotos]];
                    if ([group numberOfAssets] > 0) {
                        // Chooses the photo at the last index
                        [group enumerateAssetsAtIndexes:[NSIndexSet indexSetWithIndex:([group numberOfAssets] - 1)] options:0 usingBlock:^(ALAsset *alAsset, NSUInteger index, BOOL *innerStop) {
                            // The end of the enumeration is signaled by asset == nil.
                            if (alAsset) {
                                UIImage *latestPhoto = [UIImage imageWithCGImage:[alAsset thumbnail]];
                                [self.cameraRollButton setImage:latestPhoto forState:UIControlStateNormal];
                            }
                        }];
                    }
                } failureBlock: ^(NSError *error) {
                }];
            }
            

            【讨论】:

              【解决方案8】:

              这是一种非常酷的方法,但其中一个问题是您必须能够在运行时实例化 PHPhotoLibrary 和其他 PHPhoto 类,否则在 iOS 7.XX 上会出现链接错误。只是想指出这一点,因为我现在遇到了这些问题。

              此外,我认为您必须弱化照片框架中的链接,才能使应用在安装了 iOS 8.XX 和 iOS 7.XX 的两台设备上运行(尽管我尚未对此进行测试。)

              我遇到的一个问题是如何在运行时实例化 PHPhotoLibrary。有没有人有代码sn-ps?

              实际上,对于我正在开发的应用程序,我最终必须编写运行时代码来实例化 PHPhotoLibrary 类并调用 PHOTOS 框架方法,以便该应用程序可以在 iOS 7.x.x 和 iOS 8.x.x 上运行。其他人可能会遇到同样的问题,所以我提供了下面的代码 ->

              // PHPhotoLibrary_class will only be non-nil on iOS 8.x.x
              Class PHPhotoLibrary_class = NSClassFromString(@"PHPhotoLibrary");
              
              if (PHPhotoLibrary_class) {
              
                 /**
                  *
                  iOS 8..x. . code that has to be called dynamically at runtime and will not link on iOS 7.x.x ...
              
                  [[PHPhotoLibrary sharedPhotoLibrary] performChanges:^{
                      [PHAssetCollectionChangeRequest creationRequestForAssetCollectionWithTitle:title];
                  } completionHandler:^(BOOL success, NSError *error) {
                      if (!success) {
                          NSLog(@"Error creating album: %@", error);
                      }
                  }];
                  */
              
                  // dynamic runtime code for code chunk listed above            
                  id sharedPhotoLibrary = [PHPhotoLibrary_class performSelector:NSSelectorFromString(@"sharedPhotoLibrary")];
              
                  SEL performChanges = NSSelectorFromString(@"performChanges:completionHandler:");
              
                  NSMethodSignature *methodSig = [sharedPhotoLibrary methodSignatureForSelector:performChanges];
              
                  NSInvocation* inv = [NSInvocation invocationWithMethodSignature:methodSig];
                  [inv setTarget:sharedPhotoLibrary];
                  [inv setSelector:performChanges];
              
                  void(^firstBlock)() = ^void() {
                      Class PHAssetCollectionChangeRequest_class = NSClassFromString(@"PHAssetCollectionChangeRequest");
                      SEL creationRequestForAssetCollectionWithTitle = NSSelectorFromString(@"creationRequestForAssetCollectionWithTitle:");
                      [PHAssetCollectionChangeRequest_class performSelector:creationRequestForAssetCollectionWithTitle withObject:albumName];
              
                  };
              
                  void (^secondBlock)(BOOL success, NSError *error) = ^void(BOOL success, NSError *error) {
                     if (success) {
                         [assetsLib enumerateGroupsWithTypes:ALAssetsGroupAlbum usingBlock:^(ALAssetsGroup *group, BOOL *stop) {
                             if (group) {
                                 NSString *name = [group valueForProperty:ALAssetsGroupPropertyName];
                                 if ([albumName isEqualToString:name]) {
                                     groupFound = true;
                                     handler(group, nil);
                                 }
                             }
                         } failureBlock:^(NSError *error) {
                             handler(nil, error);
                         }];
                     }
              
                     if (error) {
                         NSLog(@"Error creating album: %@", error);
                         handler(nil, error);
                     }
                 };
              
                 // Set the first and second blocks.
                 [inv setArgument:&firstBlock atIndex:2];
                 [inv setArgument:&secondBlock atIndex:3];
              
                 [inv invoke];
              
              }
              else {   
                 // code that always creates an album on iOS 7.x.x but fails
                 // in certain situations such as if album has been deleted
                 // previously on iOS 8...x. .              
                 [assetsLib addAssetsGroupAlbumWithName:albumName
                     resultBlock:^(ALAssetsGroup *group) {
                     handler(group, nil);
                 } failureBlock:^(NSError *error) {
                     NSLog( @"Failed to create album: %@", albumName);
                     handler(nil, error);
                 }];
              }
              

              【讨论】:

                【解决方案9】:

                accepted answer (how to get last image) 的 Xamarin.iOS 版本,包括来自其他答案的所有通知:

                  private void ChooseLastTakenPictureImplementation()
                    {
                        var library = new ALAssetsLibrary();
                        // Enumerate just the photos and videos group by using ALAssetsGroupSavedPhotos.
                        library.Enumerate(ALAssetsGroupType.SavedPhotos, (ALAssetsGroup assetsGroup, ref bool stop) =>
                            {
                                if (stop || assetsGroup == null)
                                {
                                    return;
                                }
                                //Xamarin does not support ref parameters in nested lamba expressions
                                var lambdaStop = false;
                                //Check that the group has more than one picture
                                if (assetsGroup.Count > 0)
                                {
                                    // Within the group enumeration block, filter to enumerate just photos.
                                    assetsGroup.SetAssetsFilter(ALAssetsFilter.AllPhotos);
                                    // Chooses the photo at the last index
                                    assetsGroup.Enumerate(NSEnumerationOptions.Reverse, (ALAsset result, int index, ref bool innerStop) =>
                                        {
                                            // The end of the enumeration is signaled by asset == nil.
                                            if (result != null)
                                            {
                                                var representation = result.DefaultRepresentation;
                                                var latestPhoto = new UIImage(representation.GetImage(), representation.Scale, (UIImageOrientation)representation.Orientation);
                                                // Stop the enumerations
                                                lambdaStop = innerStop = true;
                                                // Do something interesting with the AV asset.
                                                HandleImageAutoPick(latestPhoto);
                                            }
                                        });
                                    stop = lambdaStop;
                                    return;
                                }
                                else
                                {
                                    //Handle this special case where user has no pictures
                                }
                            }, error =>
                            {
                                // Typically you should handle an error more gracefully than this.
                                Debug.WriteLine(error.Description);
                            });
                    }
                

                【讨论】:

                  【解决方案10】:

                  感谢您的回答 iBrad 应用程序。

                  只是想指出当用户在他/她的照片卷上没有图像时的特殊情况的错误预防(我知道奇怪的情况):

                      // Within the group enumeration block, filter to enumerate just photos.
                      [group setAssetsFilter:[ALAssetsFilter allPhotos]];
                  
                      //Check that the group has more than one picture
                      if ([group numberOfAssets] > 0) {
                          // Chooses the photo at the last index
                          [group enumerateAssetsAtIndexes:[NSIndexSet indexSetWithIndex:([group numberOfAssets] - 1)] options:0 usingBlock:^(ALAsset *alAsset, NSUInteger index, BOOL *innerStop) {
                  
                              // The end of the enumeration is signaled by asset == nil.
                              if (alAsset) {
                                  ALAssetRepresentation *representation = [alAsset defaultRepresentation];
                                  UIImage *latestPhoto = [UIImage imageWithCGImage:[representation fullScreenImage]];
                  
                                  [self.libraryButton setImage:latestPhoto forState:UIControlStateNormal];
                              }
                          }];
                      }
                      else {
                        //Handle this special case
                      }
                  

                  【讨论】:

                    【解决方案11】:

                    参考 Liam 的回答。 fullScreenImage 将返回适合您设备屏幕尺寸的缩放图像。获取实际图像大小:

                      ALAssetRepresentation *representation = [alAsset defaultRepresentation];
                      ALAssetOrientation orientation = [representation orientation];
                      UIImage *latestPhoto = [UIImage imageWithCGImage:[representation fullResolutionImage] scale:[representation scale] orientation:(UIImageOrientation)orientation];                    
                    

                    fullResolutionImage 上引用 Apple 的 ALAssetRepresentation 类参考:

                    要从 CGImage 创建正确旋转的 UIImage 对象,请使用 imageWithCGImage:scale:orientation: 或 initWithCGImage:scale:orientation:, 传递方向的值 和规模。

                    【讨论】:

                    • 好点,虽然我只想要屏幕大小的图像。我相信这会对其他人有所帮助...谢谢!
                    【解决方案12】:

                    我发现了一个错字,我不好意思承认的时间超过了它应该弄清楚的时间。也许它会为别人节省一些时间。

                    此行在indexSetWithIndex 之后缺少一个冒号:

                    [group enumerateAssetsAtIndexes:[NSIndexSet indexSetWithIndex:[group numberOfAssets] - 1]options:0 usingBlock:^(ALAsset *alAsset, NSUInteger index, BOOL *innerStop) {
                    

                    【讨论】:

                      【解决方案13】:

                      来自 iBrad 的出色回答,对我来说几乎是完美的。例外情况是它以原始方向返回图像(例如倒置、-90° 等)。

                      为了解决这个问题,我只是将fullResolutionImage 更改为fullScreenImage

                      这里:

                      UIImage *latestPhoto = [UIImage imageWithCGImage:[representation fullScreenImage]];
                      

                      现在是一种享受。

                      【讨论】:

                      • 感谢您的建议,我对您的回答投了赞成票,并将其纳入我的回答中! :)
                      • Liam 的建议做得很好,很高兴为 Stack 的世界做出贡献!
                      • 如果您只是显示图像,但实际上并没有获得全分辨率图像,则此方法有效,只是适合在用户设备上显示的图像。如果您需要完整尺寸的图片,您可以使用UIImage *latestPhoto = [UIImage imageWithCGImage:[representation fullResolutionImage] scale:[representation scale] orientation:[representation orientation]];
                      • @NateCook 这样做会给我一个警告:“从枚举类型'ALAssetOrientation'(又名'enum ALAssetOrientation')到不同枚举类型'UIImageOrientation'(又名'enum UIImag ...')的隐式转换”。执行此处描述的操作可能会更好:biasedbit.com/alasset-image-orientation
                      • 没关系 - 这基本上是 jemeshu 下面的答案。
                      猜你喜欢
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      • 2013-07-27
                      • 1970-01-01
                      • 2013-02-14
                      • 1970-01-01
                      相关资源
                      最近更新 更多