How to get a location data from photos from iPhones? - ios

I am having a trouble with extracting a location data from photos from iPhones by using PHPhotoLibrary. Can anyone teach me how?
Is it similar to how to do the same thing by using ALAssetsLibrary? Please.

You can use PHAsset location property to retrieve location data of image .
see PHAsset
Here is code:
let CameraImages = PHAsset.fetchAssetsWithMediaType(PHAssetMediaType.Image, options: nil)
CameraImages.enumerateObjectsUsingBlock({
(obj, idx, bool) -> Void in
var asset = obj as! PHAsset
let locationData = asset.location
})
EDIT:
#Ryo comment: retrieve location data from the image that i choose from imagePickerController?
You can do as
func imagePickerController(picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : AnyObject])
{
self.dismissViewControllerAnimated(false, completion: nil)
var arrayURL:[NSURL] = []
var url = info[UIImagePickerControllerReferenceURL] as! NSURL
arrayURL.append(url)
let ImageAsset = PHAsset.fetchAssetsWithALAssetURLs(url1, options: nil)
ImageAsset.enumerateObjectsUsingBlock({
(obj, idx, bool) -> Void in
var asset = obj as! PHAsset
let locationData = asset.location
print(locationData)
})
}
edit 2 :
# Ryo comment : how can i get a longitude and altitude from this location data?
just add this line
if locationData != nil {
let longi = locationData?.coordinate.longitude
let lati = locationData?.coordinate.latitude
}

Related

How to wait the upload function return true(the object in server exist.) to do next? Swift

I have an upload image function in "didFinishPickingMediaWithInfo".
when I pick photo done, start uploading image.
But when I in editing View, I click the "choose" button, the view doesn't dismiss that look like locked, press anything doesn't work.
I guess my check sever function whether have the image or not, make it locked.Sorry I'm beginner and already search the stack overflow,I dont know what answer can solve this problem.
let albumPicker = UIImagePickerController()
albumPicker.sourceType = UIImagePickerControllerSourceType.savedPhotosAlbum
albumPicker.delegate = self
albumPicker.allowsEditing = true
self.present(albumPicker, animated: true, completion: nil)
internal func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
let uuid = NSUUID().uuidString
let imageName:String = uuid + ".jpg"
let documentsPath = NSHomeDirectory().appending("/Documents/")
let imagePath = documentsPath.appending(imageName)
let imageUrl = URL(fileURLWithPath: imagePath)
photoImage = info[UIImagePickerControllerOriginalImage] as? UIImage
if picker.sourceType == .camera {
photoImage = info[UIImagePickerControllerOriginalImage] as? UIImage
UIImageWriteToSavedPhotosAlbum(photoImage!, nil, nil, nil)
}
showLoadingView()
hideLoadingView {
//uploading
let objectKey:String = imageName
oss.uploadObjectAsync(imageUrl, objectKey: objectKey)
oss.checkObjectExist(objectKey: objectKey)
//this is check server object
//First,I want to check the object exist ,and send message
let message = "http:104.168.1.111/\(imageName)"
self.sendMessage(message: message)
self.tableView.scrollToBottom()
self.dismiss(animated: true, completion: nil) //sometimes donesnt work.
}
}
var client:OSSClient?
func checkObjectExist(objectKey:String) {
let put:OSSPutObjectRequest = OSSPutObjectRequest()
put.bucketName = ossBucketName
put.objectKey = objectKey
do {
try client?.doesObjectExist(inBucket: put.bucketName, objectKey: put.objectKey)
} catch {
//debug(object: error)
print("*****ERROR HERE: \(error)")
}
}
"doesObjectExist" document says
open func doesObjectExist(inBucket bucketName: String, objectKey: String) throws
return YES Object exist
return NO && *error = nil Object don't exist
return NO && *error != nil error
Thanks.

How to get image name from UIImagePickerController taken with Camera

