iOS8 Photos Framework

1. Model

PHAsset 、PHAssetCollection、PHCollectionList 是Photos框架中的模型类,PHAsset类是数据模型,描述图片或者视频文件数据;PHAssetCollection描述了包括相册、moments、智能相册以及共享照片流等一系列图片或者视频文件的集合的元数据;PHCollectionList是一组资源集合,可能是一组Assets,也可能是一组collection。 它们关系如下图:

photos_model.png

1.1 PHAsset

PHAsset是iOS8平台的新接口,存储获取图片和视频文件的元数据,这些图片和视频文件可能在手机本地,也可能在iCloud。相当于以前的 ALAsset接口,但比起ALAsset,PhotoKit 提供了额外的关于用户资源的元数据,而这些数据在以前使用 ALAssetsLibrary 框架中是没有办法访问,或者很难访问到。

它有几个要点:
  • 需要用PHAsset的一些fetchAssets...系列类方法获取到,你想要拿到的照片的PHAsset对象。
  • PHAsset 对象只包含元数据,image或者video数据需要用PHImageManager加载。
  • PHAsset对象的元数据是不可变的,修改元数据需要借助 PHAssetChangeRequest
它有几个重要属性:
  • mediaType :资源类型,图片或者音频或视频

    Paste_Image.png

  • mediaSubtypes:图片又包含全景图(Panorama)、HDR图片、屏幕截图、livePhoto .live photo 加3Dtouch效果太赞! [视频链接].我们可以使用照片资源的 mediaSubtypes 属性验证资源库中的图像在捕捉时是否开启了 HDR,拍摄时是否使用了相机应用的全景模式.(http://www.macworld.com/article/2988585/apple-phone/how-to-get-started-with-3d-touch-live-photos-and-4k-video-on-the-iphone-6s.html)

    Paste_Image.png

  • Creation date
  • Location
  • Favorite 布尔值,用户是否标记资源为"收藏",我们平时浏览照片或视频,在下方点就表示收藏这张图。
  • hidden 要验证一个资源是否被用户标记为收被隐藏,只要检查 PHAsset 实例的 hidden 属性即可。
  • sourceType : 资源可以来源自用户相册、iCloud、iTunes同步

    Paste_Image.png

  • representsBurstburstSelectionTypes: 对于一个资源,如果其 PHAsset 的 representsBurst 属性为 true,则表示这个资源是一系列连拍照片中的代表照片 (多张照片是在用户按住快门时拍摄的)。它还有一个属性是 burstIdentifier,如果想要获取连拍照片中的剩余的其他照片,可以通过将这个值传入 fetchAssetsWithBurstIdentifier(...) 方法来获取。用户可以在连拍的照片中做标记;此外,系统也会自动用各种试探来标记用户可能会选择的潜在代表照片。这个元数据是可以通过 PHAsset 的 burstSelectionTypes 属性来访问。这个属性是用三个常量组成的位掩码:.UserPick 表示用户手动标记的资源,.AutoPick 表示用户可能标记的潜在资源,.None 表示没有标记的资源。

Paste_Image.png

我模拟器相册中某张图片:

<PHAsset: 0x7f8293659e20> B84E8479-475C-4727-A4A4-B77AA9980897/L0/001 mediaType=1/0, sourceType=1, (4288x2848), creationDate=2009-10-09 21:09:20 +0000, location=0, hidden=0, favorite=0

localIdentifier

Photos 框架中的根类PHObject只有一个公开接口localIdentifier,是对象唯一唯一标志符.PHObject实现了-isEqual 和-hash方法.可以直接使用localIdentifier属性对PHObject及其子类对象进行对比是否同一个对象。

在我的模拟器中“最近删除”这个相册的localIdentifier为72053882-BF20-4D4A-B1A5-03D1DDAE1707/L0/040

1.2 PHAssetCollection

PHAssetCollection是一组有序的资源集合,包括相册、moments、智能相册以及共享照片流.
它的重要属性 ;

  • assetCollectionType 资源集合类型,比如相册或者“时刻”相册

    typedef NS_ENUM(NSInteger, PHAssetCollectionType) {
      PHAssetCollectionTypeAlbum      = 1,
      PHAssetCollectionTypeSmartAlbum = 2,
      PHAssetCollectionTypeMoment     = 3,
    } NS_ENUM_AVAILABLE_IOS(8_0);
  • assetCollectionSubtype 子类型
enum PHAssetCollectionType : Int {
    case Album //从 iTunes 同步来的相册,以及用户在 Photos 中自己建立的相册
    case SmartAlbum //经由相机得来的相册
    case Moment //Photos 为我们自动生成的时间分组的相册
}

enum PHAssetCollectionSubtype : Int {
    case AlbumRegular //用户在 Photos 中创建的相册
    case AlbumSyncedEvent //使用 iTunes 从 Photos 照片库或者 iPhoto 照片库同步过来的事件。然而,在iTunes 12 以及iOS 9.0 beta4上,选用该类型没法获取同步的事件相册,而必须使用AlbumSyncedAlbum。
    case AlbumSyncedFaces //使用 iTunes 从 Photos 照片库或者 iPhoto 照片库同步的人物相册。
    case AlbumSyncedAlbum //做了 AlbumSyncedEvent 应该做的事
    case AlbumImported //从相机或是外部存储导入的相册,完全没有这方面的使用经验,没法验证。
    case AlbumMyPhotoStream //用户的 iCloud 照片流
    case AlbumCloudShared //用户使用 iCloud 共享的相册
    case SmartAlbumGeneric //文档解释为非特殊类型的相册,主要包括从 iPhoto 同步过来的相册。
    case SmartAlbumPanoramas //相机拍摄的全景照片
    case SmartAlbumVideos //相机拍摄的视频
    case SmartAlbumFavorites //收藏文件夹
    case SmartAlbumTimelapses //延时视频文件夹,同时也会出现在视频文件夹中
    case SmartAlbumAllHidden //包含隐藏照片或视频的文件夹
    case SmartAlbumRecentlyAdded //相机近期拍摄的照片或视频
    case SmartAlbumBursts //连拍模式拍摄的照片
    case SmartAlbumUserLibrary //这个命名最神奇了,就是相机相册,所有相机拍摄的照片或视频都会出现在该相册中,而且使用其他应用保存的照片也会出现在这里。
    case Any //包含所有类型
}
  • startDate
  • endDate
  • estimatedAssetCount :估算的asset数量,不精确
    我模拟器 智能相册里,“最近删除”这个相册的对象信息
<PHAssetCollection: 0x7f82951bd640> 72053882-BF20-4D4A-B1A5-03D1DDAE1707/L0/040 Recently Deleted assetCollectionType=2/1000000201

1.3 PHCollectionList

一组有序的资源集合的集合,暂时还没用过。几个重要属性:

  • collectionListType
  • collectionListSubtype
  • startDate
  • endDate

2. 获取模型数据

PHAsset 、PHCollection、PHCollectionList有一系列类方法可供我们访问资源的元数据

2.1 比如PHAsset提供了一系列获取PHAsset对象的方法

+ fetchAssetsInAssetCollection:options:
+ fetchAssetsWithMediaType:options:
+ fetchAssetsWithLocalIdentifiers:options:
+ fetchKeyAssetsInAssetCollection:options:
+ fetchAssetsWithOptions:
+ fetchAssetsWithBurstIdentifier:options:
+ fetchAssetsWithALAssetURLs:options:

其中fetchAssetsInAssetCollection:options:方法可以获取资源集合中的所有asset对象。每个方法中的 PHFetchOptions参数,是获取asset对象的一些配置,我们可以设置获取asset的条件,比如获取哪种资源,如何分类。获取的时候,如果该参数为空,则使用系统的默认值,当我们调用如上所示方法获取时,可以直接传nil。

例子:

    PHFetchOptions *allPhotosOptions = [[PHFetchOptions alloc] init];
    // 按图片生成时间排序
    allPhotosOptions.sortDescriptors = @[[NSSortDescriptor sortDescriptorWithKey:@"creationDate" ascending:YES]];
    // 获取图片
    PHFetchResult *allPhotos = [PHAsset fetchAssetsWithOptions:allPhotosOptions];
    // 获取智能相册
    PHFetchResult *smartAlbums = [PHAssetCollection fetchAssetCollectionsWithType:PHAssetCollectionTypeSmartAlbum subtype:PHAssetCollectionSubtypeAlbumRegular options:nil];
    // 获取用户创建的相册
    PHFetchResult *topLevelUserCollections = [PHCollectionList fetchTopLevelUserCollectionsWithOptions:nil];

2.2 PHFetchOptions

  • predicate : 做选择的约束条件。比如,只获取图片,不获取视频。指定 PHAssetMediaType为image.

     //     swift:
    options.predicate = NSPredicate(format: "mediaType = %d", PHAssetMediaType.Image.rawValue)
  • sortDescriptors 可指定字段用来对获取结果进行排序
  • includeHiddenAssets 获取结果是否包括被隐藏的资源
  • includeAllBurstAssets 获取结果是否包括连拍资源
PHFetchOptions *options = [[PHFetchOptions alloc] init];
options.wantsIncrementalChangeDetails = YES;
options.includeAllBurstAssets = YES;
options.includeHiddenAssets = YES;
// 只取图片
options.predicate = [NSPredicate predicateWithFormat:@"mediaType == %d",PHAssetMediaTypeImage];
// 按时间排序
options.sortDescriptors = @[[NSSortDescriptor sortDescriptorWithKey:@"creationDate" ascending:YES]];

PHFetchResult *albums = [PHAssetCollection fetchAssetCollectionsWithType:PHAssetCollectionTypeSmartAlbum subtype:PHAssetCollectionSubtypeSmartAlbumAllHidden options:nil];

2.3 PHFetchResult

类似数组,存储获取到asset对象集合。

  • 同步快速获取结果,
  • 即使结果集很大,框架也能保证获取速度. 因为它不会一次性将所有结果放进内存,而是按需批量加载
  • 可以用类似 NSArray 的接口来访问PHFetchResult结果内的集合。
    比如 :
         PHCollection *collection = smartAlbums[n];

3. 资源对象的增删改

上面PHAsset PHAssetCollection PHCollectionList对象都是不可变的。那么我们如何实现资源增删改呢?要借助 request API :

request.png

比如:这段代码用来修改一张图片的资源属性:是否被收藏。

  // 创建
  request = [PHAssetChangeRequest creationRequestForAssetFromImage:image]

- (void)toggleFavoriteForAsset:(PHAsset *)asset {
    [[PHPhotoLibrary sharedPhotoLibrary] performChanges:^{
        // 改变
        PHAssetChangeRequest *request = [PHAssetChangeRequest changeRequestForAsset:asset];
        request.favorite = !asset.favorite;
          } completionHandler:^(BOOL success, NSError *error) {
        NSLog(@"Finished updating asset. %@", (success ? @"Success." : error));
    }];
}

(1) 创建PHAssetChangeRequest对象。想要修改资源,需要创建一个 PHAssetChangeRequest 。然后你就可以修改创建创建日期,资源位置,以及是否将隐藏资源,是否将资源看做用户收藏等。此外,你还可以从用户的库里删除资源。类似地,若要修改资源集合或集合列表,需要创建一个 PHAssetCollectionChangeRequestPHCollectionListChangeRequest对象。然后你就可以修改集合标题,添加或删除集合成员,或者完全删除集合。
(2) 操作的请求都要求在PHPhotoLibrary的performChanges的changeBlock中执行
(3)如果有更新UI操作,需要遵守PHPhotoLibraryChangeObserver协议,实现photoLibraryDidChange(changeInfo: PHChange!)方法.在photoLibraryDidChange中进行UI更新操作。

比如,下面2段代码用来向册中新增一张图片:

    // 为image对象生成PHAsset对象,并加入collection
    [[PHPhotoLibrary sharedPhotoLibrary] performChanges:^{
        PHAssetChangeRequest *assetChangeRequest = [PHAssetChangeRequest creationRequestForAssetFromImage:image];

        if (self.assetCollection) {
            PHAssetCollectionChangeRequest *assetCollectionChangeRequest = [PHAssetCollectionChangeRequest changeRequestForAssetCollection:self.assetCollection];
            [assetCollectionChangeRequest addAssets:@[[assetChangeRequest placeholderForCreatedAsset]]];
        }
    } completionHandler:^(BOOL success, NSError *error) {
        if (!success) {
            NSLog(@"Error creating asset: %@", error);
        }
    }];

创建一个新资源只要用 creationRequestForAssetFromXXX(...)
工厂方法,来创建变化请求,并传入资源图像数据 (或一个 URL)。如果你需要对新建的资源做额外的修改,你可以用创建变化请求的placeholderForCreatedAsset属性。它会返回一个可用的 placeholder 来代替“真实的” PHAsset 引用.

// PHPhotoLibraryChangeObserver协议的方法,更新相册UI
- (void)photoLibraryDidChange:(PHChange *)changeInstance {
    // Check if there are changes to the assets we are showing.
    PHFetchResultChangeDetails *collectionChanges = [changeInstance changeDetailsForFetchResult:self.assetsFetchResults];
    if (collectionChanges == nil) {
        return;
    }

    /*
        Change notifications may be made on a background queue. Re-dispatch to the
        main queue before acting on the change as we'll be updating the UI.
     */
    dispatch_async(dispatch_get_main_queue(), ^{
        // Get the new fetch result.
        self.assetsFetchResults = [collectionChanges fetchResultAfterChanges];

        UICollectionView *collectionView = self.collectionView;

        if (![collectionChanges hasIncrementalChanges] || [collectionChanges hasMoves]) {
            // Reload the collection view if the incremental diffs are not available
            [collectionView reloadData];

        } else {
            /*
                Tell the collection view to animate insertions and deletions if we
                have incremental diffs.
             */
            [collectionView performBatchUpdates:^{
                NSIndexSet *removedIndexes = [collectionChanges removedIndexes];
                if ([removedIndexes count] > 0) {
                    [collectionView deleteItemsAtIndexPaths:[removedIndexes aapl_indexPathsFromIndexesWithSection:0]];
                }

                NSIndexSet *insertedIndexes = [collectionChanges insertedIndexes];
                if ([insertedIndexes count] > 0) {
                    [collectionView insertItemsAtIndexPaths:[insertedIndexes aapl_indexPathsFromIndexesWithSection:0]];
                }

                NSIndexSet *changedIndexes = [collectionChanges changedIndexes];
                if ([changedIndexes count] > 0) {
                    [collectionView reloadItemsAtIndexPaths:[changedIndexes aapl_indexPathsFromIndexesWithSection:0]];
                }
            } completion:NULL];
        }

        [self resetCachedAssets];
    });
}

3.1 PHPhotoLibrary

系统中PHPhotoLibrary单例对象 是用来维护用户照片库。当我们需要编辑资源对象元数据、资源内容、或者插入新的资源对象等,都可以借助通过PHPhotoLibrary单例对象执行block,block中创建我们指定的请求对象(比如PHAssetChangeRequest,PHAssetCollectionChangeRequest, PHCollectionListChangeRequest的对象)。photoLibraryDidChange(changeInfo: PHChange!)中进行.

3.2 协议PHPhotoLibraryChangeObserver

PHPhotoLibraryChangeObserver协议能让我们知道照片相册库中的改变。Photos会发送系统图片改变的消息,我们可以遵守PHPhotoLibraryChangeObserver协议,并通过 PHPhotoLibrary的registerChangeObserver方法将对象注册为观察者,时时接收照片改变的消息。

3.3 PHChange

PHphoto框架会提供给我们PHChange对象,我们可以调用changeDetailsForObject或者changeDetailsForFetchResult 方法,它返回给我们一个PHObjectChangeDetails对象,是对最新的照片实体对象的引用,可以告诉我们对象的图像数据是否曾变化过、对象是否曾被删除过。

4.请求图片和视频数据

4.1 PHImageManager

PHImageManager在框架中是个单例对象,用[PHImageManager defaultManager]获取,它提供了加载图片和视频的方法。
比如:图像请求是通过 requestImageForAsset(...) 方法派发的。
这些方法的参数:

  • 一个 PHAsset对象,
  • PHImageRequestOptions 参数对象:可以设置返回图像的大小和图像的其它可选项
  • 结果回调 (result handler)。
请求图片
- requestImageForAsset:targetSize:contentMode:options:resultHandler:
- requestImageDataForAsset:options:resultHandler:
请求视频
- requestPlayerItemForVideo:options:resultHandler:
- requestExportSessionForVideo:options:exportPreset:resultHandler:
- requestAVAssetForVideo:options:resultHandler:

默认情况下,这些API是异步执行,但是我们可以通过options参数中的synchronous属性,设置为同步执行,这些方法会阻塞当前调用线程直到下载完成或者发生错误。

4.2 请求图片

//加载 160X160 像素的图片
[manager requestImageForAsset:photo
                   targetSize:CGSizeMake(160,160)
                  contentMode:PHImageContentModeAspectFill
                      options:nil
                resultHandler:^(UIImage *result, NSDictionary *info) {
    if (result) {
        [cell setImage:result];
    } else {
... }
}];

targetSize: 返回图片的尺寸

contentMode :决定了照片应该以按比例缩放还是按比例填充的方式放到目标大小内。

options: PHImageRequestOptions类用于定制请求。上面的方法返回指定尺寸的图像,如果你仅仅指定必要的参数而没有对 options 进行配置的话,返回的图像尺寸将会是原始图像的尺寸。或者,你指定的尺寸很小,这时候会按照你的要求来返回接近该尺寸的图像。

4.2.1 PHImageRequestOptions有以下几个重要的属性:

synchronous: 指定请求是否同步执行。

resizeMode: 对请求的图像怎样缩放。有三种选择:None,不缩放;Fast,尽快地提供接近或稍微大于要求的尺寸;Exact,精准提供要求的尺寸。

deliveryMode: 图像质量。有三种值:Opportunistic,在速度与质量中均衡;HighQualityFormat,不管花费多长时间,提供高质量图像;FastFormat,以最快速度提供好的质量。

normalizedCropRect: 用于对原始尺寸的图像进行裁剪,基于比例坐标。只在 resizeMode 为 Exact 时有效。

resizeMode 默认是 None,这也造成了返回图像尺寸与要求尺寸不符。这点需要注意。要返回一个指定尺寸的图像需要避免两层陷阱:一定要指定 options 参数,resizeMode 不能为 None。

PHImageManager.png

是否裁剪图片由 targetSize、contentMode来控制,裁剪的尺寸由targetSize、contentMode、resizeMode指定。

比如

// 下载原图:
options.deliveryMode = PHImageRequestOptionsDeliveryModeHighQualityFormat;
options.resizeMode = PHImageRequestOptionsResizeModeExact;
options.progressHandler = (PHAssetImageProgressHandler)progress;
targetSize = PHImageManagerMaximumSize;
contentMode = PHImageContentModeDefault;

// 全屏图
 options.deliveryMode = PHImageRequestOptionsDeliveryModeHighQualityFormat;
options.resizeMode = PHImageRequestOptionsResizeModeExact;
options.progressHandler = (PHAssetImageProgressHandler)progress;
targetSize = UIScreen.mainScreen.bounds.size;
contentMode = PHImageContentModeAspectFit;

4.2.2 resultHandler可能被多次调用

见上图,

  • 如果synchronous属性为YES,同步请求,resultHandler只会执行一次,并且返回高质量的图片。

Paste_Image.png

  • 但是如果synchronous为NO,resultHandler是否会被多次调用取决于deliveryMode属性:

    • 如果deliveryMode是.HighQualityFormat ,resultHandler调用一次,框架只返回高质量图。
    • 如果deliveryMode是FastFormat,resultHandler也只被调用一次,保证速度尽可能保证图片质量。
    • deliveryMode是.Opportunistic Photos 可能会先提供低质量的图像以供临时显示,随后会将指定尺寸的图像返回。如果指定尺寸的高质量的图像有缓存,那么直接提供高质量的图像。如图,第一次同步调用请求返回一张模糊的图,第二次再去下载一张高质量的图片:

result_handler.png

resultHandler中的info 字典提供了关于当前请求状态的信息,比如:

  • 图像是否必须从 iCloud 请求 (如果你初始化时将 networkAccessAllowed 设置成 false,那么就必须重新请求图像) —— PHImageResultIsInCloudKey 。
  • 当前递送的 UIImage 是否是最终结果的低质量格式。当高质量图像正在下载时,这个可以让你给用户先展示一个预览图像 —— PHImageResultIsDegradedKey。
  • 请求 ID (可以便捷的取消请求),以及请求是否已经被取消。 —— PHImageResultRequestIDKey 和 PHImageCancelledKey。
    如果没有图像提供给 result handler,字典内还会有一个错误信息 —— PHImageErrorKey。

比如用-requestImageForAsset:targetSize:contentMode:options:resultHandler:接口请求图片

// 入参:video对应的PHAsset对象
// info字典返回:
"PHImageResultRequestIDKey" : (int)1
"PHImageResultImageTypeKey" : (long)0
"PHImageResultDeliveredImageFormatKey" : (long)5005
"PHImageResultIsDegradedKey" : @"0"
注: 返回的image是video第一帧的截图

// 入参:图片对应的PHAsset对象
// info字典返回:
"PHImageFileOrientationKey" : (long)0
"PHImageResultWantedImageFormatKey" : (long)5005
"PHImageResultRequestIDKey" : (int)2
"PHImageResultDeliveredImageFormatKey" : (long)4031
"PHImageResultIsDegradedKey" : @"1"

4.3 请求视频

// 视频请求对象
PHVideoRequestOptions *options = [PHVideoRequestOptions new];
// 最高质量的视频
options.deliveryMode = PHVideoRequestOptionsDeliveryModeHighQualityFormat;
// 可从iCloud中获取图片
options.networkAccessAllowed = YES;
// 如果是iCloud的视频,可以获取到下载进度
options.progressHandler = ^(double progress, NSError *error, BOOL *stop) {
    [self updateUserVisibleProgress:progress error:error];
         // 此回调block可能不在UI线程中,我们需要在UI线程中更新UI
        dispatch_async(dispatch_get_main_queue(), ^{
            self.progressView.progress = progress;
        });

};
// 方法1
[manager requestPlayerItemForVideo:video options:options resultHandler:....]
// 方法2
[manager requestExportSessionForVideo:video options:options ...];
// 方法3
[manager requestAVAssetForVideo:video options:options resultHandler...];

5. 滚动性能:缓存

5.1 PHCachingImageManager

滚动一系列缩略图时,我们可以在可视区域前后维护一些数据缓存。如图:

cache.png

  1. 生成PHCachingImageManager实例
  2. 调用方法startCachingImagesForAssets:targetSize:contentMode:options:,并指定 目标尺寸target size,内容模式content mode,选项options参数
  3. 需要从asset对象中获取图片的时候,调用
    requestImageForAsset:targetSize:contentMode:options:resultHandler
PHCachingImageManager *cim =  [[PHCachingImageManager alloc] init];
NSArray *soonToBeVisibleAssets = [cim startCachingImagesForAssets:soonToBeVisibleAssets
                                             targetSize:targetSize
                                         contentMode:PHImageContentModeAspectFill
                                                    options:nil];
NSArray *previouslyVisibleAssets = [cim stopCachingImagesForAssets:previouslyVisibleAssets
                                                         targetSize:targetSize
                                                         contentMode:PHImageContentModeAspectFill
                                                               options:nil];

// 从缓存中读取相片

  // Request an image for the asset from the PHCachingImageManager.
    [self.imageManager requestImageForAsset:asset
         targetSize:AssetGridThumbnailSize
        contentMode:PHImageContentModeAspectFill
         options:nil
         resultHandler:^(UIImage *result, NSDictionary *info) {
        // Set the cell's thumbnail image if it's still showing the same asset.
        if ([cell.representedAssetIdentifier isEqualToString:asset.localIdentifier]) {
            cell.thumbnailImage = result;
        }
    }];

官方demo中计算缓存区域的算法比较有意思

- (void)computeDifferenceBetweenRect:(CGRect)oldRect andRect:(CGRect)newRect removedHandler:(void (^)(CGRect removedRect))removedHandler addedHandler:(void (^)(CGRect addedRect))addedHandler {
    if (CGRectIntersectsRect(newRect, oldRect)) {
        CGFloat oldMaxY = CGRectGetMaxY(oldRect);
        CGFloat oldMinY = CGRectGetMinY(oldRect);
        CGFloat newMaxY = CGRectGetMaxY(newRect);
        CGFloat newMinY = CGRectGetMinY(newRect);

        if (newMaxY > oldMaxY) {
            CGRect rectToAdd = CGRectMake(newRect.origin.x, oldMaxY, newRect.size.width, (newMaxY - oldMaxY));
            addedHandler(rectToAdd);
        }

        if (oldMinY > newMinY) {
            CGRect rectToAdd = CGRectMake(newRect.origin.x, newMinY, newRect.size.width, (oldMinY - newMinY));
            addedHandler(rectToAdd);
        }

        if (newMaxY < oldMaxY) {
            CGRect rectToRemove = CGRectMake(newRect.origin.x, newMaxY, newRect.size.width, (oldMaxY - newMaxY));
            removedHandler(rectToRemove);
        }

        if (oldMinY < newMinY) {
            CGRect rectToRemove = CGRectMake(newRect.origin.x, oldMinY, newRect.size.width, (newMinY - oldMinY));
            removedHandler(rectToRemove);
        }
    } else {
        addedHandler(newRect);
        removedHandler(oldRect);
    }
}

参考

WWDC

官方例子

Photos 框架实践以及坑

照片框架

时间: 2024-11-03 04:58:44

iOS8 Photos Framework的相关文章

开发者所需要知道的 iOS8 SDK 新特性

WWDC 2014 已经过去一个多月.最激动人心的莫过于 Swift 这门新语言的发布,我在之前已经写了一些关于这么语言的第一印象和一些初步的探索.在写这篇文章的时候,Swift 随着 beta 3 得到了重大的更新,而这门语言现在也还在剧烈的变化之中.对于 Swift,现在大家的探索才刚刚上路,很多背后的机制还并不是非常清楚,或者有可能发生巨大的变化,因此在这里和之后的几篇文章,直到稳定的 1.0 版本出现,我不再打算继续深入针对 Swift 写什么文章.这基本出于对未来可能的变化会容易误导读

iOS 新特性分列式 之 iOS 9.1 - 主要内容:苹果笔 API 引入

iOS 新特性分列式 之 iOS 9.1 - 主要内容:苹果笔 API 引入 太阳火神的美丽人生 (http://blog.csdn.net/opengl_es) 本文遵循"署名-非商业用途-保持一致"创作公用协议 转载请保留此句:太阳火神的美丽人生 -  本博客专注于 敏捷开发及移动和物联设备研究:iOS.Android.Html5.Arduino.pcDuino,否则,出自本博客的文章拒绝转载或再转载,谢谢合作. ==================================

Swift 共享文件操作小结(iOS 8 +)_IOS

前言 适用于 iOS 8 + 本地共享文件列表 正文 一.准备 1.1 默认 App 的文件共享是关闭的,需要在 plist 中设置启用: Application supports iTunes file sharing 设置为 YES 启用后把设备连接到 iTunes 上,在 iTunes 应用里的文件共享就能看到你的 App 了(如果看不见需要断开重新拔插一下数据线),可以拷贝一些视频进去,便于测试. 1.2 导入库 Photos.framework AVKit.framework 用于播放

iOS实现图片保存与搜索功能_IOS

本文实现Photos.framework的常见功能,包括:创建自定义相册.保存图片到自定义相册.搜索所有相册的图片,具体内容如下1.保存图片到相册 /** * 保存图片到相册 */ - (IBAction)saveImage { PHAuthorizationStatus status = [PHPhotoLibrary authorizationStatus]; if (status == PHAuthorizationStatusDenied) { NSLog(@"请到[设置-隐私-照片]打

iOS8新特性扩展(Extension)应用之三——照片编辑插件

iOS8新特性扩展(Extension)应用之三--照片编辑插件         通过前几篇博客的介绍,我们了解到扩展给app提供的更加强大的交互能力,这种强大的交互能力另一方面体现在照片编辑插件的应用.        和通常一样,我们先创建一个工程,然后新建一个Target,选择photo editing: 从模板中,我们可以看到系统为我们创建了一个controller,这个controller就是用于处理照片的controller,其中方法如下: ? 1 2 3 4 5 6 7 8 9 10

ios8新特性:pushkit实战总结(voip开发者必读)

Question:pushkit是什么? Answer:ios8苹果新引入了名为pushkit的框架和一种新的push通知类型,被称作voip push.该push方式旨在提供区别于普通apns push的能力,通过这种push方式可以使app执行制定的代码(在弹出通知给用户之前):而该通知的默认行为和apns通知有所区别,它的默认行为里面是不会弹出通知的.目前来看push kit的用途还局限于voip push(根据笔者的实战经验来看,其他类型的push暂时不能够起作用,sdk也正处于演进中)

microsoft .net framework 类型初始值设定项引发异常

问题描述 故障截图:https://plus.google.com/photos/101869617055522555338/albums/5704392906520363393?authkey=COSJvpWomePJqQE--------------------------以下是故障提示,我看不懂---------------------------------------------有关调用实时(JIT)调试而不是此对话框的详细信息,请参见此消息的结尾.**************异常文本

.NET Framework For Java Programmers

.NET Framework For Java Programmers Author: Ashish Banerjee Objective After reading this article Java programmers should be able to decipher and de-jargonize the .NET architecture and relate it with the proposed ECMA standard.    Target Audience Java

iOS8新特性扩展(Extension)应用之四——自定义键盘控件

iOS8新特性扩展(Extension)应用之四--自定义键盘控件         iOS8系统的开放第三方键盘,使得用户在输入法的选择上更加自主灵活,也更加贴近不同语言的输入风格.这篇博客,将介绍如何开发一个第三方的键盘控件. 一.了解UIInputViewController类         UIInputViewController是系统扩展支持键盘扩展的一个类,通过这个类,我们可以自定义一款我们自己的键盘提供给系统使用.         首先,我们先来看一下这个类中的一些属性和方法: