Convert UIimage (photo library or camera) into PDF in iPhone Files - ios

I include VNDocumentCameraViewControllerDelegate in my class
To "scan the document", I used this code:
#IBAction func scanButton(_ sender: UIButton) {
let scannerViewController = VNDocumentCameraViewController()
scannerViewController.delegate = self
present(scannerViewController, animated: true)
}
In viewDidLoad, I include the PDFView
override func viewDidLoad() {
super.viewDidLoad()
pdfView = PDFView()
pdfView.translatesAutoresizingMaskIntoConstraints = false
self.view.addSubview(pdfView)
pdfView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor).isActive = true
pdfView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor).isActive = true
pdfView.topAnchor.constraint(equalTo: startOutlet.bottomAnchor).isActive = true
pdfView.bottomAnchor.constraint(equalTo: PDFOutlet.topAnchor).isActive = true
PDFOutlet.isHidden = true }
I add the documentCameraViewController functions
func documentCameraViewController(_ controller: VNDocumentCameraViewController, didFinishWith scan: VNDocumentCameraScan) {
guard scan.pageCount >= 1 else {
controller.dismiss(animated: true)
return
}
DispatchQueue.main.async {
let pdfDocument = PDFDocument()
for i in 0 ..< scan.pageCount {
if let image = scan.imageOfPage(at: i).resize(toWidth: 250) {
print("image size is \(image.size.width), \(image.size.height)")
// Create a PDF page instance from your image
let pdfPage = PDFPage(image: image)
// Insert the PDF page into your document
pdfDocument.insert(pdfPage!, at: i)
}
}
// Retrieve raw data of your PDF document
let data = pdfDocument.dataRepresentation()
let documentDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
let docURL = documentDirectory.appendingPathComponent("Scanned-Docs.pdf")
do {
try data?.write(to: docURL)
}
catch(let error)
{
print("error is \(error.localizedDescription)")
}
}
controller.dismiss(animated: true)
PDFOutlet.isHidden = false
}
func documentCameraViewController(_ controller: VNDocumentCameraViewController, didFailWithError error: Error) {
print(error)
controller.dismiss(animated: true)
}
func documentCameraViewControllerDidCancel(_ controller: VNDocumentCameraViewController) {
controller.dismiss(animated: true)
}
}
extension SnapToPDF: UINavigationControllerDelegate {
// Not used, but necessary for compilation
}
extension UIImage {
func resize(toWidth width: CGFloat) -> UIImage? {
let canvas = CGSize(width: width, height: CGFloat(ceil(width/size.width * size.height)))
return UIGraphicsImageRenderer(size: canvas, format: imageRendererFormat).image {
_ in draw(in: CGRect(origin: .zero, size: canvas))
}
}
I want to have a button for users to click the save the PDF that they have selected, but this part of the code isn't working. Everything else above works, and in the app, I can take a photo and resize it, or choose from photo library. Ideally, wanted to show options for the user to save the PDF into their Files in the iPhone, but it is not working. The button does nothing. Please help!
#IBAction func viewPDFButton(_ sender: UIButton) {
let fileManager = FileManager.default
let documentDirectory = fileManager.urls(for: .documentDirectory, in: .userDomainMask).first!
let docURL = documentDirectory.appendingPathComponent("Scanned-Docs.pdf")
if fileManager.fileExists(atPath: docURL.path){
pdfView.document = PDFDocument(url: docURL)
}
else {
print("file does not exist..")
}
}

Related

Saving a reference to an image in camera roll to recall later in app?

