Is it possible to fetch all PHAssets that don't belong to any PHAssetColletion? Basically what I want to achieve is to get all photos that are not sorted to any album.
I know I can do this:
let photos = PHAsset.fetchAssets(with: PHFetchOptions())
var photosWithoutAlbum: [PHAsset] = []
photos.enumerateObjects({
asset, index, stop in
let albums = PHAssetCollection.fetchAssetCollectionsContaining(asset, with: .album, options: PHFetchOptions())
if albums.count == 0 {
photosWithoutAlbum.append(asset)
}
})
But maybe there is a better/faster way using PHFetchOptions or different fetch method?
Related
I can get all the photos, but I do not know how to find the albums in which these photos are located.
Another option is to go through all the albums and get photos from them.
I think this is not a good option.
The goal is to get the name of the albums for each photo.
private func getPhotosAll() -> [String:Photo] {
var photos = [String:Photo]()
let assets = PHAsset.fetchAssets(with: .image, options: nil)
assets.enumerateObjects({ (asset, index, stop) in
let id = asset.localIdentifier
guard (id.isEmpty == false) else {
return
}
guard let name = asset.originalFilename else {
return
}
let photo = Photo()
photo.name = name
// TODO: Read albums?
photo.albums = [String]()
photos[id] = photo
})
return photos
}
Update. I get only user albums. And how to get the rest? (Camera Roll, People, Places, Recently Deleted)
var albums = Set<String>()
let typesAlbum: [PHAssetCollectionType] = [.album, .smartAlbum, .moment]
for type in typesAlbum {
let collectionsBox = PHAssetCollection.fetchAssetCollectionsContaining(asset,
with: type, options: nil)
collectionsBox.enumerateObjects { (collection, _, _) in
guard let albumName = collection.localizedTitle else {
return
}
albums.insert(albumName)
}
}
For each PHAsset, call fetchAssetCollectionsContaining(_:with:options:).
I'm building Gallery app like a iOS standard Photos App. (Swift 4.1)
I want to fetch the thumbnails, titles, and total number of images that I can see when I launch the standard photo app.
The Photos framework seems more complex than I thought.
It is not easy to find a way to explain why, and what procedures should be approached.
Can you tell me about this?
The minimum number of steps to achieve what you are asking is:
// import the framework
import Photos
// get the albums list
let albumList = PHAssetCollection.fetchAssetCollections(with: .album, subtype: .albumRegular, options: nil)
// you can access the number of albums with
albumList.count
// individual objects with
let album = albumList.object(at: 0)
// eg. get the name of the album
album.localizedTitle
// get the assets in a collection
func getAssets(fromCollection collection: PHAssetCollection) -> PHFetchResult<PHAsset> {
let photosOptions = PHFetchOptions()
photosOptions.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: true)]
photosOptions.predicate = NSPredicate(format: "mediaType == %d", PHAssetMediaType.image.rawValue)
return PHAsset.fetchAssets(in: collection, options: photosOptions)
}
// eg.
albumList.enumerateObjects { (coll, _, _) in
let result = self.getAssets(fromCollection: coll)
print("\(coll.localizedTitle): \(result.count)")
}
// Now you can:
// access the count of assets in the PHFetchResult
result.count
// get an asset (eg. in a UITableView)
let asset = result.object(at: indexPath.row)
// get the "real" image
PHCachingImageManager.default().requestImage(for: asset, targetSize: CGSize(width: 200, height: 200), contentMode: .aspectFill, options: nil) { (image, _) in
// do something with the image
}
I also suggest to take a look at the Apple sample code for the Photos framework, is not hard to follow, together with the Photos framework documentation.
I am trying to get all the photos from cameraRoll using Photos framework but its taking a lot of time to fetch all the photos from cameraRoll.
Is their anyway to add pagination to it ?
so i can fetch while scrolling.
var images = [UIImage]()
var assets = [PHAsset]()
fileprivate func assetsFetchOptions() -> PHFetchOptions {
let fetchOptions = PHFetchOptions()
//fetchOptions.fetchLimit = 40 //uncomment to limit photo
let sortDescriptor = NSSortDescriptor(key: "creationDate", ascending: false)
fetchOptions.sortDescriptors = [sortDescriptor]
return fetchOptions
}
fileprivate func fetchPhotos() {
let allPhotos = PHAsset.fetchAssets(with: .image, options: assetsFetchOptions())
DispatchQueue.global(qos: .background).async {
allPhotos.enumerateObjects({ (asset, count, stop) in
//print(count)
let imageManager = PHImageManager.default()
let targetSize = CGSize(width: 200, height: 200)
let options = PHImageRequestOptions()
options.isSynchronous = true
imageManager.requestImage(for: asset, targetSize: targetSize, contentMode: .aspectFit, options: options, resultHandler: { (image, info) in
if let image = image {
self.images.append(image)
self.assets.append(asset)
}
if count == allPhotos.count - 1 {
DispatchQueue.main.async {
self.collectionView?.reloadData()
}
}
})
})
}
}
allPhotos is of type PHFetchResult< PHAsset > which is a lazy collection, ie it doesn't actually go out and get the photo until you ask it for one, which is what .enumerateObjects is doing. You can just grab the photos one at a time with the subscript operator or get a range of objects with objects(at:) to page through the collection as needed.
In iOS PhotoKit, I can fetch all non-empty albums like this:
let fetchOptions = PHFetchOptions()
fetchOptions.predicate = NSPredicate(format: "estimatedAssetCount > 0")
let albumFetchResult = PHAssetCollection.fetchAssetCollections(with: .album, subtype: .any, options: albumFetchOptions)
albumFetchResult.enumerateObjects({ (collection, _, _) in
// Do something with the album...
})
Then I can get only photos from the album like this:
let fetchOptions = PHFetchOptions()
fetchOptions.predicate = NSPredicate(format: "mediaType = %d", PHAssetResourceType.photo.rawValue)
let fetchResults = PHAsset.fetchAssets(in: collection, options: fetchOptions)
But the first part can give me albums with only videos, which means that after I apply the predicate to the second part, the album will be empty. Is there a way to filter out those albums in the first part, before I start using them?
It seems that collections cannot be filtered like this without also fetching the items in the collections. See the docs for available fetch options; none allow filtering by number of a specific type of media.
The way I would achieve this is by fetching all albums created by the user, and then fetching the assets from the albums with a predicate that returns only images.
So to put it in code:
var userCollections: PHFetchResult<PHAssetCollection>!
// Fetching all PHAssetCollections with at least some media in it
let options = PHFetchOptions()
options.predicate = NSPredicate(format: "estimatedAssetCount > 0")
// Performing the fetch
userCollections = PHAssetCollection.fetchAssetCollections(with: .album, subtype: .albumRegular, options: options)
Next, fetch the assets from a collection that are images by specifying a predicate:
// Getting the specific collection (I assumed to use a tableView)
let collection = userCollections[indexPath.row]
let optionsToFilterImage = PHFetchOptions()
optionsToFilterImage.predicate = NSPredicate(format: "mediaType = %d", PHAssetMediaType.Image.rawValue)
// Fetching the asset with the predicate to filter just images
let justImages = PHAsset.fetchAssets(in: collection, options: optionsToFilterImage)
Lastly, count the number of images:
if justImages.count > 0 {
// Display it
} else {
// The album has no images
}
I have a PHAssetCollection (an album), which I have in my app. On viewDidLoad(), my code checks to see if there is a picture already in the album, and if so, it adds the pre-existing picture to the PHAssetCollecton. However, I NEVER want to add pre-existing pictures in the PHAssetCollection. I always want my assetCollection to be fresh, without any picture(s) or data.
How do I set self.assetCollection to be a brand new AssetCollection with no picture(s) or data? Deleting that line of code causes my app to crash...
code in viewDidLoad()
//Check if the folder exists, if not, create it
let fetchOptions = PHFetchOptions()
fetchOptions.predicate = NSPredicate(format: "title = %#", albumName)
let collection:PHFetchResult = PHAssetCollection.fetchAssetCollectionsWithType(.Album, subtype: .Any, options: fetchOptions)
if let first_Obj:AnyObject = collection.firstObject{
//Album already exists, and has pictures.
self.albumFound = true
//Here is my issue. I want to set self.assetCollection to a brand new PHAssetCollection with NO data... brand new
self.assetCollection = first_Obj as! PHAssetCollection
There is also this code in viewDidAppear, I am unsure if this is of any use, but contains self.assetCollection so I added it:
self.photosAsset = PHAsset.fetchAssetsInAssetCollection(self.assetCollection, options: nil)