iOS 将照片保存在特定应用的相册中
Posted
技术标签:
【中文标题】iOS 将照片保存在特定应用的相册中【英文标题】:iOS save photo in an app specific album 【发布时间】:2012-08-15 15:20:51 【问题描述】:我正在创建一个 ios 5 应用程序。我想将照片保存到设备中。
我想将照片保存到特定于我的应用的相册中,因此我需要创建相册,然后将照片保存到相册中。
我知道如何创建相册:
ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init];
[library addAssetsGroupAlbumWithName:@"MY APP NAME" resultBlock:^(ALAssetsGroup *group)
//How to get the album URL?
failureBlock:^(NSError *error)
//Handle the error
];
我现在想将照片添加到新相册,我该怎么做?示例代码非常感谢!
【问题讨论】:
This article 似乎可以通过自定义ALAssetsLibrary
类别实现您正在寻找的东西。看起来文章下面有一些有用的 cmets 也可能有助于提高性能。
【参考方案1】:
您可以使用以下代码更改专辑名称:
__weak ALAssetsLibrary *lib = self.library;
[self.library addAssetsGroupAlbumWithName:@"My Photo Album" resultBlock:^(ALAssetsGroup *group)
///checks if group previously created
if(group == nil)
//enumerate albums
[lib enumerateGroupsWithTypes:ALAssetsGroupAlbum
usingBlock:^(ALAssetsGroup *g, BOOL *stop)
//if the album is equal to our album
if ([[g valueForProperty:ALAssetsGroupPropertyName] isEqualToString:@"My Photo Album"])
//save image
[lib writeImageDataToSavedPhotosAlbum:UIImagePNGRepresentation(image) metadata:nil
completionBlock:^(NSURL *assetURL, NSError *error)
//then get the image asseturl
[lib assetForURL:assetURL
resultBlock:^(ALAsset *asset)
//put it into our album
[g addAsset:asset];
failureBlock:^(NSError *error)
];
];
failureBlock:^(NSError *error)
];
else
// save image directly to library
[lib writeImageDataToSavedPhotosAlbum:UIImagePNGRepresentation(image) metadata:nil
completionBlock:^(NSURL *assetURL, NSError *error)
[lib assetForURL:assetURL
resultBlock:^(ALAsset *asset)
[group addAsset:asset];
failureBlock:^(NSError *error)
];
];
failureBlock:^(NSError *error)
];
【讨论】:
太好了,它的工作。我在声明 ALAssetsLibrary *lib 时删除了 __weak,因为 completionBlock 没有调用。【参考方案2】:对于希望从 iOS 9 开始执行此操作的任何人,由于 ALAssetsLibrary 已被弃用,取而代之的是新的照片库,因此事情变得有些复杂。
这里有一些用于将 UIImages 添加到特定专辑名称的 Swift 代码(如果专辑不存在,则创建该专辑),您可能需要根据需要进行一些重构/优化:
func insertImage(image : UIImage, intoAlbumNamed albumName : String)
//Fetch a collection in the photos library that has the title "albumNmame"
let collection = fetchAssetCollectionWithAlbumName(albumName)
if collection == nil
//If we were unable to find a collection named "albumName" we'll create it before inserting the image
phphotoLibrary.sharedPhotoLibrary().performChanges(
PHAssetCollectionChangeRequest.creationRequestForAssetCollectionWithTitle(albumName)
, completionHandler: (success : Bool, error : NSError?) in
if error != nil
print("Error: " + error!.description)
if success
//Fetch the newly created collection (which we *assume* exists here)
let newCollection = self.fetchAssetCollectionWithAlbumName(albumName)
self.insertImage(image, intoAssetCollection: newCollection!)
)
else
//If we found the existing AssetCollection with the title "albumName", insert into it
self.insertImage(image, intoAssetCollection: collection!)
func fetchAssetCollectionWithAlbumName(albumName : String) -> PHAssetCollection?
//Provide the predicate to match the title of the album.
let fetchOption = PHFetchOptions()
fetchOption.predicate = NSPredicate(format: "title == '" + albumName + "'")
//Fetch the album using the fetch option
let fetchResult = PHAssetCollection.fetchAssetCollectionsWithType(
PHAssetCollectionType.Album,
subtype: PHAssetCollectionSubtype.AlbumRegular,
options: fetchOption)
//Assuming the album exists and no album shares it's name, it should be the only result fetched
let collection = fetchResult.firstObject as? PHAssetCollection
return collection
func insertImage(image : UIImage, intoAssetCollection collection : PHAssetCollection)
//Changes for the Photos Library must be maded within the performChanges block
PHPhotoLibrary.sharedPhotoLibrary().performChanges(
//This will request a PHAsset be created for the UIImage
let creationRequest = PHAssetCreationRequest.creationRequestForAssetFromImage(image)
//Create a change request to insert the new PHAsset in the collection
let request = PHAssetCollectionChangeRequest(forAssetCollection: collection)
//Add the PHAsset placeholder into the creation request.
//The placeholder is used because the actual PHAsset hasn't been created yet
if request != nil && creationRequest.placeholderForCreatedAsset != nil
request!.addAssets([creationRequest.placeholderForCreatedAsset!])
,
completionHandler: (success : Bool, error : NSError?) in
if error != nil
print("Error: " + error!.description)
)
【讨论】:
【参考方案3】:对于那些在 Objective-C.
中寻找 Eddy 答案的人
#import <Photos/Photos.h>
- (void)insertImage:(UIImage *)image intoAlbumNamed:(NSString *)albumName
//Fetch a collection in the photos library that has the title "albumNmame"
PHAssetCollection *collection = [self fetchAssetCollectionWithAlbumName: albumName];
if (collection == nil)
//If we were unable to find a collection named "albumName" we'll create it before inserting the image
[[PHPhotoLibrary sharedPhotoLibrary] performChanges:^
[PHAssetCollectionChangeRequest creationRequestForAssetCollectionWithTitle: albumName];
completionHandler:^(BOOL success, NSError * _Nullable error)
if (error != nil)
NSLog(@"Error inserting image into album: %@", error.localizedDescription);
if (success)
//Fetch the newly created collection (which we *assume* exists here)
PHAssetCollection *newCollection = [self fetchAssetCollectionWithAlbumName:albumName];
[self insertImage:image intoAssetCollection: newCollection];
];
else
//If we found the existing AssetCollection with the title "albumName", insert into it
[self insertImage:image intoAssetCollection: collection];
- (PHAssetCollection *)fetchAssetCollectionWithAlbumName:(NSString *)albumName
PHFetchOptions *fetchOptions = [PHFetchOptions new];
//Provide the predicate to match the title of the album.
fetchOptions.predicate = [NSPredicate predicateWithFormat:[NSString stringWithFormat:@"title == '%@'", albumName]];
//Fetch the album using the fetch option
PHFetchResult *fetchResult = [PHAssetCollection fetchAssetCollectionsWithType:PHAssetCollectionTypeAlbum subtype:PHAssetCollectionSubtypeAlbumRegular options:fetchOptions];
//Assuming the album exists and no album shares it's name, it should be the only result fetched
return fetchResult.firstObject;
- (void)insertImage:(UIImage *)image intoAssetCollection:(PHAssetCollection *)collection
[[PHPhotoLibrary sharedPhotoLibrary] performChanges:^
//This will request a PHAsset be created for the UIImage
PHAssetCreationRequest *creationRequest = [PHAssetCreationRequest creationRequestForAssetFromImage:image];
//Create a change request to insert the new PHAsset in the collection
PHAssetCollectionChangeRequest *request = [PHAssetCollectionChangeRequest changeRequestForAssetCollection:collection];
//Add the PHAsset placeholder into the creation request.
//The placeholder is used because the actual PHAsset hasn't been created yet
if (request != nil && creationRequest.placeholderForCreatedAsset != nil)
[request addAssets: @[creationRequest.placeholderForCreatedAsset]];
completionHandler:^(BOOL success, NSError * _Nullable error)
if (error != nil)
NSLog(@"Error inserting image into asset collection: %@", error.localizedDescription);
];
【讨论】:
是否可以只添加到该相册而不添加到相机胶卷?【参考方案4】:改编埃迪对 Swift 4 的回答:
func saveImageToAlbum(_ image: UIImage, name: String)
if let collection = fetchAssetCollection(name)
self.saveImageToAssetCollection(image, collection: collection)
else
// Album does not exist, create it and attempt to save the image
PHPhotoLibrary.shared().performChanges(
PHAssetCollectionChangeRequest.creationRequestForAssetCollection(withTitle: name)
, completionHandler: (success: Bool, error: Error?) in
guard success == true && error == nil else
NSLog("Could not create the album")
if let err = error
NSLog("Error: \(err)")
return
if let newCollection = self.fetchAssetCollection(name)
self.saveImageToAssetCollection(image, collection: newCollection)
)
func fetchAssetCollection(_ name: String) -> PHAssetCollection?
let fetchOption = PHFetchOptions()
fetchOption.predicate = NSPredicate(format: "title == '" + name + "'")
let fetchResult = PHAssetCollection.fetchAssetCollections(
with: PHAssetCollectionType.album,
subtype: PHAssetCollectionSubtype.albumRegular,
options: fetchOption)
return fetchResult.firstObject
func saveImageToAssetCollection(_ image: UIImage, collection: PHAssetCollection)
PHPhotoLibrary.shared().performChanges(
let creationRequest = PHAssetCreationRequest.creationRequestForAsset(from: image)
if let request = PHAssetCollectionChangeRequest(for: collection),
let placeHolder = creationRequest.placeholderForCreatedAsset
request.addAssets([placeHolder] as NSFastEnumeration)
, completionHandler: (success: Bool, error: Error?) in
guard success == true && error == nil else
NSLog("Could not save the image")
if let err = error
NSLog("Error: " + err.localizedDescription)
return
)
【讨论】:
【参考方案5】:Objective C 的改进版本,使用块。如果相册不存在,它会创建一个相册,然后保存三种类型的媒体项目 - 照片、GIF 和视频:
// Types of media, that can be saved to an album
typedef NS_ENUM(NSUInteger, AlbumMediaType)
AlbumMediaTypePhoto,
AlbumMediaTypeGIF,
AlbumMediaTypeVideo
;
/**
Creates album if it doesn't exist and returns it in a block
*/
- (void)createCollectionOnComplete:(void (^ _Nonnull)(PHAssetCollection * _Nonnull collection))onComplete
NSString *albumTitle = @"YOUR_ALBUM_TITLE";
__block PHAssetCollection *collection;
__block PHObjectPlaceholder *placeholder;
// Searching for an existing album
PHFetchOptions *fetchOptions = [[PHFetchOptions alloc] init];
fetchOptions.predicate = [NSPredicate predicateWithFormat:@"title = %@", albumTitle];
collection = [PHAssetCollection fetchAssetCollectionsWithType:PHAssetCollectionTypeAlbum
subtype:PHAssetCollectionSubtypeAny
options:fetchOptions].firstObject;
// If album is not found, we create it
if (!collection)
[[PHPhotoLibrary sharedPhotoLibrary] performChanges:^
PHAssetCollectionChangeRequest *createAlbum = [PHAssetCollectionChangeRequest creationRequestForAssetCollectionWithTitle:albumTitle];
placeholder = [createAlbum placeholderForCreatedAssetCollection];
completionHandler:^(BOOL success, NSError *error)
if (success)
PHFetchResult *collectionFetchResult = [PHAssetCollection fetchAssetCollectionsWithLocalIdentifiers:@[placeholder.localIdentifier]
options:nil];
collection = collectionFetchResult.firstObject;
// After creating album, we return it
onComplete(collection);
];
else
// If album already exists, we instantly return it
onComplete(collection);
/**
Saves an item of a given mediatype, that is located in mediaURL
*/
- (void)saveToAlbumMediaItemFromURL:(NSURL *)mediaURL mediaType:(AlbumMediaType)mediaType
NSData *mediaData = [NSData dataWithContentsOfURL:mediaURL];
if (!mediaData)
OWSFail(@"%@ Could not load data: %@", self.logTag, [self.attachmentStream mediaURL]);
return;
[self createCollectionOnComplete:^(PHAssetCollection * _Nonnull collection)
[[PHPhotoLibrary sharedPhotoLibrary] performChanges:^
// We create a PHAsset using creationRequest
PHAssetCreationRequest *assetRequest;
switch (mediaType)
case AlbumMediaTypePhoto:
assetRequest = [PHAssetCreationRequest creationRequestForAssetFromImage:[UIImage imageWithData:mediaData]];
break;
case AlbumMediaTypeGIF:
assetRequest = [PHAssetCreationRequest creationRequestForAsset];
PHAssetResourceCreationOptions *options = [[PHAssetResourceCreationOptions alloc] init];
[assetRequest addResourceWithType:PHAssetResourceTypePhoto data:mediaData options:options];
break;
case AlbumMediaTypeVideo:
if ( !UIVideoAtPathIsCompatibleWithSavedPhotosAlbum(mediaURL.path) )
OWSFail(@"%@ Could not save incompatible video data.", self.logTag);
break;
NSString *videoPath = [[NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) objectAtIndex:0] stringByAppendingPathComponent:@"file.mov"];
[mediaData writeToFile:videoPath atomically:YES];
assetRequest = [PHAssetCreationRequest creationRequestForAssetFromVideoAtFileURL:[NSURL fileURLWithPath:videoPath]];
break;
default:
break;
// Creating a request to change an album
PHAssetCollectionChangeRequest *albumChangeRequest = [PHAssetCollectionChangeRequest changeRequestForAssetCollection:collection];
// PHAsset is not created yet, so we use a placeholder
PHObjectPlaceholder *placeholder = [assetRequest placeholderForCreatedAsset];
// We add a placeholder of a created item to the request of changing album
if (albumChangeRequest != nil && placeholder != nil)
[albumChangeRequest addAssets: @[placeholder]];
completionHandler:^(BOOL success, NSError *error)
if (success)
NSLog(@"Media item saved!");
else
NSLog(@"Error saving media item - %@", error ? error.localizedDescription : @"");
];
];
我们可以使用这些方法以这种方式保存媒体项目:
[self saveToAlbumMediaItemFromURL:[self.attachmentStream mediaURL] mediaType:AlbumMediaTypeGIF];
【讨论】:
以上是关于iOS 将照片保存在特定应用的相册中的主要内容,如果未能解决你的问题,请参考以下文章
如何在 iOS 中使用 UIActivityViewController 创建和保存特定相册