I'm trying to let the user take or select an image in an ImagePickerController, and I want to save a reference (as efficiently as possible) in my app to recall when the app loads again. Is saving the image's file URL the best approach for this?
import UIKit
import SwiftUI
struct ImagePicker: UIViewControllerRepresentable {
var sourceType: UIImagePickerController.SourceType = .photoLibrary
#Binding var selectedImage: UIImage
#Environment(\.presentationMode) private var presentationMode
func makeUIViewController(context: UIViewControllerRepresentableContext<ImagePicker>) -> UIImagePickerController {
let imagePicker = UIImagePickerController()
imagePicker.allowsEditing = false
imagePicker.sourceType = sourceType
imagePicker.delegate = context.coordinator
return imagePicker
}
func updateUIViewController(_ uiViewController: UIImagePickerController, context: UIViewControllerRepresentableContext<ImagePicker>) {
}
func makeCoordinator() -> Coordinator {
Coordinator(self)
}
final class Coordinator: NSObject, UIImagePickerControllerDelegate, UINavigationControllerDelegate {
var parent: ImagePicker
init(_ parent: ImagePicker) {
self.parent = parent
}
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
if let image = info[UIImagePickerController.InfoKey.originalImage] as? UIImage {
parent.selectedImage = image
}
if let imgUrl = info[UIImagePickerController.InfoKey.imageURL] as? URL{
let imgName = imgUrl.lastPathComponent
let documentDirectory = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true).first
let localPath = documentDirectory?.appending(imgName)
let image = info[UIImagePickerController.InfoKey.originalImage] as! UIImage
let data = image.pngData()! as NSData
data.write(toFile: localPath!, atomically: true)
//let imageData = NSData(contentsOfFile: localPath!)!
let photoURL = URL.init(fileURLWithPath: localPath!)//NSURL(fileURLWithPath: localPath!)
print(photoURL)
//TODO save this url in my app as a reference to look up
}
parent.presentationMode.wrappedValue.dismiss()
}
}
}
URL of image of camera roll may change , better way is to save your image to filesystem in the app sandbox and the you can save given name or something to retrieve it when u needed back
//MARK: save and retrive Images
extension UIImage {
func saveImage(imageName: String) {
guard let documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first else { return }
let fileName = imageName
let fileURL = documentsDirectory.appendingPathComponent(fileName)
guard let data = self.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)
}
}
static 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
}
static func removeImage(fileName: String){
guard let documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first else { return }
let fileURL = documentsDirectory.appendingPathComponent(fileName)
if FileManager.default.fileExists(atPath: fileURL.path) {
do {
try FileManager.default.removeItem(atPath: fileURL.path)
print("Removed image")
} catch let removeError {
print("couldn't remove file at path", removeError)
}
}
}
}
Usage
yourImage.saveImage(imageName: "imageNameToSave")//<-save
UIImage.loadImageFromDiskWith(fileName: "ImageNameToRetrive")//<-retrive
UIImage.removeImage(fileName: "ImageNameToRemove")//<-remove
Edit:
You can definitely do FileManager as well, it truly depends on how many files you will be saving, and to where you want to save them. If it is a measly 1 file, that doesn't need to be secured and is public to the app, UserDefaults is the way to go. If you want to add a bit more control of that file, FileManager would be the way to go.
UserDefaults is your way to go to store locally.
Store Image Data
func locallyStoreImgData(image: UIImage, key:String) {
if let pngRepresentation = image.pngData() {
UserDefaults.standard.set(pngRepresentation, forKey: key)
}
else {
//Was unable to create png representation
}
}
Retrieve Image Data
func obtainImg(key:String) -> UIImage? {
if let imageData = UserDefaults.standard.object(forKey: key) as? Data,
let image = UIImage(data: imageData) {
return image
}
return nil
}
Use Case
locallyStoreImgData(image: myImage, key: "myImageKey")
if let image = obtainImg(key: "myImageKey") {
//Do something with image
}
else {
//Was unable to recreate image
}

GPUImage3 Unable to export video to Document Directory

