Сохранить изображения с помощью phimagemanager в пользовательский альбом?

Я делаю приложение, которое делает снимки с помощью AVFoundation, и я хочу сохранить их в пользовательский альбом, который затем могу запросить и показать в своем приложении. (Я бы предпочел, чтобы их не было в общей фотопленке, если только пользователь этого не захочет) Я не могу найти ничего, показывающего, как это сделать в Swift... или вообще. Есть ли другой способ, которым я должен это сделать?

Я нашел этот пример на SO, но для меня это не имеет смысла, и я не могу заставить его работать.

    func savePhoto() {
    var albumFound : Bool = false
    var assetCollection: PHAssetCollection!
    var photosAsset: PHFetchResult!
    var assetThumbnailSize:CGSize!

    // Create the album if does not exist (in viewDidLoad)
    if let first_Obj:AnyObject = collection.firstObject{
        //found the album
        self.albumFound = true
        self.assetCollection = collection.firstObject as PHAssetCollection
    }else{
        //Album placeholder for the asset collection, used to reference collection in completion handler
        var albumPlaceholder:PHObjectPlaceholder!
        //create the folder
        NSLog("\nFolder \"%@\" does not exist\nCreating now...", albumName)
        PHPhotoLibrary.sharedPhotoLibrary().performChanges({
            let request = PHAssetCollectionChangeRequest.creationRequestForAssetCollectionWithTitle(albumName)
            albumPlaceholder = request.placeholderForCreatedAssetCollection
            },
            completionHandler: {(success:Bool, error:NSError!)in
                NSLog("Creation of folder -> %@", (success ? "Success":"Error!"))
                self.albumFound = (success ? true:false)
                if(success){
                    let collection = PHAssetCollection.fetchAssetCollectionsWithLocalIdentifiers([albumPlaceholder.localIdentifier], options: nil)
                    self.assetCollection = collection?.firstObject as PHAssetCollection
                }
        })
    }



    let bundle = NSBundle.mainBundle()
    let myFilePath = bundle.pathForResource("highlight1", ofType: "mov")
    let videoURL:NSURL = NSURL.fileURLWithPath(myFilePath!)!

    let priority = DISPATCH_QUEUE_PRIORITY_DEFAULT
    dispatch_async(dispatch_get_global_queue(priority, 0), {
        PHPhotoLibrary.sharedPhotoLibrary().performChanges({
            //let createAssetRequest = PHAssetChangeRequest.creationRequestForAssetFromImage
            let createAssetRequest = PHAssetChangeRequest.creationRequestForAssetFromVideoAtFileURL(videoURL)
            let assetPlaceholder = createAssetRequest.placeholderForCreatedAsset
            let albumChangeRequest = PHAssetCollectionChangeRequest(forAssetCollection: self.assetCollection, assets: self.photosAsset)
            albumChangeRequest.addAssets([assetPlaceholder])
            }, completionHandler: {(success, error)in
                dispatch_async(dispatch_get_main_queue(), {
                    NSLog("Adding Image to Library -> %@", (success ? "Sucess":"Error!"))
                    //picker.dismissViewControllerAnimated(true, completion: nil)
                })
        })

    })
}

Любая помощь/объяснения были бы замечательными!


person Jonovono    schedule 19.11.2014    source источник
comment
у меня тоже такая же проблема.. какое-нибудь решение для этого??.. кто-нибудь??   -  person Anooj Krishnan G    schedule 24.12.2014
comment
привет @AnoojKrishnanG. Я заработал. Позвольте мне опубликовать свое решение позже сегодня или завтра - прямо сейчас у меня есть рождественские вещи.   -  person Jonovono    schedule 26.12.2014
comment
Привет, @AnoojKrishnanG. Только что опубликовал ответ. Дайте мне знать, если это работает для вас. Думаю, у меня там все есть.   -  person Jonovono    schedule 26.12.2014


Ответы (5)


Вот как я это делаю:

Наверху:

import Photos

var image: UIImage!
var assetCollection: PHAssetCollection!
var albumFound : Bool = false
var photosAsset: PHFetchResult!
var assetThumbnailSize:CGSize!
var collection: PHAssetCollection!
var assetCollectionPlaceholder: PHObjectPlaceholder!

Создание альбома:

