I'm trying to get all videos that are in the camera roll on a user's phone when they try and upload a video, but I'm not sure how to.
I've done this to get all pictures and noticed that if I change the .image to .video it gets all the videos, but they are still presented as an image and you can't play the video:
func fetchImagesFromDeviceLibary() {
let allPhotos = PHAsset.fetchAssets(with: .image, options: getAssetFetchOptions())
DispatchQueue.global(qos: .background).async {
//Enumerate objects
allPhotos.enumerateObjects({ (asset, count, stop) in
let imageManager = PHImageManager.default()
let targetSize = CGSize(width: 600, height: 600)
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.videos.append(image)
self.assets.append(asset)
if self.selectedVideo == nil {
self.selectedVideo = image
}
if count == allPhotos.count - 1 {
DispatchQueue.main.async {
self.collectionView.reloadData()
}
}
}
})
})
}
}
func getAssetFetchOptions() -> PHFetchOptions {
let options = PHFetchOptions()
options.fetchLimit = 50
let sortDescriptor = NSSortDescriptor(key: "creationDate", ascending: false)
options.sortDescriptors = [sortDescriptor]
return options
}
How would I get all the videos and display them on screen so that you can interact with them?
After changing fetchAssets with .image to .video make the required changes in the getAssetsFetchOption().
func getAssetFetchOptions() -> PHFetchOptions {
let fetchOptions = PHFetchOptions()
fetchOptions.sortDescriptors = [NSSortDescriptor(key: "creationDate",
ascending: false)]
// For Images Only
// fetchOptions.predicate = NSPredicate(format: mediaType == %d", PHAssetMediaType.image.rawValue)
// For Videos Only
// fetchOptions.predicate = NSPredicate(format: "mediaType == %d, PHAssetMediaType.video.rawValue)
// For Images and Videos
// fetchOptions.predicate = NSPredicate(format: "mediaType == %d || mediaType == %d", PHAssetMediaType.image.rawValue, PHAssetMediaType.video.rawValue)
// For Videos with some duration, here I’m taking it as 10 second
fetchOptions.predicate = NSPredicate(format: "mediaType = %d AND duration < 10", PHAssetMediaType.video.rawValue)
fetchOptions.fetchLimit = 50
let imagesAndVideos = PHAsset.fetchAssets(with: fetchOptions)
print(“LIST: \(imagesAndVideos)”)
return options
}
Hope this will work for you too.
Create AVPlayer Instance :
let videoURL = "your video url"
// Create an AVPlayer, passing it the local video url path
let player = AVPlayer(url: videoURL as URL)
let controller = AVPlayerViewController()
controller.player = player
present(controller, animated: true) {
player.play()
}
Do not forgot to import AVKit and AVFoundation.
Also try to make AvPlayer instance globally.
Related
I tried to fetch all images from album. It fetches all images with their URL's and image data, but it does not fetch images directly from given URL path, So I need to download images in Document Directory and then get path. So it's taking too much time. I use below code. I want fetch images like iPhone photos library fetches.
Please find error.
func fatchImagesfromAlbum() {
DispatchQueue.global(qos: .background).async {
self.photoAssets = self.fetchResult as! PHFetchResult<AnyObject>
let fetchOptions = PHFetchOptions()
fetchOptions.predicate = NSPredicate(format: "mediaType = %d", PHAssetMediaType.image.rawValue)
self.photoAssets = PHAsset.fetchAssets(in: self.assetCollection, options: fetchOptions) as! PHFetchResult<AnyObject>
for i in 0..<self.photoAssets.count{
autoreleasepool {
let asset = self.photoAssets.object(at: i)
let imageSize = CGSize(width: asset.pixelWidth,
height: asset.pixelHeight)
let options = PHImageRequestOptions()
options.deliveryMode = .fastFormat
options.isSynchronous = true
options.isNetworkAccessAllowed = true
self.imageManager.requestImage(for: asset as! PHAsset, targetSize: imageSize, contentMode: .aspectFill, options: options, resultHandler: { (image, info) -> Void in
if image != nil {
let image1 = image as! UIImage
let imageUrl = info!["PHImageFileURLKey"] as? NSURL
let imageName = imageUrl?.lastPathComponent
let urlString: String = imageUrl!.path!
let theFileName = (urlString as NSString).lastPathComponent
self.imageName.append("\(theFileName)")
self.imagePath.append("\(urlString)")
let documentDirectory = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true).first!
let photoURL = NSURL(fileURLWithPath: documentDirectory)
let localPath = photoURL.appendingPathComponent(imageName!)
DispatchQueue.global(qos: .background).async {
if !FileManager.default.fileExists(atPath: localPath!.path) {
do {
try UIImageJPEGRepresentation(image1, 0.1)?.write(to: localPath!)
print("file saved")
}catch {
print("error saving file")
}
}
else {
print("file already exists")
}
}
}
})
DispatchQueue.main.async
{
self.collectionView.reloadData()
}
}
}
self.hudHide()
}
PHPhotoLibrary.shared().register(self)
if fetchResult == nil {
let allPhotosOptions = PHFetchOptions()
allPhotosOptions.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: true)]
fetchResult = PHAsset.fetchAssets(with: allPhotosOptions)
}
}
I recommend simply using UIImagePickerController or if your app requires multiple image selection functionality, a third-party library like DKImagePickerController. As another user already mentioned in the comments, these will only copy the image(s) the user selected into your app's directory and save on processing time.
Following #matt's code when using the UIImagePicker, I can prevent the user from picking a .livePhoto once an image is choosen using:
let asset = info[UIImagePickerControllerPHAsset] as? PHAsset
if asset?.playbackStyle == .livePhoto {
// alert user this photo isn't a possibility
}
When using the PHFetchOptions how can I prevent them from being shown instead of filtering them out inside the enumerateObjects callback?
fileprivate func fetchPhotos() {
let fetchOptions = PHFetchOptions()
fetchOptions.fetchLimit = 1000
let sortDescriptor = NSSortDescriptor(key: "creationDate", ascending: false)
fetchOptions.sortDescriptors = [sortDescriptor]
let allPhotos = PHAsset.fetchAssets(with: .video, options: fetchOptions)
allPhotos.enumerateObjects {
[weak self] (asset, count, stop) in
if asset.playbackStyle == .livePhoto {
return
}
let imageManager = PHImageManager.default()
let targetSize = CGSize(width: 350, height: 350)
let options = PHImageRequestOptions()
options.isSynchronous = true
imageManager.requestImage(for: asset, targetSize: targetSize, contentMode: .aspectFit, options: options, resultHandler: {
[weak self] (image, info) in
if let image = image {
self?.tableData.append(image)
}
if count == allPhotos.count - 1 {
self?.collectionView.reloadData()
}
})
}
}
You can use the predicate property of PHFetchOptions. Setup the predicate to fail if the mediaSubtype attribute of the asset indicates it is a live photo.
fetchOptions.predicate = NSPredicate(format: "(mediaSubtype & %ld) == 0", PHAssetMediaSubtype.PhotoLive.rawValue)
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.
I used PHImageManager to load images from photo album.
Here is my code...
var photoLibrary = [UIImage]()
func grabPhotos(){
let imgManager = PHImageManager.default()
let requestOptions = PHImageRequestOptions()
requestOptions.isSynchronous = true
requestOptions.deliveryMode = .highQualityFormat
let fetchOptions = PHFetchOptions()
fetchOptions.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: false)]
if let fetchResult : PHFetchResult = PHAsset.fetchAssets(with: .image, options: fetchOptions) {
if fetchResult.count > 0 {
for i in 0..<fetchResult.count{
imgManager.requestImage(for: fetchResult.object(at: i) as PHAsset , targetSize: CGSize(width: 200, height: 200), contentMode: .aspectFill, options: requestOptions, resultHandler: {
image, error in
self.photoLibrary.append(image!)
})
}
}
else{
showAllertToImportImage()//A function to show alert
}
}
}
Now I want to load videos from photo album and I need to show them in UICollectionView.
For loading images in UICollectionView I used UIImageView in the cell. What should I use in the cell to load videos ?
I also need to show the video duration.
Please see answer of fetch video and duration of video
func grabPhotos(){
let imgManager = PHImageManager.default()
let requestOptions = PHImageRequestOptions()
requestOptions.isSynchronous = true
requestOptions.deliveryMode = .highQualityFormat
let fetchOptions = PHFetchOptions()
fetchOptions.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: false)]
if let fetchResult : PHFetchResult = PHAsset.fetchAssets(with: .video, options: fetchOptions) {
if fetchResult.count > 0 {
for i in 0..<fetchResult.count{
//Used for fetch Image//
imgManager.requestImage(for: fetchResult.object(at: i) as PHAsset , targetSize: CGSize(width: 200, height: 200), contentMode: .aspectFill, options: requestOptions, resultHandler: {
image, error in
let imageOfVideo = image! as UIImage
self.photoLibrary.append(imageOfVideo)
})
//Used for fetch Video//
imgManager.requestAVAsset(forVideo: fetchResult.object(at: i) as PHAsset, options: PHVideoRequestOptions(), resultHandler: {(avAsset, audioMix, info) -> Void in
if let asset = avAsset as? AVURLAsset {
//let videoData = NSData(contentsOf: asset.url)
let duration : CMTime = asset.duration
let durationInSecond = CMTimeGetSeconds(duration)
print(durationInSecond)
}
})
}
}
else{
//showAllertToImportImage()//A function to show alert
}
}
}
I'm trying to use smartAlbum to generate an array of either only videos or only photos or both.
You can see my code below:
PHFetchResult *collectionList = [PHCollectionList fetchMomentListsWithSubtype:PHCollectionListSubtypeMomentListCluster options:nil];
PHFetchOptions *options = nil;
if (self.xSelected) {
options = [[PHFetchOptions alloc] init];
options.sortDescriptors = #[[NSSortDescriptor sortDescriptorWithKey:#"creationDate" ascending:NO]];
options.predicate = [NSPredicate predicateWithFormat:#"mediaType = %d",PHAssetMediaTypeImage];
}
if (self.ySelected) {
options = [[PHFetchOptions alloc] init];
options.predicate = [NSPredicate predicateWithFormat:#"mediaType = %d",PHAssetMediaTypeVideo];
}
[collectionList enumerateObjectsUsingBlock:^(PHCollectionList *collection, NSUInteger idx, BOOL *stop) {
PHFetchResult *momentsInCollection = [PHAssetCollection fetchMomentsInMomentList:collection options:options];
for (id moment in momentsInCollection) {
PHAssetCollection *castedMoment = (PHAssetCollection *)moment;
[_smartAlbums insertObject:castedMoment atIndex:0];
}
}];
This however is constantly breaking on the first line inside the block and giving the following error:
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Unsupported predicate in fetch options: mediaType == 2'
I did a little research and found this link.
I'm wondering if this is an Apple bug or if its just something wrong with my code.
It seems like it worked for the people that referred to this answer, which is so weird coz its basically the same thing.
Thanks in advance,
Anish
EDIT:
I think I found the answer here in Apple's Documentation. Looks like mediaType is a key only for PHAsset and not PHAssetCollection. So now I guess the question is how to get PFAssetCollection with only videos or only images.
Use this method to get photos and videos assets array separately.
Get All Videos
func getAllVideos(completion:#escaping (_ videoAssets : [PHAsset]?) -> Void) {
var videoAssets : [PHAsset] = []
let fetchOptions = PHFetchOptions()
fetchOptions.sortDescriptors = [NSSortDescriptor(key: "creationDate",ascending: false)]
fetchOptions.predicate = NSPredicate(format: "mediaType = %d", PHAssetMediaType.video.rawValue)
let allVideo = PHAsset.fetchAssets(with: .video, options: fetchOptions)
allVideo.enumerateObjects { (asset, index, bool) in
videoAssets.append(asset)
}
completion(videoAssets)
}
Get All Photos
func getAllPhotos(completion:#escaping (_ photosAssets : [PHAsset]?) -> Void) {
var photosAssets : [PHAsset] = []
let fetchOptions = PHFetchOptions()
let scale = UIScreen.main.scale
let screenWidth = UIScreen.main.bounds.width * scale
let screenHeight = UIScreen.main.bounds.height * scale
fetchOptions.sortDescriptors = [NSSortDescriptor(key: "creationDate",ascending: false)]
fetchOptions.predicate = NSPredicate(format: "mediaType = %d || (mediaSubtype & %d) != 0 && (pixelHeight != %d AND pixelWidth != %d) OR (pixelHeight != %d AND pixelWidth != %d)", PHAssetMediaType.image.rawValue, PHAssetMediaSubtype.photoLive.rawValue ,screenHeight, screenWidth, screenWidth, screenHeight)
let allPhotos = PHAsset.fetchAssets(with: .image, options: fetchOptions)
allPhotos.enumerateObjects { (asset, index, bool) in
photosAssets.append(asset)
}
completion(photosAssets)
}
No need to put mediaType in predicate
In swift 4.0 this is how i used fetchAsset() method from Photos framework , to get all videos from photo library.
You can also get the video from specific folder using predicate.
func fetchAllVideos()
{
//let albumName = "blah"
let fetchOptions = PHFetchOptions()
// fetchOptions.predicate = NSPredicate(format: "title = %#", albumName)
//uncomment this if you want video from custom folder
fetchOptions.predicate = NSPredicate(format: "mediaType = %d ", PHAssetMediaType.video.rawValue )
let allVideo = PHAsset.fetchAssets(with: .video, options: fetchOptions)
allVideo.enumerateObjects { (asset, index, bool) in
// videoAssets.append(asset)
let imageManager = PHCachingImageManager()
imageManager.requestAVAsset(forVideo: asset, options: nil, resultHandler: { (asset, audioMix, info) in
if asset != nil {
let avasset = asset as! AVURLAsset
let urlVideo = avasset.url
print(urlVideo)
}
})
}
}
Hope this help!!!