I am using following source code to export filtered video to document directory but the exported file is corrupted/wrong.
Would you please go through following source and let me know where I am making mistake?
class ViewController: UIViewController {
#IBOutlet weak var renderView: RenderView!
var movie:MovieInput!
var writer:MovieOutput!
var filter:LookupFilter!
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
let bundleURL = Bundle.main.resourceURL!
let movieURL = URL(string:"sample_iPod.m4v", relativeTo:bundleURL)!
do {
let documentDirectory = try FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor:nil, create:true)
let fileURL = documentDirectory.appendingPathComponent("TestVideo.mov")
movie = try MovieInput(url:movieURL, playAtActualSpeed:true)
writer = try MovieOutput(URL: fileURL, size: Size(width: 100.0, height: 100.0))
filter = LookupFilter()
filter.lookupImage = PictureInput(image: UIImage(named: "Image")!)
movie --> filter --> renderView
movie.runBenchmark = true
movie.addTarget(writer)
movie.start()
writer.startRecording()
self.writer.finishRecording {
print("Written")
}
} catch {
print("Couldn't process movie with error: \(error)")
}
}
}
Simple answer: now you have 5 seconds .
self.movie.addTarget(writer)
self.movie.start()
self.filter --> self.writer
self.writer.startRecording()
let interval = 5 // now you have 5 seconds .
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + interval) {
self.writer.finishRecording {
print("Written")
}
}
To solve your problem forward,
You should extract out the record logic. put that in a filter button action.
like the following demo code .
#IBAction func capture(_ sender: AnyObject) {
if (!isRecording) {
do {
self.isRecording = true
let documentsDir = try FileManager.default.url(for:.documentDirectory, in:.userDomainMask, appropriateFor:nil, create:true)
let fileURL = URL(string:"test.mp4", relativeTo:documentsDir)!
do {
try FileManager.default.removeItem(at:fileURL)
} catch {
}
movieOutput = try MovieOutput(URL:fileURL, size:Size(width:480, height:640), liveVideo:true)
filter --> movieOutput!
movieOutput!.startRecording()
DispatchQueue.main.async {
// Label not updating on the main thread, for some reason, so dispatching slightly after this
(sender as! UIButton).titleLabel!.text = "Stop"
}
} catch {
fatalError("Couldn't initialize movie, error: \(error)")
}
} else {
movieOutput?.finishRecording{
self.isRecording = false
DispatchQueue.main.async {
(sender as! UIButton).titleLabel!.text = "Record"
}
self.movieOutput = nil
}
}
}
You miss one line code filter --> writer
movie.addTarget(writer)
movie.start()
filter --> writer
writer.startRecording()
self.writer.finishRecording {
print("Written")
}

Present preview of downloaded file directly in app

My application have file download option which downloads the file using alamofire download method. When the download completes i need to present the preview of the file without saving it to internal/cloud storage. How can i achieve this whatsapp like function that shows the preview after downloading the file.
func downloadFile(fileUrl: URL) {
let destination = DownloadRequest.suggestedDownloadDestination(for: .documentDirectory)
Alamofire.download(fileUrl, to: destination)
.response(completionHandler: { (downloadResponse) in
self.dic.url = downloadResponse.destinationURL
self.dic.uti = downloadResponse.destinationURL!.uti
let rect = CGRect(x: 0, y: 0, width: 100, height: 100)
self.dic.presentOpenInMenu(from: rect, in: self.view, animated: true)
})
}
To present a preview of a file use Appleā€™s QuickLook framework that lets you embed previewing for a huge range of file types, including iWork documents, Microsoft Office documents, PDFs, images, and more, all without writing much code.
First, import the QuickLook framework, then make your view controller conform to the QLPreviewControllerDataSource protocol.
Reference:
https://www.hackingwithswift.com/example-code/libraries/how-to-preview-files-using-quick-look-and-qlpreviewcontroller
https://github.com/gargsStack/QLPreviewDemo
https://www.appcoda.com/quick-look-framework/
Code:
class ViewController: UIViewController {
var previewItem = URL!
func downloadFile(fileUrl: URL) {
let destination = DownloadRequest.suggestedDownloadDestination(for: .documentDirectory)
Alamofire.download(fileUrl, to: destination)
.response(completionHandler: { (downloadResponse) in
let previewController = QLPreviewController()
previewController.dataSource = self
self.previewItem = downloadResponse.destinationURL
self.present(previewController, animated: true, completion: nil)
})
}
}
extension ViewController: QLPreviewControllerDataSource {
func numberOfPreviewItems(in controller: QLPreviewController) -> Int {
return 1
}
func previewController(_ controller: QLPreviewController, previewItemAt index: Int) -> QLPreviewItem {
return self.previewItem as QLPreviewItem
}
}
Here is one solution using Alamofire. Someone may help.
Steps:
Alamofire has excellent staff to direct download and also save/write
your file into disc.
Return a path where downloaded file saved.
Using UIDocumentInteractionController pass the file path
Then present this view
import Alamofire
extension UIViewController : UIDocumentInteractionControllerDelegate{
func downloadFileForPreview(fileName: String, fileExtension: String, filePath: String ) {
let destination: DownloadRequest.DownloadFileDestination = { _, _ in
let documentsURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
let fileWithExtension = "file.\(fileExtension)"
let fileURL = documentsURL.appendingPathComponent(fileWithExtension)
return (fileURL, [.removePreviousFile, .createIntermediateDirectories])
}
//UtilitySwift.showUniversalLoadingView(true)
Alamofire.download(filePath, to: destination).response { response in
debugPrint(response)
//UtilitySwift.showUniversalLoadingView(false)
if response.error == nil, let storeFilePath = response.destinationURL?.path {
//let image = UIImage(contentsOfFile: imagePath)
self.previewDocument(withFilePath: response.destinationURL)
print(storeFilePath)
}
else{
UtilitySwift.showErrorMessage(message: response.error?.localizedDescription ?? "Error occured when downloading" )
print(response.error?.localizedDescription ?? "")
}
}
}
// Converted to Swift 5.1 by Swiftify v5.1.29672 - https://objectivec2swift.com/
func previewDocument(withFilePath filePath: URL?) {
var documentInteractionController: UIDocumentInteractionController?
if filePath != nil {
// Initialize Document Interaction Controller
if let filePath = filePath {
documentInteractionController = UIDocumentInteractionController(url: filePath)
}
// Configure Document Interaction Controller
documentInteractionController?.delegate = self as UIDocumentInteractionControllerDelegate
//if not possible to open
if !documentInteractionController!.presentPreview(animated: true) {
documentInteractionController?.presentOptionsMenu(from: CGRect.zero, in: self.view, animated: true)
}
} else {
// error
print("file preview error")
}
}
//UIDocumentInteractionControllerDelegate
public func documentInteractionControllerViewControllerForPreview(_ controller: UIDocumentInteractionController) -> UIViewController {
self
}
}
Call from any UIViewController
self.downloadFileForPreview(fileName: "file", fileExtension: fileExt ?? "", filePath: REPLACE_WITH_DOWNLOAD_URL)

