I am trying to get Video without open Gallery or UIImagePickerControllerlike I got success in getting images without open gallery. Is there any way to get files without open UIImagePickerController.
Can someone please explain to me how to get files without open UIImagePickerController. Any help would be greatly appreciated.
Thanks in advance.
For example, using the following code, you can get the latest user video:
import Photos
let options = PHFetchOptions()
options.fetchLimit = 1
let sortDescriptor = NSSortDescriptor(key: "creationDate", ascending: false)
options.sortDescriptors = [sortDescriptor]
let fetchResult = PHAsset.fetchAssets(with: .video, options: options)
if fetchResult.count == 0 {
// user has no video...
return
}
let asset = fetchResult[0]
let requestOptions = PHVideoRequestOptions()
let manager = PHImageManager.default()
Then, if you want to get the video itself, use the following code:
manager.requestAVAsset(forVideo: asset, options: requestOptions, resultHandler: { oAsset, oAudioMix, oDict in
if let urlAsset = oAsset as? AVURLAsset {
let url = urlAsset.url
// use URL to get file content
}
})
Otherwise, if you just want to play the video, use the following code:
manager.requestPlayerItem(forVideo: asset, options: requestOptions, resultHandler: { oPlayerItem, oDict in
// do something with oPlayerItem
})
For more information you can read this
Related
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'm trying to enable users to share the most recent video from their Photos gallery to Facebook. I've looked at http://wiki.akosma.com/IPhone_URL_Schemes#Facebook to see the URL schemes, but I can't figure out the proper URL to create in my app. I have done this successfully for Instagram, here is what it looks like:
let fetchOptions = PHFetchOptions()
fetchOptions.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: false)]
let fetchResult = PHAsset.fetchAssetsWithMediaType(.Video, options: fetchOptions)
if let lastAsset = fetchResult.firstObject as? PHAsset {
let localIdentifier = lastAsset.localIdentifier
let u = "instagram://library?LocalIdentifier=" + localIdentifier
let url = NSURL(string: u)!
if UIApplication.sharedApplication().canOpenURL(url) {
UIApplication.sharedApplication().openURL(NSURL(string: u)!)
}
}
What is the proper way to do this successfully?
I am using PHCachingImageManager().requestAVAssetForVideo to fetch videos from iPhone. I am not getting all the videos from my iPhone. Videos stored in Photos are being fetched not all.
How can I get all the videos stored in iPhone storage.
Here is the code.
let options = PHFetchOptions()
options.sortDescriptors = [NSSortDescriptor(key: "modificationDate",
ascending: true)]
let assetResults = PHAsset.fetchAssetsWithMediaType(.Video,
options: options)
for i in 0 ..< assetResults.count{
let object: AnyObject = assetResults[i]
if let asset = object as? PHAsset{
let options = PHVideoRequestOptions()
options.deliveryMode = .Automatic
options.networkAccessAllowed = true
options.version = .Current
options.progressHandler = {(progress: Double,
error: NSError?,
stop: UnsafeMutablePointer<ObjCBool>,
info: [NSObject : AnyObject]?) in
}
/* Now get the video */
PHCachingImageManager().requestAVAssetForVideo(asset,options: options,resultHandler: {(asset: AVAsset?,audioMix: AVAudioMix?,info: [NSObject : AnyObject]?) -> Void in dispatch_async(dispatch_get_main_queue(), {
/* Did we get the URL to the video? */
if let asset = asset as? AVURLAsset{
})
})
}
}
You need to have a look inside PHFetchOptions Maybe some of your video assets are hidden?
var fetchOptions = PHFetchOptions()
fetchOptions.includeHiddenAssets = true
From the Docs-
// Whether hidden assets are included in fetch results. Defaults to NO
public var includeHiddenAssets: Bool
Hope it helps.
I would like to fetch all photos that are saved in device and save them to my app and then eventually (if user allow this) delete originals.
This is my whole class I created for this task:
class ImageAssetsManager: NSObject {
let imageManager = PHCachingImageManager()
func fetchAllImages() {
let options = PHFetchOptions()
options.predicate = NSPredicate(format: "mediaType = %d", PHAssetMediaType.Image.rawValue)
options.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: true)]
if #available(iOS 9.0, *) {
options.fetchLimit = 5
} else {
// Fallback on earlier versions
}
let imageAssets = PHAsset.fetchAssetsWithOptions(options)
print(imageAssets.count)
self.getAssets(imageAssets)
}
func getAssets(assets: PHFetchResult) {
var assetsToDelete: [PHAsset] = []
assets.enumerateObjectsUsingBlock { (object, count, stop) in
if object is PHAsset {
let asset = object as! PHAsset
let imageSize = CGSize(width: asset.pixelWidth,height: asset.pixelHeight)
let options = PHImageRequestOptions()
options.deliveryMode = .FastFormat
options.synchronous = true
self.imageManager.requestImageForAsset(asset, targetSize: imageSize, contentMode: .AspectFill, options: options, resultHandler: { [weak self]
image, info in
self.addAssetToSync(image, info: info)
assetsToDelete.append(asset)
})
}
}
self.deleteAssets(assetsToDelete)
}
func addAssetToSync(image: UIImage?, info: [NSObject : AnyObject]?) {
guard let image = image else {
return
}
guard let info = info else {
return
}
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), {
let imageData = UIImageJPEGRepresentation(image, 0.95)!
let fileUrl = info["PHImageFileURLKey"] as! NSURL
dispatch_async(dispatch_get_main_queue(), {
let photoRootItem = DatabaseManager.sharedInstance.getPhotosRootItem()
let ssid = DatabaseManager.sharedInstance.getSsidInfoByName(ContentManager.sharedInstance.ssid)
let item = StorageManager.sharedInstance.createFile(imageData, name: fileUrl.absoluteString.fileNameWithoutPath(), parentFolder: photoRootItem!, ssid: ssid!)
})
})
}
func deleteAssets(assetsToDelete: [PHAsset]){
PHPhotoLibrary.sharedPhotoLibrary().performChanges({
PHAssetChangeRequest.deleteAssets(assetsToDelete)
}, completionHandler: { success, error in
guard let error = error else {return}
})
}
}
It's working but my problem is that it's working just for a limited number of photos. When I try it with all I get memory warnings and then app crashed. I know why it is. I know that my problem is that I get all photos to memory and it's too much. I could fetch images with that fetch limit and make it to loop but I am not sure if it is best solution.
I was hoping that with some solution process few photos then release memory and again and again until end. But this change would be somewhere in enumerateObjectsUsingBlock. I am not sure if it helps but I don't even need to get image. I just need to copy image file from device path to my app sandbox path.
What's best solution for this? How to avoid memory warnings and leaks? Thanks
Change your dispatch_async calls to dispatch_sync. Then you will process photos one at a time as you walk through enumerateObjectsUsingBlock, instead of trying to process them all at the same time.
I have an app that uses a UIImagePickerController to retrieve pictures both from camera and from the photos library.
In the image picker delegate I only want to save the NSURL (UIImagePickerControllerReferenceURL) of the picked image to save memory. When the user needs to see the image later on, I load it with PHCachingImageManager directly from the photos library.
Now - this whole thing works great with pictures the user chooses from the library, but not with pictures directly taken by camera (since there is no URL). I am currently trying to save the picture with PHAsset, but I have no idea how to get the NSURL of the save picture.
This is what I've been up to:
func imagePickerController(picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : AnyObject])
{
picker.dismissViewControllerAnimated(true, completion: nil)
let pickedImage = info[UIImagePickerControllerOriginalImage] as! UIImage
if picker.sourceType == .Camera
{
// When taking a picture with the camera, store it in the user roll
PHPhotoLibrary.sharedPhotoLibrary().performChanges(
{ () -> Void in
// save the image
PHAssetCreationRequest.creationRequestForAssetFromImage(pickedImage)
// TODO how to get the asset url
}, completionHandler:
{ (finished, error) -> Void in
if (finished)
{
}
}
)
}
else
{
let pickedImageUrl: NSURL? = info[UIImagePickerControllerReferenceURL] as? NSURL
currentImageUrl = pickedImageUrl
currentImage = pickedImage
toggleImageInfoView(true)
toggleMapState(true)
}
}
Any ideas how to get the url of the saved picture?
Best,
Georg
UPDATE: Seems like I found an answer to this Problem.
Step 1: I save the image to the camera
UIImageWriteToSavedPhotosAlbum(image.image, self, #selector(cameraImageSavedAsynchronously), nil)
this is done asynchronously, so make sure to set a selector when operation has finished.
Step 2: When operation has completed, I do the following:
func fetchLastImage(completion: (localIdentifier: String?) -> Void)
{
let fetchOptions = PHFetchOptions()
fetchOptions.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: false)]
fetchOptions.fetchLimit = 1
let fetchResult = PHAsset.fetchAssetsWithMediaType(.Image, options: fetchOptions)
if (fetchResult.firstObject != nil)
{
let lastImageAsset: PHAsset = fetchResult.firstObject as! PHAsset
completion(localIdentifier: lastImageAsset.localIdentifier)
}
else
{
completion(localIdentifier: nil)
}
}
I fetch the last image in camera roll with PHAsset and save the local identifier of the image. This is not an URL, but a unique identifier which does not change. This way, you can access the saved image perfectly.
Hope this helps others!
I agree with you.
but, if the Image's Exif has the date of the earlier .
let fetchResult = PHAsset.fetchAssetsWithMediaType(.Image, options: fetchOptions)
fetchResult.firstObject
fetchResult.firstObject is not the one you just saved.
maybe you can modify fetchOptions.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: false) to key: "modificationDate"
BTW, I found an other way:
__block PHObjectPlaceholder *placeholderAsset = nil;
[[PHPhotoLibrary sharedPhotoLibrary] performChanges:^{
PHAssetChangeRequest *newAssetRequest = [PHAssetChangeRequest creationRequestForAssetFromVideoAtFileURL:url];
newAssetRequest.location = location;
newAssetRequest.creationDate = [NSDate date];
placeholderAsset = newAssetRequest.placeholderForCreatedAsset;
} completionHandler:^(BOOL success, NSError *error) {
if(success){
PHAsset *asset = [self getAssetFromlocalIdentifier:placeholderAsset.localIdentifier];
completionBlock(asset, YES);
} else {
completionBlock(nil, NO);
}
}];
can get the newly PHAsset.
Ive updated the answer to include returning any asset type, as well as simpler/cleaner way of returning the asset.
Theres no need to a competition handler.
func fetchLastAsset() -> PHAsset? {
let fetchOptions = PHFetchOptions()
fetchOptions.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: false)]
fetchOptions.fetchLimit = 1
let fetchResult = PHAsset.fetchAssets(with: fetchOptions)
return fetchResult.firstObject
}