func createAlbum() {
    //Get PHFetch Options
    let fetchOptions = PHFetchOptions()
    fetchOptions.predicate = NSPredicate(format: "title = %@", "camcam")
    let collection : PHFetchResult = PHAssetCollection.fetchAssetCollectionsWithType(.Album, subtype: .Any, options: fetchOptions)
    //Check return value - If found, then get the first album out
    if let _: AnyObject = collection.firstObject {
        self.albumFound = true
        assetCollection = collection.firstObject as! PHAssetCollection
    } else {
        //If not found - Then create a new album
        PHPhotoLibrary.sharedPhotoLibrary().performChanges({
            let createAlbumRequest : PHAssetCollectionChangeRequest = PHAssetCollectionChangeRequest.creationRequestForAssetCollectionWithTitle("camcam")
            self.assetCollectionPlaceholder = createAlbumRequest.placeholderForCreatedAssetCollection
            }, completionHandler: { success, error in
                self.albumFound = success

                if (success) {
                    let collectionFetchResult = PHAssetCollection.fetchAssetCollectionsWithLocalIdentifiers([self.assetCollectionPlaceholder.localIdentifier], options: nil)
                    print(collectionFetchResult)
                    self.assetCollection = collectionFetchResult.firstObject as! PHAssetCollection
                }
        })
    }
}

При сохранении фото:

func saveImage(){
        PHPhotoLibrary.sharedPhotoLibrary().performChanges({
            let assetRequest = PHAssetChangeRequest.creationRequestForAssetFromImage(self.image)
            let assetPlaceholder = assetRequest.placeholderForCreatedAsset
            let albumChangeRequest = PHAssetCollectionChangeRequest(forAssetCollection: self.assetCollection, assets: self.photosAsset)
            albumChangeRequest!.addAssets([assetPlaceholder!])
            }, completionHandler: { success, error in
                print("added image to album")
                print(error)

                self.showImages()
        })
    }

Показываю изображения из этого альбома:

