Hi guys I want to fetch all images from document and append them in cell.
I created this function to save images on didSelectItemAt
func saveImage(image: UIImage, imageName: String) {
let path = "yourProjectImages"
let document = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first?.appendingPathComponent(path, isDirectory: true)
let imageUrl = document!.appendingPathComponent("\(imageName).jpg", isDirectory: true)
do {
try UIImagePNGRepresentation(image)?.write(to: imageUrl, options: .atomic)
} catch {
print(error.localizedDescription)
}
}
I used it as I said in didSelectItemAt
override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let selectedImage = images[indexPath.item]
saveImage(image: selectedImage, imageName: "\(selectedImage)")
}
it seems to add picture immediately in document after clicking on the picture
But how to fetch them all in HomeFeed which is my rootController?
These showed photos is album I used Photos framework to fetch multiple photos from album.
Related
I want to perform an operation on a video in the iphone camera roll, and I require an absolute URI as I will be using ffmpeg natively within my app.
Is it possible to operate on the video in place? or would I need to copy the video to a tmp dir, operate on it, and then write back to the camera roll?
I've read some docs and tutorials and answering below based on that research.
Is it possible to operate on the video in place?
Yes (By copying it to temp dir) and No (to the original location where the video is actually stored)
Take a look at the following image and quote from official docs
Using PhotoKit, you can fetch and cache assets for display and
playback, edit image and video content or manage collections of
assets such as albums, Moments, and Shared Albums.
We don't have direct access to the location where the image/video is stored instead we get raw data or representation using PHAsset and Asset objects are immutable so we can't perform operations directly on it. We would need PHAssetChangeRequest to create, delete, change the metadata for, or edit the content of a Photos asset.
would I need to copy the video to a temp dir, operate on it, and then write back to the camera roll?
Yep, that's the way to go.
If you already fetched the assets, and have the PHFetchResult object try:
var video = PHAsset() // the video to be edited
…
if video.canPerform(.content) { // check if the selected PHAsset can be edited
video.requestContentEditingInput(with: nil, completionHandler: { editingInput, _ in
let videoAsset = editingInput?.audiovisualAsset // get tracks and metadata of the video and start editing
let videoURL = (videoAsset as? AVURLAsset)?.url // This might be nil so better use videoAsset
/*
Start editing your video here
*/
guard let input = editingInput else { return }
let output = PHContentEditingOutput(contentEditingInput: input)
let outputURL = output.renderedContentURL // URL at which you write/export the edited video, it must be a .mov file
let editedVideo = NSData() // suppose your video fileName is editedVideo.mov, I used NSData since I don't know what final edited object will be.
editedVideo.write(to: outputURL, atomically: false)
PHPhotoLibrary.shared().performChanges({
let changeRequest = PHAssetChangeRequest(for: video)
changeRequest.contentEditingOutput = output
})
})
}
Or if you're using default imagePicker, we can get tmp video url using:
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
let videoURL = info[UIImagePickerController.InfoKey.mediaURL] as! NSURL
print(videoURL) // file is already in tmp folder
let video = info[UIImagePickerController.InfoKey.phAsset] as! PHAsset
// implement the above code using this PHAsset
// your will still need to request photo library changes, and save the edited video and/or delete the older one
}
I implement something like this in my project I hope it will help you.
I show all the items in collection view and perform action on selection , you can also get the url of selected video
func getVideoFromCameraRoll() {
let options = PHFetchOptions()
options.sortDescriptors = [ NSSortDescriptor(key: "creationDate", ascending: false) ]
options.predicate = NSPredicate(format: "mediaType = %d", PHAssetMediaType.video.rawValue)
videos = PHAsset.fetchAssets(with: options)
videoLibraryCV.reloadData()
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return videos.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath)
let asset = videos!.object(at: indexPath.row)
let width: CGFloat = 150
let height: CGFloat = 150
let size = CGSize(width:width, height:height)
cell.layer.borderWidth = 0.5
cell.layer.borderColor = UIColor.lightGray.cgColor
PHImageManager.default().requestImage(for: asset, targetSize: size, contentMode: PHImageContentMode.aspectFit, options: nil)
{ (image, userInfo) -> Void in
let imageView = cell.viewWithTag(1) as! UIImageView
imageView.image = image
let labelView = cell.viewWithTag(2) as! UILabel
labelView.text = String(format: "%02d:%02d",Int((asset.duration / 60)),Int(asset.duration) % 60)
}
return cell
}
func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {
let asset = photos!.object(at: indexPath.row)
guard(asset.mediaType == PHAssetMediaType.Video)
else {
print("Not a valid video media type")
return
}
PHCachingImageManager().requestAVAssetForVideo(asset, options: nil, resultHandler: {
(asset: AVAsset ? , audioMix : AVAudioMix ? , info : [NSObject: AnyObject] ? ) in
let asset = asset as!AVURLAsset
print(asset.URL) // Here is video URL
})
}
I hope it will work for you ...:)
My app saved photo to the local documents folder and I used the UICollectionView to display all the image from that folder. But whenever I try to open the CollectionView it often took several seconds to open. I'm thinking that maybe the image files are too big, each photo is around 10MB. I also tried using thumbnails to display in collectionview but it still too slow. Any idea how to speed that up?
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reuseIdentifier, for: indexPath) as! SPCell
// Configure the cell
cell.imageView.image = loadImage(fileName: self.fileURLs[indexPath.row])
return cell
}
func loadImagesFromDocuments(){
let fileManager = FileManager.default
let documentsURL = NSHomeDirectory() + "/Documents/Secure/"
do {
fileURLs = try fileManager.contentsOfDirectory(at: URL(string: documentsURL)!, includingPropertiesForKeys: nil)
} catch {
print("Error while enumerating files : \(error.localizedDescription)")
}
}
func loadImage(fileName: URL) -> UIImage? {
do {
let imageData = try Data(contentsOf: fileName)
return UIImage(data: imageData)
} catch {
print("Error loading image : \(error)")
}
return nil
}
Current problem is that you load the image every time the cell appears so Instead of
var fileURLs = [URL]()
Make it
var fileImages = [UIImage]()
Then inside viewDidLoad
fileImages = fileURLs.compactMap { self.loadImage(fileName: $0) }
you are synchronously loading images while returning every cell at indexPath.
cell.imageView.image = loadImage(fileName: self.fileURLs[indexPath.row])
Instead, you can create a custom UICollectionViewCell implementation with a variable in global scope say imageURL.
after cell initialization, do this:
cell.imageURL = self.fileURLs[indexPath.row]
and, in ViewDidLoad() of your custom class, add this line:
self.imageView.image = loadImage(fileName: self.imageURL)
doing so, will lead to images being loaded in each custom implementation of cell unblocking the thread which is invoking the DataSource of your CollectionView.
I'm relatively new to Swift.
I'm currently trying to grab videos stored in the photo library and display them in a collection view. After selecting a video in the collection view, I want to be able to play the video.
Right now I've written part of the function grabVideos and I have 2 questions:
How should I store these videos? Can they be stored as UIImages? A lot of the other sources I found grabbed videos from online sources and they just stored the video url
What should I do in the resultHandler? I would assume thats were I store my videos into a global array
Note: code below is in a function called getVideos()
let imgManager = PHImageManager.default()
let requestOption = PHVideoRequestOptions()
requestOption.isSynchronous = true
requestOption.deliveryMode = .highQualityFormat
let fetchOption = PHFetchOptions()
fetchOption.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: false)]
if let fetchResult:PHFetchResult = PHAsset.fetchAssets(with: .video, options: fetchOption) {
if fetchResult.count > 0 {
for i in 0...fetchResult.count {
imgManager.requestAVAsset(forVideo: fetchResult.object(at: i) as! PHAsset, options: requestOption, resultHandler: {{ (<#AVAsset?#>, <#AVAudioMix?#>, <#[AnyHashable : Any]?#>) in
<#code#>
}})
}
} else {
print("Error: No Videos Found")
}
}
First you add a variable to your ViewController var fetchResults: PHFetchResult<PHAsset>?
Then you execute the fetch in viewDidLoad for instance
let fetchOption = PHFetchOptions()
fetchOption.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: false)]
let fetchResults = PHAsset.fetchAssets(with: .video, options: fetchOption);
self.fetchResults = fetchResults
if fetchResults.count == 0 {
print("Error: No Videos Found")
return
}
In your collectionViewCell you have to add a UIImageView so that we can show thumbnail in each cell, then you just do the following in collection view data source methods
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return fetchResults?.count ?? 0
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "reuse", for: indexPath) as! TestCollectionViewCell
let videoAsset = fetchResults!.object(at: indexPath.item)
PHImageManager.default().requestImage(for: videoAsset, targetSize: cell.bounds.size, contentMode: .aspectFill, options: nil) { (image: UIImage?, info: [AnyHashable : Any]?) in
cell.imageView.image = image
}
return cell
}
This can be approved upon, but will be OK for the first try, specifically look at using PHCachingImageManager instead of PHImageManager, more on this in the links below:
PHCachingImageManager
How to use PHCachingImageManager
How to play a video from PHAsset you can find answered:
Swift - Playing Videos from iOS PHAsset
i have this code for download PDF file :
var documents = [PDFDocument]()
DispatchQueue.global(qos: .default).async(execute: {
//All stuff here
print("Download PDF");
let url=NSURL(string: urlString);
let urlData=NSData(contentsOf: url! as URL);
if((urlData) != nil)
{
let documentsPath = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0]
let fileName = urlString as NSString;
let filePath="\(documentsPath)/\(fileName.lastPathComponent)";
let fileExists = FileManager().fileExists(atPath: filePath)
if(fileExists){
// File is already downloaded
print("PDF Already Downloaded");
}
else{
//download
DispatchQueue.main.async(execute: { () -> Void in
print(filePath)
urlData?.write(toFile: filePath, atomically: true);
print("PDF Saved");
self.refreshData()
})
}
}
})
Now I want to remove this file from uitableview in table and in documentdirecotry how to use index path row and how to find file name for removing
i know i will remove the file here but i don't know how to exactly remove the PDF in documentDirectory and Table
override func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
return true
}
override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
if (editingStyle == UITableViewCellEditingStyle.delete) {
// handle delete (by removing the data from your array and updating the tableview)
}
}
here is my table view cell
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! BookshelfCell
let document = documents[indexPath.row]
if let documentAttributes = document.documentAttributes {
if let title = documentAttributes["Title"] as? String {
cell.title = title
}
if let author = documentAttributes["Author"] as? String {
cell.author = author
}
here is my refresh data part
let fileManager = FileManager.default
let documentDirectory = fileManager.urls(for: .documentDirectory, in: .userDomainMask)[0]
let contents = try! fileManager.contentsOfDirectory(at: documentDirectory, includingPropertiesForKeys: nil, options: .skipsHiddenFiles)
documents = contents.flatMap { PDFDocument(url: $0) }
You will need to complete three steps to properly delete the document and update your table view:
Use FileManager's removeItem(at: URL) or removeItem(atPath: String) to delete the file from disk. (note that both of these methods throw so you need to use a do-catch block along with try and only proceed if the method didn't throw an error.) Update: If you look at the documentation for PDFDocument you will find that in addition to the documentAttributes that you are already using there is another optional property, documentURL that should give you exactly what you need to remove it.
Remove the document from documents (you could just refresh the whole array using your existing code but removing a single item is faster). documents.remove(at: indexPath.row)
Finally, you need to tell the table view to remove the row in question (you could of course just reload the whole table view but removing the single cell is cleaner) tableView.deleteRows(at: [indexPath], with .fade)
In case you are unfamiliar with do-catch blocks here is a bit of code from Apple's book on Swift (see below for link) simplified a bit:
do {
try makeASandwich()
eatASandwich() // This only gets called if the line above worked
} catch {
dealWithTheError() // This only gets called if makeASandwich() throws an error
}
Side Note
Apple has a fantastic guide on the Swift Language if you haven't done so yet I suggest reading, at least, The Basics. This will give you a base understanding of the language. If you are also new to programming I would suggest going through Apple's Learn to Code series that is free on iPads in the Swift Playgrounds app. The series will guide you through all the basics of programming giving you the tools to search through the documentation that Apple provides and find answers to your questions.
We all started at the beginning at some point, and we all had to crawl before we could walk and well before we could run.
I save images from user's photo library like this
func saveImage (image: UIImage, path: String ) -> Bool{
let jpgImageData = UIImageJPEGRepresentation(image, 1.0)
let result = jpgImageData!.writeToFile(path, atomically: true)
return result
}
func getDocumentsURL() -> NSURL {
let documentsURL = NSFileManager.defaultManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask)[0]
return documentsURL
}
func fileInDocumentsDirectory(filename: String) -> String
let fileURL = getDocumentsURL().URLByAppendingPathComponent(filename)
return fileURL.path!
}
let imagePath = fileInDocumentsDirectory(myImageName)
saveImage(yourPickedImage, path: imagePath)
And I read those images like this
func loadImageFromPath(path: String) -> UIImage? {
let image = UIImage(contentsOfFile: path)
return image
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reuseIdentifier, for: indexPath) as! Cell
cell.backgroundImage.image = loadImageFromPath(String(Globals.imagesArray[(indexPath as NSIndexPath).item]))
This all works fine, until I install a new build in Xcode 8, the images do not load into the UIImageView.
I have a feeling it could be that the file path changes, because the images do load if I quit and restart the app, but not from a new build.
How do I remedy this?
Thanks
The problem, as you already suspect, is that you are saving the value of path and using it later to try to load the file. You can't do that, because the path will be different each time. You need to go through all the work of calling imagePath = fileInDocumentsDirectory(myImageName) in order to load the path, just as you did when you saved it.
(Also, though this has nothing to do with your question, constructing a URL and then making a string path out of it, as you do in fileInDocumentsDirectory, is silly. Stick to the URL throughout!)