How should I get ALAssetURL by UIImagePickerController delegate? - swift

When I use an ImagePicker by sourceType.camera, I want to get AssetUrl in order to get a PHAsset object. I called Info[UIImagePickerControllerReferenceURL], but it's always nil. Is there any way to resolve this? or some other ways replaced? I use Xcode 8.3 and Swift 3.1
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
//
if info[UIImagePickerControllerReferenceURL] != nil{
let imageAssetUrl: URL = (info[UIImagePickerControllerReferenceURL] as? URL)!
print("\(imageAssetUrl)")
let allPhotosOptions = PHFetchOptions()
// sort by create
allPhotosOptions.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: false)]
//OriginalImage
allPhotosOptions.predicate = NSPredicate(format: "mediaType = %d", PHAssetMediaType.image.rawValue)
let fetchResult = PHAsset.fetchAssets(withALAssetURLs: [imageAssetUrl], options: allPhotosOptions)
if fetchResult.count > 0 {
let asset = fetchResult.firstObject
}
}
picker.dismiss(animated: true) {
//
}
}

Related

Save video in Photo Library Swift

I'm trying to figure out how to save a video in my Photo Library. To get started I tried to simply pick a video by using the UIImagePickerController and after picking it saving it again in the library using the UISaveVideoAtPathToSavedPhotosAlbum. Doesn't make much sense but I try to understand how saving videos work.
The following code does not work, as it does not save the video:
#IBAction func ChooseVideo(_ sender: Any) {
let imagePickerController = UIImagePickerController()
imagePickerController.delegate = self
imagePickerController.mediaTypes = ["public.movie"]
self.present(imagePickerController, animated: true, completion: nil)
}
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
let videoURL = info[UIImagePickerController.InfoKey.mediaURL] as? URL
print(UIVideoAtPathIsCompatibleWithSavedPhotosAlbum(videoURL!.path))
dismiss(animated: true, completion: {
UISaveVideoAtPathToSavedPhotosAlbum(videoURL!.path, self, #selector(self.video(_:didFinishSavingWithError:contextInfo:)), nil)
})
}
I hope you can help me as I couldn't find much about saving videos.
Best regards,
MB
Solution:
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any])
{
// *** store the video URL returned by UIImagePickerController *** //
let videoURL = info[UIImagePickerController.InfoKey.mediaURL] as! URL
// *** load video data from URL *** //
let videoData = NSData(contentsOf: videoURL)
// *** Get documents directory path *** //
let paths = NSSearchPathForDirectoriesInDomains(FileManager.SearchPathDirectory.documentDirectory, FileManager.SearchPathDomainMask.userDomainMask, true)[0]
// *** Append video file name *** //
print(paths)
let dataPath = paths.appending("/videoFileName.mp4")
// *** Write video file data to path *** //
videoData?.write(toFile: dataPath, atomically: false)
PHPhotoLibrary.shared().performChanges({
PHAssetChangeRequest.creationRequestForAssetFromVideo(atFileURL: URL(fileURLWithPath: dataPath))
}) { saved, error in
if saved {
let fetchOptions = PHFetchOptions()
fetchOptions.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: false)]
let fetchResult = PHAsset.fetchAssets(with: .video, options: fetchOptions).firstObject
// fetchResult is your latest video PHAsset
// To fetch latest image replace .video with .image
}
}
}
Use following func to save video to documents directory
func imagePickerController(picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [NSObject : AnyObject])
{
// *** store the video URL returned by UIImagePickerController *** //
let videoURL = info[UIImagePickerControllerMediaURL] as! NSURL
// *** load video data from URL *** //
let videoData = NSData(contentsOfURL: videoURL)
// *** Get documents directory path *** //
let paths = NSSearchPathForDirectoriesInDomains(NSSearchPathDirectory.DocumentDirectory, NSSearchPathDomainMask.UserDomainMask, true)[0]
// *** Append video file name *** //
let dataPath = documentsDirectory.stringByAppendingPathComponent("/videoFileName.mp4")
// *** Write video file data to path *** //
videoData?.writeToFile(dataPath, atomically: false)
}
now save this video in photo gellary
PHPhotoLibrary.shared().performChanges({
PHAssetChangeRequest.creationRequestForAssetFromVideo(atFileURL: Your document directory file)
}) { saved, error in
if saved {
let fetchOptions = PHFetchOptions()
fetchOptions.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: false)]
let fetchResult = PHAsset.fetchAssets(with: .video, options: fetchOptions).firstObject
// fetchResult is your latest video PHAsset
// To fetch latest image replace .video with .image
}
}
after it if you don't need then delete the image from document directory
, I hope it will work for you ...:)