This below code is working perfectly fine for images picked from gallery. But will not work if taken with Camera. I tried to save image into storage and read again, but I was unable to do that. So could any one help me in this? Thank you.
func imagePickerController(picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : AnyObject]) {
if let referenceUrl = info[UIImagePickerControllerReferenceURL] as? NSURL, image = info[UIImagePickerControllerOriginalImage] as? UIImage {
let phAsset = PHAsset.fetchAssetsWithALAssetURLs([referenceUrl], options: nil).lastObject as! PHAsset
PHImageManager.defaultManager().requestImageDataForAsset(phAsset, options: PHImageRequestOptions(), resultHandler: { (imagedata, dataUTI, orientation, info) in
if info!.keys.contains(NSString(string: "PHImageFileURLKey")) {
let path = info![NSString(string: "PHImageFileURLKey")] as! NSURL
print("path q\(path)")
self.mImageUrl = path
self.mlocalPath = path.path
self.mImageExtension = path.pathExtension
self.mImageName = path.lastPathComponent!
print("mImageName q\(self.mImageName)")
}
})
}
dismissViewControllerAnimated(true, completion: nil)
}
Swift 5+
As the previous answers sugested, the image is not stored in gallery yet and hence no imageName. You need to store it in gallery. Use the below Helper class to save and get images from FileManager.
Thanks to this Answer
class CameraImageManager {
static let shared = CameraImageManager()
public func saveImage(imageName: String, image: UIImage) {
guard let documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first else { return }
let fileName = imageName
let fileURL = documentsDirectory.appendingPathComponent(fileName)
guard let data = image.jpegData(compressionQuality: 1) else { return }
//Checks if file exists, removes it if so.
if FileManager.default.fileExists(atPath: fileURL.path) {
do {
try FileManager.default.removeItem(atPath: fileURL.path)
print("Removed old image")
} catch let removeError {
print("couldn't remove file at path", removeError)
}
}
do {
try data.write(to: fileURL)
} catch let error {
print("error saving file with error", error)
}
}
public func getImagePathFromDiskWith(fileName: String) -> URL? {
let documentDirectory = FileManager.SearchPathDirectory.documentDirectory
let userDomainMask = FileManager.SearchPathDomainMask.userDomainMask
let paths = NSSearchPathForDirectoriesInDomains(documentDirectory, userDomainMask, true)
if let dirPath = paths.first {
let imageUrl = URL(fileURLWithPath: dirPath).appendingPathComponent(fileName)
return imageUrl
}
return nil
}
public func loadImageFromDiskWith(fileName: String) -> UIImage? {
let documentDirectory = FileManager.SearchPathDirectory.documentDirectory
let userDomainMask = FileManager.SearchPathDomainMask.userDomainMask
let paths = NSSearchPathForDirectoriesInDomains(documentDirectory, userDomainMask, true)
if let dirPath = paths.first {
let imageUrl = URL(fileURLWithPath: dirPath).appendingPathComponent(fileName)
let image = UIImage(contentsOfFile: imageUrl.path)
return image
}
return nil
}
}
Now, in your imagePickerController didFinishPickingMediaWithInfo callback function, this is how you can assign a name to an image and save it.
public func imagePickerController(_ picker: UIImagePickerController,
didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey: Any]) {
guard let image = info[.editedImage] as? UIImage else { return }
let imageName = "RDV_" + UUID().uuidString
CameraImageManager.shared.saveImage(imageName: imageName, image: image)
print("IMAGE NAME IS: ", imageName)
}
Hope It Helps.
You can use a notification with addObserver like this
ViewController A : where you want image to be changed, add this in viewDidLoad
NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(self.methodOfImageChange(_:)), name:"ImageChanged", object: nil)
Add this method in ViewController A
func methodOfImageChange(notification: NSNotification){
let appStatus = notification.userInfo as? Dictionary<String,AnyObject>
// appStatus contains your image in "image" key
}
Now in didFinishPickingMediaWithInfo add this
let dictionary: [String:AnyObject] = [
"image" : (info[UIImagePickerControllerOriginalImage] as? UIImage)!,
]
NSNotificationCenter.defaultCenter().postNotificationName("ImageChanged", object: self, userInfo: dictionary)
picker .dismissViewControllerAnimated(true, completion: nil)
Hope this helps
The image isn't in the gallery yet, so I don't believe you have a name.
In my app the flow (via navigation controller) is:
Selection VC (choice of Camera or Photo Library) ->
UIImagePickerController ->
Edit VC (with back navigation and action button for - among others - saving to Photo Library)
If the user chooses Camera, they take a picture and the options are "Retake" or "Use Photo". Is they choose "Use Photo", they are in the Edit VC.
If they then choose to go back to the Select VC, the image is nowhere to be found.