Download PDF file and save in document directory

I have the following code that allows me to download a PDF file from a URL, it works correctly:
class ViewController: UIViewController {
#IBOutlet weak var progressView: UIProgressView!
override func viewDidLoad() {
let _ = DownloadManager.shared.activate()
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
DownloadManager.shared.onProgress = { (progress) in
OperationQueue.main.addOperation {
self.progressView.progress = progress
}
}
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
DownloadManager.shared.onProgress = nil
}
#IBAction func startDownload(_ sender: Any) {
let url = URL(string: "https://d0.awsstatic.com/whitepapers/KMS-Cryptographic-Details.pdf")!
let task = DownloadManager.shared.activate().downloadTask(with: url)
task.resume()
}
}
The file is currently going to: file:///Users/cybermac/Library/Developer/CoreSimulator/Devices/CAEC75D0-423A-4FB2-B0D6-9E7CADB190A1/data/Containers/Data/Application/8B5CBFC8-7058-48DB-A1C4-872302A80610/Library/Caches/com.apple.nsurlsessiond/Downloads/com.example.DownloadTaskExample/CFNetworkDownload_Q7OVlf.tmp
How do I save it in /Documents/?
Something like this: file:///Users/cybermac/Library/Developer/CoreSimulator/Devices/CAEC75D0-423A-4FB2-B0D6-9E7CADB190A1/data/Containers/Data/Application/64370B29-2C01-470F-AE76-17EF1A7BC918/Documents/
The idea is that the file saved in that directory can be used to read it offline (with PDFKit or webKit). It will only be deleted if the application is deleted.
You need to move the file to your custom location after the download. Implement URLSessionDownloadDelegate and you will receive the location of your downloaded file.
Delegate method:
func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL)
Code to move the file:
do {
let documentsURL = try
FileManager.default.url(for: .documentDirectory,
in: .userDomainMask,
appropriateFor: nil,
create: false)
let savedURL = documentsURL.appendingPathComponent("yourCustomName.pdf")
try FileManager.default.moveItem(at: location, to: savedURL)
} catch {
print ("file error: \(error)")
}
To learn more refer to this repo.
This snippet was downloaded your Remote PDF from Server and "Save" into your Photo Library
func drawPDFfromURL(url: URL) -> UIImage? {
guard let document = CGPDFDocument(url as CFURL) else { return nil }
guard let page = document.page(at: 1) else { return nil }
let pageRect = page.getBoxRect(.mediaBox)
let renderer = UIGraphicsImageRenderer(size: pageRect.size)
let img = renderer.image { ctx in
UIColor.white.set()
ctx.fill(pageRect)
ctx.cgContext.translateBy(x: 0.0, y: pageRect.size.height)
ctx.cgContext.scaleBy(x: 1.0, y: -1.0)
ctx.cgContext.drawPDFPage(page)
}
return img
}
step 2
UIImageWriteToSavedPhotosAlbum(drawPDFfromURL(url: url!) ?? UIImage(), nil, nil, nil)