Getting location details from image in ios swift

I am trying to get location details from image using
UIImagePickerControllerReferenceURL but I found that PHAsset.fetchAssets(withALAssetURLs: [URL], options: opts) has been deprecated .Please help me in getting location details.
Can we do it using PHAssetCollection?. If so please help me
public func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any])
{
print(info)
let chosenImage = info[UIImagePickerControllerOriginalImage] as! UIImage
selectedImage.contentMode = .scaleAspectFit
selectedImage.image = chosenImage
dismiss(animated:true, completion: nil)
if let URL = info[UIImagePickerControllerReferenceURL] as? URL {
let opts = PHFetchOptions()
opts.fetchLimit = 1
let assets = PHAsset.fetchAssets(withALAssetURLs: [URL], options: opts)
let asset = assets[0]
print(asset.location)
// The location is "asset.location", as a CLLocation
// ... Other stuff like dismiss omitted
}
}
Only solution I found so far is to use the iOS 10 code block even in iOS 11 and just ignore the UIImagePickerControllerReferenceURL deprecated message (the key still exists and works in iOS 11)
import AssetsLibrary
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
if let imageUrl = info[UIImagePickerControllerReferenceURL] as? NSURL{
print(imageUrl.absoluteString) //"assets-library://asset/asset.JPG?id=ED7AC36B-A150-4C38-BB8C-B6D696F4F2ED&ext=JPG"
// access image from URL
let assetLibrary = ALAssetsLibrary()
assetLibrary.asset(for: imageUrl as URL! , resultBlock: { (asset: ALAsset!) -> Void in
if let actualAsset = asset as ALAsset? {
let assetRep: ALAssetRepresentation = actualAsset.defaultRepresentation()
let iref = assetRep.fullResolutionImage().takeUnretainedValue()
let image = UIImage.init(cgImage: iref)
self.img.image = image
}
}, failureBlock: { (error) -> Void in
})
}
dismiss(animated: true, completion: nil)
}
Hope this will help.

didFinishPickingMediaWithInfo function goes on infinite loop

It looks like the didFinishPickingMediaWithInfo function is going on an infinite loop and it eventually crashes with an error that says in the console:
warning: could not execute support code to read Objective-C class data in >the process. This may reduce the quality of type information available.
Right when I record a video and press the choose button, it crashes because it calls the didFinishPickingMediaWithInfo. Here is the relevant code:
let imagePicker: UIImagePickerController! = UIImagePickerController()
let saveFileName = "/test.mp4"
if (UIImagePickerController.isSourceTypeAvailable(.camera)) {
if UIImagePickerController.availableCaptureModes(for: .rear) != nil {
//if the camera is available, and if the rear camera is available, the let the image picker do this
imagePicker.sourceType = .camera
imagePicker.mediaTypes = [kUTTypeMovie as String]
imagePicker.allowsEditing = false
imagePicker.delegate = self as? UIImagePickerControllerDelegate & UINavigationControllerDelegate
imagePicker.videoMaximumDuration = 60
imagePicker.videoQuality = .typeIFrame1280x720
present(imagePicker, animated: true, completion: nil)
} else {
postAlert("Rear camera doesn't exist", message: "Application cannot access the camera.")
}
} else {
postAlert("Camera inaccessable", message: "Application cannot access the camera.")
}
}
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
print(123)
imagePickerController(imagePicker, didFinishPickingMediaWithInfo: [saveFileName : kUTTypeMovie])
let videoURL = info[UIImagePickerControllerReferenceURL] as? NSURL
print("\(String(describing: videoURL))" )
guard let path = videoURL?.path else { return }
let videoName = path.lastPathComponent
let paths = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)
let documentDirectory = paths.first as String!
let localPath = documentDirectory! + "/" + videoName
guard let imageData = NSData(contentsOfFile: localPath) else { return }
let image = UIImage(data: imageData as Data)
picker.dismiss(animated: true, completion: nil)
}
override func viewDidLoad() {
super.viewDidLoad()
self.imagePicker.delegate = self
}
Thank you in advance!
You are calling the function from inside of itself, here:
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
print(123)
imagePickerController(imagePicker, didFinishPickingMediaWithInfo: [saveFileName : kUTTypeMovie])
That is causing your infinite loop.

