Detailed explanation of iOS development photo framework and ios photo framework
I. Summary
Photos and videos are a very important part of iOS devices. Recently, I just made a custom iOS image selector. By the way, I sorted out how to use the photo framework in iOS. Before the emergence of iOS 8, developers can only use the AssetsLibrary framework to access the Photo Library of devices. This is a framework that is not keeping up with the pace of iOS application development and code design principles, but is indeed powerful, considering that iOS7 still occupies a lot of penetration rates, AssetsLibrary is also the focus of this article. After the emergence of iOS8, Apple provided a framework named PhotoKit, which allows applications to better connect to the Photo Library of devices. This framework will also be introduced at the end of this article.
It is also worth noting that in iOS, the photo library is not only a collection of photos, but also a video. In AssetsLibrary, both objects of the same type are described, but the types are different. For convenience, "resource" is usually used to represent "photos and videos" in iOS 」.
II. Introduction to AssetsLibrary Composition
The composition of AssetsLibrary is more in line with the composition of the Photo Library itself. The complete photo library objects, albums, and photos can all be found in AssetsLibrary, which makes the use of AssetsLibrary intuitive and convenient.
- AssetsLibrary: Indicates the resource library (photo library) of the entire device. You can use AssetsLibrary to obtain and include photos and videos from the device.
- ALAssetsGroup: Maps an album in the photo album. You can use ALAssetsGroup to obtain information about a photo album, resources under the photo album, and add resources to a photo album.
- ALAsset: Maps a photo or video in the photo library. You can use ALAsset to obtain details of a photo or video, or save photos and videos.
- ALAssetRepresentation: ALAssetRepresentation is the encapsulation of ALAsset (but not its subclass). It can more easily obtain Resource Information in ALAsset. Each ALAsset has at least one ALAssetRepresentation object, which can be obtained through defaultRepresentation. For example, two ALAssetRepresentation images are generated for RAW and JPEG images taken by the System camera. One is used to encapsulate the RAW information of the image, and the other is used to encapsulate the JPEG information of the image.
III. Basic use of AssetsLibrary
AssetsLibrary has many functions, which can be divided into two parts: resource acquisition/storage. The storage part is relatively simple and the API is relatively small. Therefore, this section is not detailed here. The APIS for obtaining resources are rich. A common example of using a large number of AssetsLibrary APIS is the image selector (ALAsset Picker ). To create an image selector, the idea should be to get the photo library-list all albums-display all images in the album-preview the picture in the big picture.
First, check whether the App has photo authorization:
12345678910 |
NSString *tipTextWhenNoPhotosAuthorization; // Prompt // Obtain the photo Access authorization status of the current application ALAuthorizationStatus authorizationStatus = [ALAssetsLibrary authorizationStatus]; // If no access authorization is obtained or the access authorization status is explicitly forbidden, a prompt is displayed to guide the user to enable authorization. if (authorizationStatus == ALAuthorizationStatusRestricted || authorizationStatus == ALAuthorizationStatusDenied) { NSDictionary *mainInfoDictionary = [[ NSBundle mainBundle] infoDictionary]; NSString *appName = [mainInfoDictionary objectForKey: @"CFBundleDisplayName" ]; tipTextWhenNoPhotosAuthorization = [ NSString stringWithFormat: @ "Please allow % @ access to your mobile phone album in the \" Settings-privacy-photos \ "option of the device" , appName]; // Display prompt } |
If you have obtained the authorization, you can obtain the album list:
12345678910111213141516171819 |
_assetsLibrary = [[ALAssetsLibrary alloc] init]; _albumsArray = [[ NSMutableArray alloc] init]; [_assetsLibrary enumerateGroupsWithTypes:ALAssetsGroupAll usingBlock:^(ALAssetsGroup *group, BOOL *stop) { if (group) { [group setAssetsFilter:[ALAssetsFilter allPhotos]]; if (group.numberOfAssets > 0) { // Save the album to an array for later display [_albumsArray addObject:group]; } } else { if ([_albumsArray count] > 0) { // Save all the albums to display the album list } else { // If there is no album with resources, a prompt is displayed. } } } failureBlock:^( NSError *error) { NSLog ( @"Asset group not found!\n" ); }]; |
In the code above, all the album lists are traversed, and the reference of the album ALAssetGroup object with no blank Chinese album sources is stored in an array. The following points need to be emphasized:
- In iOS, an empty Album is allowed, that is, there is no resource in the album. If you do not want to obtain an empty Album, You need to manually filter it as in the code above.
- ALAssetsGroup has a setAssetsFilter method that can be used to pass in a filter. You can only get photos in the album or videos. Once filtering is set, the resource list and resource quantity in ALAssetsGroup are automatically updated.
- AssetsLibrary uses Asynchronous processing (Asynchronous) to obtain and save albums and resources. This is because the volume of resource files is quite large (and possibly large ). For example, in the previous photo album traversal operation, the album results are output using blocks. If the album traversal is complete, the group parameter value in the last output block is nil. The stop parameter is used to manually stop traversal. If you set * stop to YES, the next traversal will be stopped. This may cause misunderstandings, so you need to pay attention to it.
Now you can get the album. Next, you can get the resources in the album:
12345678 |
_imagesAssetArray = [[ NSMutableArray alloc] init]; [assetsGroup enumerateAssetsWithOptions: NSEnumerationReverse usingBlock:^(ALAsset *result, NSUInteger index, BOOL *stop) { if (result) { [_imagesAssetArray addObject:result]; } else { // The result is nil, that is, after the photos or videos are traversed, the resource list can be displayed. } }]; |
Similar to the process of traversing an album, traversing a photo also uses a series of asynchronous methods. In the block output by the above method, in addition to the result parameter indicating resource information, stop is used to stop traversing manually, an index parameter is also provided, which indicates the index of the resource. Generally, the result. thumbnail (result. thumbnail) is used to display the resource list. Therefore, even if there are many resources, the resource traversal speed will be quite fast. However, if you do need to load the resource's high-definition graph or other time-consuming processing, you can use the index and stop parameters above to pull the resource in segments. For example:
1234567891011121314151617181920212223 |
NSUInteger _targetIndex; // Index target value, pull the resource until this value is manually stopped NSUInteger _currentIndex; // Current index, starting from this value each time the resource is pulled _targetIndex = 50; _currentIndex = 0; - ( void )loadAssetWithAssetsGroup:(assetsGroup *)assetsGroup { [assetsGroup enumerateAssetsAtIndexes:[ NSIndexSet indexSetWithIndex:_currentIndex] options: NSEnumerationReverse usingBlock:^(ALAsset *result, NSUInteger index, BOOL *stop) { _currentIndex = index; if (index > _targetIndex) { // If the index used to pull the resource is larger than the target value, stop pulling the resource. *stop = YES ; } else { if (result) { [_imagesAssetArray addObject:result]; } else { // The result is nil, indicating that the photo or video has been traversed. } } }]; } // The previously pulled data has been displayed. You need to display new data, call the loadAssetWithAssetsGroup method again, and update the value of _ targetIndex as needed. |
The last step is to obtain the image details, for example:
1234 |
// Obtain the detailed resource information of the resource image. imageAsset is the ALAsset object of a resource. ALAssetRepresentation *representation = [imageAsset defaultRepresentation]; // Obtain the fullScreenImage of the resource Image UIImage *contentImage = [UIImage imageWithCGImage:[representation fullScreenImage]]; |
An ALAssetRepresentation contains multiple versions of the image. The most common ones are fullResolutionImage and fullScreenImage. FullResolutionImage is the source image of the image. The images obtained through fullResolutionImage are not processed, including the information that has been processed by the "edit" function in the system album, therefore, you need to display the information processed by the "edit" function. Using fullResolutionImage is inconvenient. In addition, the pull of fullResolutionImage is slow, and the image loading process can be clearly felt when switching between multiple fullresolutionimages. Therefore, we recommend that you obtain the image's fullScreenImage, which is the full-screen image version. This version contains the information processed by the "edit" function in the system album and a thumbnail, however, the image has little distortion, but the disadvantage is that the image size is a version that adapts to the screen size. Therefore, additional processing is required when the image is displayed, however, we recommend that you use fullScreenImage because the loading speed is very high (the image loading time cannot be felt when switching between multiple images.
The processing process of the system album is also like this. We can see that the full resolutionimage of the image is not used throughout the process. thumbnails are used from the album list display to the final view of resources, this is also an important reason for iOS album loading.
Iii. AssetsLibrary's pitfalls
As a set of old frameworks, AssetsLibrary is not only flawed, but also has a lot of problems. In addition to the precautions for asynchronous pulling of resources mentioned above, the following points are worth noting:
1. AssetsLibrary instances must be strongly referenced
After an AssetsLibrary instance, as shown above, we can obtain the required album and resources through a series of enumeration methods and store them in an array for ease of display. However, when we store these acquired albums and resources to an array, we actually only store these albums and resources in the array for reference (pointer) in AssetsLibrary ), therefore, no matter how to use the data after storing the album and resource arrays, we must first ensure that AssetsLibrary is not released by ARC. Otherwise, when we extract the data from the array, the reference data is lost (see ). This is easy to ignore. Therefore, we recommend that you use AssetsLibrary's viewController as a strongly held property or private variable to avoid the need to enumerate the data required by AssetsLibrary, assetsLibrary is released by ARC.
For example, instantiate an AssetsLibrary local variable, enumerate all albums, store them in an array named _ albumsArray, view the array again when displaying the album, and find that the data in ALAssetsGroup has been lost.
2. AssetsLibrary follows the write priority principle
Write first, that is, when using AssetsLibrary to read resources, any other process (not necessarily the same App) will receive ALAssetsLibraryChangedNotification when saving resources, the read operation is interrupted by the user. The most common one is to use a process to write a fullResolutionImage. because it takes a long time to read fullResolutionImage, it is easy to get exception.
3. Enabling Photo Stream easily causes exception
Essentially, this is the same problem as AssetsLibrary follows the write priority principle. If you enable the shared Photo Stream, the shared Photo Stream is secretly executed in the form of mstreamd. When someone writes the Photo to Camera Roll, it will be automatically saved to the Photo Stream Album. If the user is reading the file, the exception will be generated as mentioned above. Because the user decides whether to enable the shared photo stream, developers cannot change it. However, the following interface can be used to disable frequent notification messages when the shared photo stream needs to be protected.
1 |
[ALAssetsLibrary disableSharedPhotoStreamsSupport]; |
Iv. Introduction to PhotoKit
PhotoKit is a set of more complete and efficient libraries than AssetsLibrary, and its processing of resources is quite different from AssetsLibrary.
First, we will briefly introduce several concepts:
- PHAsset: Indicates a resource in the photo gallery. Similar to ALAsset, PHAsset can be used to obtain and save resources.
- PHFetchOptions: When obtaining resources, you can pass the parameter nil, that is, use the system default value.
- PHFetchResult: Indicates a series of resource sets or album sets.
- PHAssetCollection: Indicates an album or a time point, or a "smart album" (a specific series of albums provided by the system, such as recently deleted albums, video lists, and favorites, as shown in)
- PHImageManager: Used to process resource loading. The image loading process carries cache processing. You can pass in a PHImageRequestOptions to control the resource output size and other specifications.
- PHImageRequestOptions: As mentioned above, it controls a series of parameters during image loading.
The second section of UITableView in is all the smart albums listed by PhotoKit.
List several code snippets to show how to obtain the album and the code of resources in a specific album:
1234567891011121314151617181920212223 |
// List all albums. PHFetchResult *smartAlbums = [PHAssetCollection fetchAssetCollectionsWithType:PHAssetCollectionTypeSmartAlbum subtype:PHAssetCollectionSubtypeAlbumRegular options: nil ]; // List albums created by all users PHFetchResult *topLevelUserCollections = [PHCollectionList fetchTopLevelUserCollectionsWithOptions: nil ]; // Obtain the set of all resources and sort them by the Resource Creation Time. PHFetchOptions *options = [[PHFetchOptions alloc] init]; options.sortDescriptors = @[[ NSSortDescriptor sortDescriptorWithKey: @"creationDate" ascending: YES ]]; PHFetchResult *assetsFetchResults = [PHAsset fetchAssetsWithOptions:options]; // Obtain the first set in the resource set and obtain the image PHCachingImageManager *imageManager = [[PHCachingImageManager alloc] init]; PHAsset *asset = assetsFetchResults[0]; [imageManager requestImageForAsset:asset targetSize:SomeSize contentMode:PHImageContentModeAspectFill options: nil resultHandler:^(UIImage *result, NSDictionary *info) { // Obtain a UIImage, which is displayed on the interface. }]; |
Based on the above code snippets, PhotoKit has three major improvements compared with AssetsLibrary:
- Retrieving data from AssetsLibrary is essentially an enumeration method, which traverses the photo album and resources to obtain the corresponding data. PhotoKit directly acquires the corresponding data by passing in parameters, which improves the efficiency.
- In AssetsLibrary, album and resource correspond to different objects (ALAssetGroup and ALAsset). Therefore, there are two completely unrelated interfaces for getting album and resource. In PhotoKit, PHFetchResult can be used to store album or resource objects in a unified manner. Therefore, it is convenient to process album and resource.
- When PhotoKit returns the resource result, it also returns the metadata of the resource. It is difficult to obtain the metadata in AssetsLibrary. At the same time, with PHAsset, developers can also directly obtain whether resources are added to Favorites (favorite) and hidden (hidden), and whether the HDR or panoramic mode is enabled for image shooting, you can even obtain other images from a picture. This is also the beginning of the article,PhotoKit is an important factor for better integration with the Photo Library of devices.
For more information about PhotoKit, see Apple's Example app using Photos framework.