how to share QR created image swift 4

I am creating QR code in swift and assigning it to an imageView
when I try to share that image with generated code, it does not shares that image,
func createCode()
{
let text = email
let data = text.data(using: .ascii, allowLossyConversion: false)
fillter = CIFilter(name: "CIQRCodeGenerator")
fillter.setValue(data, forKey: "inputMessage")
let transform = CGAffineTransform(scaleX: 5.0, y: 5.0)
CreatedImage = UIImage(ciImage: (fillter.outputImage?.transformed(by: transform))!)
imageCode.image = CreatedImage as UIImage
}
and this is share button
#IBAction func shareButtonPressed(_ sender: Any)
{
let activityItem: [UIImage] = [imageCode.image!]
let activity = UIActivityViewController(activityItems: activityItem as [UIImage], applicationActivities: [])
activity.popoverPresentationController?.sourceView = self.view
self.present(activity, animated: true, completion: nil)
}
it shows like it has nothing to share, it does not pick any bit of image
Have you created a variable to store the image somewhere e.g.
var generatedImage: UIImage?
Assuming then, that I have read your question correctly, in your creation function you can cast the image at the end of the function e.g:
generatedImage = imageCode.image
Then in your share function you could say:
guard let validQR = generatedImage else { return }
let activityItem: [UIImage] = [validQR]
let activity = UIActivityViewController(activityItems: activityItem as [UIImage], applicationActivities: [])
activity.popoverPresentationController?.sourceView = self.view
self.present(activity, animated: true, completion: nil)
I tested with an image from my Bundle e.g:
generatedImage = UIImage(named: "SCNPyramid")
And I was able to share the image :)
after searching all..
I cam I to know that make a programmatically screen shot of desired view, that is sent..
I have been having the same problem and solved it by first saving the generated qr code image to a file and then sharing the file url.
private func shareQRCode() {
guard let qrcode = self.qrCodeImage,
let data = qrcode.pngData(),
let url = self.saveInCache(data: data, fileName: "QRCode.png") else { return }
// set up activity view controller
let imageToShare = [url]
let activityViewController = UIActivityViewController(activityItems: imageToShare, applicationActivities: nil)
activityViewController.popoverPresentationController?.sourceView = self.view // so that iPads won't crash
// work around to prevent dismissing current view after saving image
let tempController = TransparentViewController()
tempController.modalPresentationStyle = .overFullScreen
activityViewController.completionWithItemsHandler = { [weak tempController] _, _, _, _ in
if let presentingViewController = tempController?.presentingViewController {
presentingViewController.dismiss(animated: false, completion: nil)
} else {
tempController?.dismiss(animated: false, completion: nil)
}
}
present(tempController, animated: true) { [weak tempController] in
tempController?.present(activityViewController, animated: true, completion: nil)
}
}
Here is the code for saveInCache function:
private func saveInCache(data: Data, fileName: String) -> URL? {
let paths = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask)
let path = paths[0]
let fileUrl = path.appendingPathComponent(fileName)
let fileManager = FileManager.default
if self.pathExists(fileUrl) {
do {
try fileManager.removeItem(at: fileUrl)
} catch { return fileUrl }
}
guard fileManager.createFile(atPath: fileUrl.path, contents: data, attributes: nil) else {
return nil
}
return fileUrl
}
private func pathExists(_ path: URL) -> Bool {
let fileManager = FileManager.default
var isDir: ObjCBool = false
if fileManager.fileExists(atPath: path.path, isDirectory: &isDir) {
if isDir.boolValue {
// file exists and is a directory
return true
} else {
// file exists and is not a directory
return true
}
} else {
// file does not exist
return false
}
}
And here a simple Transparent View Controller for ActivityViewController work around:
final class TransparentViewController: UIViewController {
override func viewDidLoad() {
self.view.backgroundColor = .clear
}
}

Resources