Get Image location from UIImagePickerController

I have tried multiple ways to get the location of an image which took through the UIImagePickerController Camera.
What I want to achieve is that, I want to select an image using UIImagePickerController Camera and I have to save it into Photo Library so that only I can take back the PHAsset from it and also the location associated with it.
//MARK: Saving an Image to PhotoLibrary and taking back the PHAsset
class func savingThis(image : UIImage, completion : (asset : PHAsset?) -> ())
{
var localIdentifier : String?
let imageManager = PHPhotoLibrary.sharedPhotoLibrary()
imageManager.performChanges({ () -> Void in
let request = PHAssetChangeRequest.creationRequestForAssetFromImage(image)
if let properAsset = request.placeholderForCreatedAsset {
localIdentifier = properAsset.localIdentifier
}
else {
completion(asset: nil)
}
}, completionHandler: { (success, error) -> Void in
if let properLocalIdentifier = localIdentifier {
let result = PHAsset.fetchAssetsWithLocalIdentifiers([properLocalIdentifier], options: nil)
if result.count > 0 {
completion(asset: result[0] as? PHAsset)
}
else {
completion(asset: nil)
}
}
else {
completion(asset: nil)
}
})
}
I have tried this code, to save and get back the PHAsset. But the problem is that this PHAsset does not have any location associated with it, wondering why? And what I missed?
I believe that I don't have to manually set GPS data into image's metadata right? I think that Photos Framework or Asset Library takes care of it. So as you know that Asset Library is deprecated our only option is to use Photos Framework. I read online that, Saving image to Photo Library takes care of it. Isn't it correct?
Is there any alternative? Should I use UIImageWriteToSavedPhotosAlbum method to save image to Camera Roll, And I can take back the very recent photo using Photos Framework. But I don't think that UIImageWriteToSavedPhotosAlbum will take care of the location thing.
Do you have any thoughts?
First of all thanking all who were all kind to take a look into the question.
I found my answer.
//MARK: Saving an Image to PhotoLibrary and taking back the PHAsset
class func savingThis(image : UIImage, completion : (asset : PHAsset?) -> ())
{
var localIdentifier : String?
let imageManager = PHPhotoLibrary.sharedPhotoLibrary()
imageManager.performChanges({ () -> Void in
let request = PHAssetChangeRequest.creationRequestForAssetFromImage(image)
request.location = // Assigned current location here :)
if let properAsset = request.placeholderForCreatedAsset {
localIdentifier = properAsset.localIdentifier
}
else {
completion(asset: nil)
}
}, completionHandler: { (success, error) -> Void in
if let properLocalIdentifier = localIdentifier {
let result = PHAsset.fetchAssetsWithLocalIdentifiers([properLocalIdentifier], options: nil)
if result.count > 0 {
completion(asset: let asset = result[0] as? PHAsset)
}
else {
completion(asset: nil)
}
}
else {
completion(asset: nil)
}
})
}
When you get a callback from UIImagePickerController:
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo
info: [UIImagePickerController.InfoKey : Any]) {}
you can try to get the image location from the info dictionary.
For that, you need to request the related PHAsset object:
if let assetImage = info[UIImagePickerController.InfoKey.phAsset] as? PHAsset {
print("Image location info = \( assetImage.location))")
}
Important - before using this approach you need to request the user permission:
PHPhotoLibrary.requestAuthorization()
If you do not do it the info dictionary for UIImagePickerController.InfoKey.phAsset will return nil.

Save video on the gallery and store the path to the video