How to save video to app's directory and playback in view controller?

I'm trying to save a video in my app directory and then play it back in my view controller. I'm having an issue with saving and making the path. Can anyone help?
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any])
{
// Save the video to the app directory
let videoURL = info[UIImagePickerControllerMediaURL] as! NSURL
let videoData = NSData(contentsOf: videoURL as URL)
let paths = NSSearchPathForDirectoriesInDomains(FileManager.SearchPathDirectory.documentDirectory, FileManager.SearchPathDomainMask.userDomainMask, true)
let documentsDirectory: AnyObject = paths[0] as AnyObject
let dataPath = documentsDirectory.appendingPathComponent("/vid1.mp4")
videoData?.write(toFile: dataPath, atomically: false)
self.dismiss(animated: true, completion: nil)
}
#IBAction func playVideoAction(_ sender: Any)
{
let paths = NSSearchPathForDirectoriesInDomains(FileManager.SearchPathDirectory.documentDirectory, FileManager.SearchPathDomainMask.userDomainMask, true)
let documentsDirectory: AnyObject = paths[0] as AnyObject
let dataPath = documentsDirectory.appendingPathComponent("/vid1.mp4")
let videoAsset = (AVAsset(url: NSURL(fileURLWithPath: dataPath) as URL))
let playerItem = AVPlayerItem(asset: videoAsset)
let player = AVPlayer(playerItem: playerItem)
let playerViewController = AVPlayerViewController()
playerViewController.player = player
self.present(playerViewController, animated: true)
{
playerViewController.player!.play()
}
}
how to save video file into document directory
I've used code from this link but with the update to xcode 8/swift 3 it is not as helpful. Been stuck on this problem for awhile now.
You want to use PHImageManager.requestExportSession(forVideo:options:). This will prepare the asset asynchronously (including downloading it if needed), and create an AVExportSession you can use to save the file. By specifying you want the original, and to passthrough the content (if possible) you should get the best quality video available.
var dataPath: URL {
let paths = NSSearchPathForDirectoriesInDomains(FileManager.SearchPathDirectory.documentDirectory, FileManager.SearchPathDomainMask.userDomainMask, true)
let documentsDirectory = URL(fileURLWithPath: paths[0])
return documentsDirectory.appendingPathComponent("/vid1.mp4")
}
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
let url = info[UIImagePickerControllerReferenceURL] as! URL
let assets = PHAsset.fetchAssets(withALAssetURLs: [url], options: nil)
let asset = assets.object(at: 0)
let options = PHVideoRequestOptions()
options.version = .original
PHImageManager.default().requestExportSession(forVideo: asset, options: options, exportPreset: AVAssetExportPresetPassthrough) { (exportSession, info) in
guard let session = exportSession else { return }
session.outputURL = self.dataPath
session.outputFileType = AVFileTypeMPEG4
session.exportAsynchronously {
DispatchQueue.main.async {
self.dismiss(animated: true, completion: nil)
}
}
}
}
If you didn't want to save to disk but just playback, you can use requestPlayerItem to get an AVPlayerItem you can use in your player:
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
let url = info[UIImagePickerControllerReferenceURL] as! URL
let assets = PHAsset.fetchAssets(withALAssetURLs: [url], options: nil)
let asset = assets.object(at: 0)
let options = PHVideoRequestOptions()
options.version = .original
PHImageManager.default().requestPlayerItem(forVideo: asset, options: options) { (playerItem, info) in
let player = AVPlayer(playerItem: playerItem)
let playerViewController = AVPlayerViewController()
playerViewController.player = player
DispatchQueue.main.async {
self.dismiss(animated: true, completion: nil)
self.present(playerViewController, animated: true) {
playerViewController.player!.play()
}
}
}
}