func showImages() {
        //This will fetch all the assets in the collection

        let assets : PHFetchResult = PHAsset.fetchAssetsInAssetCollection(assetCollection, options: nil)
        print(assets)

        let imageManager = PHCachingImageManager()
        //Enumerating objects to get a chached image - This is to save loading time
        assets.enumerateObjectsUsingBlock{(object: AnyObject!,
            count: Int,
            stop: UnsafeMutablePointer<ObjCBool>) in

            if object is PHAsset {
                let asset = object as! PHAsset
                print(asset)

                let imageSize = CGSize(width: asset.pixelWidth, height: asset.pixelHeight)

                let options = PHImageRequestOptions()
                options.deliveryMode = .FastFormat

                imageManager.requestImageForAsset(asset, targetSize: imageSize, contentMode: .AspectFill, options: options, resultHandler: {(image: UIImage?,
                    info: [NSObject : AnyObject]?) in
                    print(info)
                    print(image)
                })
            }
        }
person Jonovono    schedule 26.12.2014
comment
Похоже, вам не хватает кода в разделе При сохранении фото - person djunod; 10.02.2015
comment
Спасибо за ваш ответ, но в вашем коде много опечаток, и многие из них отсутствуют. Как вы думаете, вы могли бы привести свои ответы в порядок? Это было бы очень полезно - person ndbroadbent; 14.03.2015
comment
Может ли кто-нибудь предоставить Objective-C, который является точным эквивалентом этого кода? - person Adam Freeman; 22.07.2015
comment
Это сработало для меня, но мне нужно реализовать еще одну вещь. Как я могу получить URL-адрес/путь изображения, чтобы сохранить его в своей модели данных. - person Ankita Shah; 01.10.2015
comment
как сохранить это изображение с произвольным именем файла ?? - person Jirson Tavera; 09.09.2016
comment
Не могли бы вы предоставить вариант Swift 3? Выдает ошибку: Контекстный тип NSFastEnumeration нельзя использовать с литералом массива. в строке: albumChangeRequest!.addAssets([assetPlaceholder!]) - person Priyal; 09.01.2017
comment
ошибка решена! Но я не могу добавить изображение в альбом после перезапуска приложения. Любая помощь ? У меня есть требование, аналогичное Instagram. Я хочу создать альбом с именем - мой альбом и добавить в этот альбом все те фотографии, которые пользователь загрузил через мое приложение. - person Priyal; 10.01.2017

Ответьте на Objective-C и немного подчистите.

__block PHFetchResult *photosAsset;
__block PHAssetCollection *collection;
__block PHObjectPlaceholder *placeholder;

// Find the album
PHFetchOptions *fetchOptions = [[PHFetchOptions alloc] init];
fetchOptions.predicate = [NSPredicate predicateWithFormat:@"title = %@", @"YOUR_ALBUM_TITLE"];
collection = [PHAssetCollection fetchAssetCollectionsWithType:PHAssetCollectionTypeAlbum
                                                      subtype:PHAssetCollectionSubtypeAny
                                                      options:fetchOptions].firstObject;
// Create the album
if (!collection)
{
    [[PHPhotoLibrary sharedPhotoLibrary] performChanges:^{
        PHAssetCollectionChangeRequest *createAlbum = [PHAssetCollectionChangeRequest creationRequestForAssetCollectionWithTitle:@"YOUR_ALBUM_TITLE"];
        placeholder = [createAlbum placeholderForCreatedAssetCollection];
    } completionHandler:^(BOOL success, NSError *error) {
        if (success)
        {
            PHFetchResult *collectionFetchResult = [PHAssetCollection fetchAssetCollectionsWithLocalIdentifiers:@[placeholder.localIdentifier]
                                                                                                            options:nil];
            collection = collectionFetchResult.firstObject;
        }
    }];
}

// Save to the album
[[PHPhotoLibrary sharedPhotoLibrary] performChanges:^{
    PHAssetChangeRequest *assetRequest = [PHAssetChangeRequest creationRequestForAssetFromImage:[UIImage imageWithData:imageData]];
    placeholder = [assetRequest placeholderForCreatedAsset];
    photosAsset = [PHAsset fetchAssetsInAssetCollection:collection options:nil];
    PHAssetCollectionChangeRequest *albumChangeRequest = [PHAssetCollectionChangeRequest changeRequestForAssetCollection:collection
                                                                                                                  assets:photosAsset];
    [albumChangeRequest addAssets:@[placeholder]];
} completionHandler:^(BOOL success, NSError *error) {
    if (success)
    {
        NSString *UUID = [placeholder.localIdentifier substringToIndex:36];
        self.photo.assetURL = [NSString stringWithFormat:@"assets-library://asset/asset.PNG?id=%@&ext=JPG", UUID];
        [self savePhoto];
    }
    else
    {
        NSLog(@"%@", error);
    }
}];

Бит в конце с UUID я нашел на другом Поток StackOverflow для создания замены свойства AssetURL из ALAsset.

Примечание. См. комментарий Криса ниже для более полного ответа.

person cschandler    schedule 23.07.2015
comment
@cschandler › что означает [self savePhoto]; делает? - person Rashad; 10.11.2015
comment
Есть ли проблема с этим подходом, имеющим два асинхронных запроса (т. е. вызовы performChanges), где один зависит от результата другого? Не приведет ли это к состоянию гонки? - person chris; 02.12.2015
comment
Вы, вероятно, правы в этом. Думаю, я бы использовал группу отправки, чтобы убедиться, что вызов завершен, или просто переместил бы раздел «Сохранить в альбом» внутри обработчика завершения для раздела «Создать альбом» для полной безопасности. - person cschandler; 29.12.2015

Мне нравится повторно использовать код, который я пишу, поэтому я решил создать расширение для PHPhotoLibrary, где его можно использовать следующим образом:

PHPhotoLibrary.saveImage(photo, albumName: "Trip") { asset in
    guard let asset = asset else {
        assert(false, "Asset is nil")
        return
    }
    PHPhotoLibrary.loadThumbnailFromAsset(asset) { thumbnail in
        print(thumbnail)
    }
}

Вот код: https://gist.github.com/ricardopereira/636ccd0a3c8a327c43d42e7cbca4d041

person ricardopereira    schedule 18.05.2016
comment
Большое спасибо! Я немного улучшил его, добавил видео, а также категорию, которая позволила мне его скомпилировать ( PHAsset+identifier.swift) github.com/kv2/PHPhotoLibrary-PhotoAsset.swift - person stackOverFlew; 04.08.2016
comment
Тип «PHAsset» не имеет члена «fetchAssetsWithLocalIdentifier». У него есть fetchAssetsWithLocalIndentifiers, но у него другой список параметров, который я не знаю, как редактировать. - person Xitcod13; 22.08.2016
comment
Ладно заработало. Необходимо заменить fetchAssetWithLocalIdentifier на PHAsset.fetchAssetsWithLocalIdentifiers([placeholder.localIdentifier], options: nil).firstObject as? PHAsset - person Xitcod13; 22.08.2016
comment
Как мне продолжить, чтобы получить путь к изображению для его хранения в БД ?? - person Xitcod13; 22.08.2016
comment
@ Xitcod13 Вам не нужен путь. Вам просто нужно сохранить ссылку localIdentifier (Уникальная строка, постоянно идентифицирующая объект.). - person ricardopereira; 22.08.2016
comment
Есть ли шанс, что вы могли бы написать это на Objective-C? - person user-44651; 08.06.2017

Как обновлено для Swift 2.1+ и для видео на случай, если вы пытаетесь это сделать и оказались здесь. Сравните с другими ответами на наличие небольших различий (например, использование для изображений, а не для видео)

var photosAsset: PHFetchResult!
var collection: PHAssetCollection!
var assetCollectionPlaceholder: PHObjectPlaceholder!

//Make sure we have custom album for this app if haven't already
let fetchOptions = PHFetchOptions()
fetchOptions.predicate = NSPredicate(format: "title = %@", "MY_APP_ALBUM_NAME")
self.collection = PHAssetCollection.fetchAssetCollectionsWithType(.Album, subtype: .Any, options: fetchOptions).firstObject as! PHAssetCollection

//if we don't have a special album for this app yet then make one
if self.collection == nil {
     PHPhotoLibrary.sharedPhotoLibrary().performChanges({
        let createAlbumRequest : PHAssetCollectionChangeRequest = PHAssetCollectionChangeRequest.creationRequestForAssetCollectionWithTitle("MY_APP_ALBUM_NAME")
        self.assetCollectionPlaceholder = createAlbumRequest.placeholderForCreatedAssetCollection
             }, completionHandler: { success, error in
                if success {
                    let collectionFetchResult = PHAssetCollection.fetchAssetCollectionsWithLocalIdentifiers([self.assetCollectionPlaceholder.localIdentifier], options: nil)
                    print(collectionFetchResult)
                    self.collection = collectionFetchResult.firstObject as! PHAssetCollection
                }
            })
 }

//save the video to Photos                     
PHPhotoLibrary.sharedPhotoLibrary().performChanges({
     let assetRequest = PHAssetChangeRequest.creationRequestForAssetFromVideoAtFileURL(self.VIDEO_URL_FOR_VIDEO_YOU_MADE!)
     let assetPlaceholder = assetRequest!.placeholderForCreatedAsset
     self.photosAsset = PHAsset.fetchAssetsInAssetCollection(self.collection, options: nil)
     let albumChangeRequest = PHAssetCollectionChangeRequest(forAssetCollection: self.collection, assets: self.photosAsset)
     albumChangeRequest!.addAssets([assetPlaceholder!])
         }, completionHandler: { success, error in
               if success {
                  print("added video to album")
               }else if error != nil{
                  print("handle error since couldn't save video")
               }
         }
 })
person ColossalChris    schedule 27.01.2016

Я улучшил код @ricardopereira и @ColossalChris. Добавлено видео в расширение и добавлено другое расширение поверх PHAsset, чтобы избавиться от ошибок компиляции. Работает в Свифт 2.1.

Пример использования:

#import "Yourtargetname-Swift.h"//important!
NSURL *videoURL = [[NSURL alloc] initFileURLWithPath:PATH_TO_VIDEO];

[PHPhotoLibrary saveVideo:videoURL albumName:@"my album" completion:^(PHAsset * asset) {
    NSLog(@"success");
    NSLog(@"asset%lu",(unsigned long)asset.pixelWidth);
}];

Импортируйте оба файла swift: https://github.com/kv2/PHPhotoLibrary-PhotoAsset.swift

Его можно использовать в Objective-C, если вы импортируете заголовок swift для своей цели (см. ViewController.m).

person stackOverFlew    schedule 03.08.2016