I have an app in which the user can record a video, this video is saved in the photo gallery, and I store the path to the video so that in the future the user could see again the video inside the app. The problem is that the method that I use I think it's giving me some kind of temporary path, and after some days, the video still in the gallery, but the path is not valid anymore and make the app crash. This is the code I'm using:
func imagePickerController(picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [NSObject : AnyObject]) {
let mediaType: String = info["UIImagePickerControllerMediaType"] as! String
if mediaType == "public.movie" {
let tempImageURL = info[UIImagePickerControllerMediaURL] as! NSURL!
let pathString = tempImageURL.relativeString
self.dismissViewControllerAnimated(true, completion: {})
if picker.sourceType == UIImagePickerControllerSourceType.PhotoLibrary {
self.videoPath = pathString
// Save the path in the DB
} else {
VideoManager.saveVideo(tempImageURL, onComplete: { (path) -> Void in
self.videoPath = path
// Save the path in the DB
})
var fileManager: NSFileManager = NSFileManager()
fileManager.removeItemAtPath(pathString!, error: nil)
}
}
}
And the VideoManager.saveVideo method code is the following:
func saveVideo(videoURL: NSURL, onComplete:((path: String) -> Void)) {
var assetsLibrary: ALAssetsLibrary = ALAssetsLibrary()
assetsLibrary.writeVideoAtPathToSavedPhotosAlbum(videoURL, completionBlock: { (assetURL: NSURL!, error: NSError!) -> Void in
var path: String = error == nil ? "\(assetURL)" : kEmptyString
onComplete(path: path)
})
}
I don't know what I'm doing wrong, I've tried with the method UISaveVideoAtPathToSavedPhotosAlbum but without success.. Any ideas?
For giving a little more information, when the video is selected from the gallery, the url I get is like the following one:
file:///private/var/mobile/Containers/Data/Application/D2E8E31B-CEA0-43B0-8EF9-1820F6BDE4A9/tmp/trim.AD855155-AB78-4A16-9AA8-DF2B3F39824E.MOV
And when I record a new video using the camera, first I have this URL:
file:///private/var/mobile/Containers/Data/Application/D2E8E31B-CEA0-43B0-8EF9-1820F6BDE4A9/tmp/capture/capturedvideo.MOV
and when I do writeVideoAtPathToSavedPhotosAlbum it returns an URL like:
assets-library://asset/asset.MOV?id=958507B5-1353-4DDC-BC07-D9CBC6126657&ext=MOV
Both of them work, but some days later stop working.
OK finally I found the solution, the thing was that you can't access directly the photo gallery with the stored url, you got to use the assetLibrary.assetForURL method, which I was missing. In the end the imagepickercontroller delegate method is like this:
func imagePickerController(picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [NSObject : AnyObject]) {
let mediaType: String = info["UIImagePickerControllerMediaType"] as! String
if mediaType == "public.movie" {
self.dismissViewControllerAnimated(true, completion: {})
if picker.sourceType == UIImagePickerControllerSourceType.PhotoLibrary {
let tempImageURL = info[UIImagePickerControllerReferenceURL] as! NSURL!
self.videoPath = tempImageURL.absoluteString
} else {
let tempImageURL = info[UIImagePickerControllerMediaURL] as! NSURL!
VideoManager.saveVideo(tempImageURL, onComplete: { (path) -> Void in
self.videoPath = path
})
}
}
}
I also was missing that when you record a video, you got to obtain the url using:
let tempImageURL = info[UIImagePickerControllerMediaURL] as! NSURL!
But when you get the video you have to do:
let tempImageURL = info[UIImagePickerControllerReferenceURL] as! NSURL!
Hope it helps!!

Extract GPS data from photo