Save image in Realm

I'm trying to pick image from device's Photo Library in method:
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any])
{
userPhoto.image = info[UIImagePickerControllerOriginalImage] as! UIImage?
userPhoto.contentMode = .scaleAspectFill
userPhoto.clipsToBounds = true
dismiss(animated: true, completion: nil)
}
and save this picture in Realm (as NSData):
asset.assetImage = UIImagePNGRepresentation(userPhoto.image!)! as NSData?
...
try! myRealm.write
{
user.assetsList.append(asset)
myRealm.add(user)
}
After build succeeded and trying to pick and save image (in the app) i have app error:
'Binary too big'
What i'm doing wrong?
P.S. Sorry for my English :)
After some actions i have this code. But it's overwrite my image.
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any])
{
let imageUrl = info[UIImagePickerControllerReferenceURL] as! NSURL
let imageName = imageUrl.lastPathComponent
let documentDirectory = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true).first!
let photoURL = NSURL(fileURLWithPath: documentDirectory)
let localPath = photoURL.appendingPathComponent(imageName!)
let image = info[UIImagePickerControllerOriginalImage]as! UIImage
let data = UIImagePNGRepresentation(image)
do
{
try data?.write(to: localPath!, options: Data.WritingOptions.atomic)
}
catch
{
// Catch exception here and act accordingly
}
userPhoto.image = image
userPhoto.contentMode = .scaleAspectFill
userPhoto.clipsToBounds = true
urlCatch = (localPath?.path)!
self.dismiss(animated: true, completion: nil);
}
Don't save the image itself into realm, just save the location of the image into realm as String or NSString and load the image from that saved path. Performance wise it's always better to load images from that physical location and your database doesn't get too big
func loadImageFromPath(_ path: NSString) -> UIImage? {
let image = UIImage(contentsOfFile: path as String)
if image == nil {
return UIImage()
} else{
return image
}
}
or you just save the image name, if it's in your documents directory anyhow
func loadImageFromName(_ imgName: String) -> UIImage? {
guard imgName.characters.count > 0 else {
print("ERROR: No image name")
return UIImage()
}
let imgPath = Utils.getDocumentsDirectory().appendingPathComponent(imgName)
let image = ImageUtils.loadImageFromPath(imgPath as NSString)
return image
}
and here a rough example how to save a captured image to your directory with a unique name:
#IBAction func capture(_ sender: AnyObject) {
let videoConnection = stillImageOutput?.connection(withMediaType: AVMediaTypeVideo)
stillImageOutput?.captureStillImageAsynchronously(from: videoConnection, completionHandler: { (imageDataSampleBuffer, error) -> Void in
let imageData = AVCaptureStillImageOutput.jpegStillImageNSDataRepresentation(imageDataSampleBuffer)
//self.stillImage = UIImage(data: imageData!)
//self.savedImage.image = self.stillImage
let timestampFilename = String(Int(Date().timeIntervalSince1970)) + "someName.png"
let filenamePath = URL(fileReferenceLiteralResourceName: getDocumentsDirectory().appendingPathComponent(timestampFilename))
let imgData = try! imageData?.write(to: filenamePath, options: [])
})
/* helper get Document Directory */
class func getDocumentsDirectory() -> NSString {
let paths = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)
let documentsDirectory = paths[0]
//print("Path: \(documentsDirectory)")
return documentsDirectory as NSString
}
https://realm.io/docs/objc/latest/#current-limitations
maximum data size is 16 MB . this is limitation of realm
Depending on how your serializing the image data (for example if it's a lossless bitmap), it's quite possible that this data exceed 16MB, which as you've stated is Realm's maximum supported size for binary properties.
When you invoke NSData.length, how large does it say your data is?