I have a hard time because I want to extract the GPS coordinates from a photo. I use the function imagePickerController:didFinishPickingMediaWithInfo to pick an image and the I am inserting that image in a collectionView using the new Photos framework.
I want to extract the GPS coordinates from the photo. I have done some research and I am aware of the fact that UIImage does not contain all the metadata, so I tried using the AssetsLibrary framework.
Inside didFinishPickingMediaWithInfo I am using the following code to extract the photo location:
var referenceURL : NSURL = info.objectForKey(UIImagePickerControllerReferenceURL) as NSURL
var library : ALAssetsLibrary = ALAssetsLibrary()
library.assetForURL(referenceURL, resultBlock: { (asset : ALAsset!) -> Void in
var rep : ALAssetRepresentation = asset.defaultRepresentation()
var metadata : NSDictionary = rep.metadata()
let location: AnyObject! = asset.valueForProperty(ALAssetPropertyLocation)
if location != nil {
println(location)
}
else
{
println("Location not found")
}
})
{
(error : NSError!) -> Void in
}
However, it doesn't find the location, even though I checked the image and it contains EXIF metadata (it contains also GPS locations, in which I am interested in). How can I retrieve the coordinates from photo?
ALAssetsLibrary is deprecated in iOS 10. Fortunately, with Photos framework, this is trivial to implement. When imagePickerController(_ picker:, didFinishPickingMediaWithInfo) is called, you can retrieve location information through a simple lookup. Take a look at the code below:
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
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]
// The location is "asset.location", as a CLLocation
// ... Other stuff like dismiss omitted
}
Hope this helps. This is Swift 3, of course..
I found a solution using the following code:
if picker.sourceType == UIImagePickerControllerSourceType.PhotoLibrary
{
if let currentLat = pickedLat as CLLocationDegrees?
{
self.latitude = pickedLat!
self.longitude = pickedLong!
}
else
{
var library = ALAssetsLibrary()
library.enumerateGroupsWithTypes(ALAssetsGroupAll, usingBlock: { (group, stop) -> Void in
if (group != nil) {
println("Group is not nil")
println(group.valueForProperty(ALAssetsGroupPropertyName))
group.enumerateAssetsUsingBlock { (asset, index, stop) in
if asset != nil
{
if let location: CLLocation = asset.valueForProperty(ALAssetPropertyLocation) as CLLocation!
{ let lat = location.coordinate.latitude
let long = location.coordinate.longitude
self.latitude = lat
self.longitude = lat
println(lat)
println(long)
}
}
}
} else
{
println("The group is empty!")
}
})
{ (error) -> Void in
println("problem loading albums: \(error)")
}
}
}
What it does is that it reads the entire album and prints the location if the photo has that property, else it prints "location not found". It does so for every photo in the album.So I have another question... I want to display the location info just for the photo that I have selected, not for the entire album. Does anyone have a clue how this can be accomplished?
func imagePickerController(picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [NSObject : AnyObject]) {
if picker.sourceType == UIImagePickerControllerSourceType.PhotoLibrary {
let image = info[UIImagePickerControllerOriginalImage] as! UIImage
pickedImage.image = image
let url = info[UIImagePickerControllerReferenceURL] as! NSURL
let library = ALAssetsLibrary()
library.assetForURL(url, resultBlock: { (asset) in
if let location = asset.valueForProperty(ALAssetPropertyLocation) as? CLLocation {
self.latitude = location.coordinate.latitude
self.longitude = location.coordinate.longitude
}
}, failureBlock: { (error: NSError!) in
print("Error!")
})
}
self.dismissViewControllerAnimated(true, completion: nil)
}
Finally managed to get this after trying a lot of different ways, it's remarkably poorly referenced in the apple documentation (or I just couldn't find it). Unfortunately any images that weren't taken through the stock "camera" app don't tend to have location metadata. But it works fine when they do.
Got the answer from https://stackoverflow.com/a/27556241/4337311
With iOS 11 everything becomes much easier to recover GPS coordinate from a picture in your photo roll.
First import the Photo SDK
import Photos
Then before pushing the UIImagePickerController, ask the authorisation to access the data :
if PHPhotoLibrary.authorizationStatus() == .notDetermined {
PHPhotoLibrary.requestAuthorization { [weak self](_) in
// Present the UIImagePickerController
self?.present(picker, animated: true, completion: nil)
}
}
Then when you grab back the image from the UIImagePickerController, you only need to do the following to grab the coordinates :
let coordinate = (info[UIImagePickerControllerPHAsset] as? PHAsset)?.location?.coordinate
iOS 12 & 13
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
// For Location
if let asset = info[UIImagePickerController.InfoKey.phAsset] as? PHAsset {
print("============> \(asset.location?.coordinate.longitude ?? 0) \(asset.location?.coordinate.latitude ?? 0)")
}
}

Resources