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..")
}
}
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")
}
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
}
}
I am in middle of developing an iOS app and I want to export certain reports in PDF format. So I am using webView to generate report and print webView to pdf.
Here is my code:
var renderer : UIPrintPageRenderer!
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(true)
renderer = CustomPrintPageRenderer()
if let url = Bundle.main.url(forResource: "pdf", withExtension: "html", subdirectory: "web") {
let request = URLRequest(url: url);
webView.delegate = self;
webView.loadRequest(request);
}
}
func webViewDidFinishLoad(_ webView: UIWebView) {
var pages : [UIViewPrintFormatter] = []
var index = 0
for query in queryList {
var result = webView.stringByEvaluatingJavaScript(from: query)
if(!webView.isLoading) {
let printFormatter = webView.viewPrintFormatter()
pages.append(printFormatter)
}
}
for page in pages {
renderer.addPrintFormatter(page, startingAtPageAt: index)
index = index + 1
}
let pdfData = drawPDFUsingPrintPageRenderer(printPageRenderer: renderer)
pdfData?.write(toFile: "\(AppDelegate.getAppDelegate().getDocDir())/report.pdf", atomically: true)
}
func drawPDFUsingPrintPageRenderer(printPageRenderer: UIPrintPageRenderer) -> NSData! {
let data = NSMutableData()
UIGraphicsBeginPDFContextToData(data, CGRect.zero, nil)
printPageRenderer.prepare(forDrawingPages: NSMakeRange(0, printPageRenderer.numberOfPages))
let bounds = UIGraphicsGetPDFContextBounds()
for i in 0...(printPageRenderer.numberOfPages - 1) {
UIGraphicsBeginPDFPage()
printPageRenderer.drawPage(at: i, in: bounds)
}
UIGraphicsEndPDFContext();
return data
}
Now problem is that, the generated PDF contains only last page printed multiple time (once for each page in pages which is not the expected behavior.
How can I add a PDF file for an app , where you click on a button to view the file & when you're done you get back to screen you were at?
If you simply want to view a PDF file you can load it into a UIWebView.
let url : NSURL! = NSURL(string: "http://developer.apple.com/iphone/library/documentation/UIKit/Reference/UIWebView_Class/UIWebView_Class.pdf")
webView.loadRequest(NSURLRequest(URL: url))
Swift 4.1 :
let url: URL! = URL(string: "http://developer.apple.com/iphone/library/documentation/UIKit/Reference/UIWebView_Class/UIWebView_Class.pdf")
webView.loadRequest(URLRequest(url: url))
If you'd like to achieve more, a good framework is PSPDFKit.
Apple added PDFKit framework in iOS 11
Add a UIView to your view controller and make it's class to PDFView
import UIKit
import PDFKit
class ViewController: UIViewController {
#IBOutlet var pdfView: PDFView!
override func viewDidLoad() {
super.viewDidLoad()
if let path = Bundle.main.path(forResource: "sample", ofType: "pdf") {
if let pdfDocument = PDFDocument(url: URL(fileURLWithPath: path)) {
pdfView.displayMode = .singlePageContinuous
pdfView.autoScales = true
pdfView.displayDirection = .vertical
pdfView.document = pdfDocument
}
}
}
}
There are 4 display modes : singlePage, singlePageContinuous, twoUp, twoUpContinuous .
SWIFT 4+
If has to open file from local cache/Documentdiectory which has file path
Method 1: using UIDocumentInteractionController
class ViewController: UIViewController,UIDocumentInteractionControllerDelegate {
//let path = Bundle.main.path(forResource: "Guide", ofType: ".pdf")!
let dc = UIDocumentInteractionController(url: URL(fileURLWithPath: path))
dc.delegate = self
dc.presentPreview(animated: true)
}
//MARK: UIDocumentInteractionController delegates
func documentInteractionControllerViewControllerForPreview(_ controller: UIDocumentInteractionController) -> UIViewController {
return self//or use return self.navigationController for fetching app navigation bar colour
}
Method 2: using WebView
let webview = WKWebView(frame: UIScreen.main.bounds)
view.addSubview(webview)
webview.navigationDelegate = self
webview.load(URLRequest(url: URL(fileURLWithPath: path)))//URL(string: "http://") for web URL
For Xcode 8.1 and Swift 3.0
Save the PDF file in any folder of your xcode.
Suppose the file name is 'Filename.pdf'
if let pdf = Bundle.main.url(forResource: "Filename", withExtension: "pdf", subdirectory: nil, localization: nil) {
let req = NSURLRequest(url: pdf)
yourWebViewOutletName.loadRequest(req as URLRequest)
}
Same will apply if you want to open any html file.
you can use UIDocumentInteractionController to preview file in IOS.
In the view controller's file, add a property of type UIDocumentInteractionController
and implement a simple delegate method of it
self.documentInteractionController = UIDocumentInteractionController.init(URL: url)
self.documentInteractionController?.delegate = self
self.documentInteractionController?.presentPreviewAnimated(true)
func documentInteractionControllerViewControllerForPreview(controller: UIDocumentInteractionController) -> UIViewController {
return self
}
don't forget to add UIDocumentInteractionControllerDelegate in view controller's class
Check out PDFKit from IOS11
Here is an example of a view controller which implements PDFView from PDFKit.
import UIKit
import PDFKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Add PDFView to view controller.
let pdfView = PDFView(frame: self.view.bounds)
self.view.addSubview(pdfView)
// Fit content in PDFView.
pdfView.autoScales = true
// Load Sample.pdf file.
let fileURL = Bundle.main.url(forResource: "Sample", withExtension: "pdf")
pdfView.document = PDFDocument(url: fileURL!)
}
}
SWIFT 5
An update to open the file from the Document directory (device) and present preview:
let urlFile = URL(string: pathToFile)
var documentInteractionController: UIDocumentInteractionController!
documentInteractionController = UIDocumentInteractionController.init(url: urlFile!)
documentInteractionController?.delegate = self
documentInteractionController?.presentPreview(animated: true)
And UIDocumentInteractionControllerDelegate:
extension ViewController: UIDocumentInteractionControllerDelegate {
func documentInteractionControllerViewControllerForPreview(_ controller: UIDocumentInteractionController) -> UIViewController {
return self
}
}
If you want to dismiss the document preview you can use:
documentInteractionController?.dismissPreview(animated: true)
You can use this code in Swift 4
Import PDFKit
Copy this code
let pdfView = PDFView()
pdfView.translatesAutoresizingMaskIntoConstraints = false
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: view.safeAreaLayoutGuide.topAnchor).isActive = true
pdfView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor).isActive = true
guard let path = Bundle.main.url(forResource: "test", withExtension: "pdf") else { return }
if let document = PDFDocument(url: path) {
pdfView.document = document
}
You can use this UIViewController. It contains the share button for free:
import UIKit
import PDFKit
class PDFWebViewController: UIViewController {
var pdfURL: URL!
private var pdfView: PDFView!
override func viewDidLoad() {
super.viewDidLoad()
self.edgesForExtendedLayout = []
self.setPDFView()
self.fetchPDF()
}
private func setPDFView() {
DispatchQueue.main.async {
self.pdfView = PDFView(frame: self.view.bounds)
self.pdfView.maxScaleFactor = 3;
self.pdfView.minScaleFactor = self.pdfView.scaleFactorForSizeToFit;
self.pdfView.autoScales = true;
self.pdfView.autoresizingMask = [.flexibleHeight, .flexibleWidth]
self.view.addSubview(self.pdfView)
}
}
private func fetchPDF() {
DispatchQueue.global(qos: .userInitiated).async {
if let data = try? Data(contentsOf: self.pdfURL), let document = PDFDocument(data: data) {
DispatchQueue.main.async {
self.pdfView.document = document
self.addShareBarButton()
}
}
}
}
private func addShareBarButton() {
let barButtonItem = UIBarButtonItem(barButtonSystemItem: .action,
target: self,
action: #selector(self.presentShare))
barButtonItem.tintColor = .white
self.navigationItem.rightBarButtonItem = barButtonItem
}
#objc private func presentShare() {
guard let pdfDocument = self.pdfView.document?.dataRepresentation() else { return }
let activityViewController = UIActivityViewController(activityItems: [pdfDocument], applicationActivities: nil)
activityViewController.popoverPresentationController?.barButtonItem = self.navigationItem.rightBarButtonItem
self.present(activityViewController, animated: true)
}
}
To use it:
let viewController = PDFWebViewController()
// the url can be a web url or a file url
viewController.pdfURL = url
self.navigationController?.pushViewController(viewController, animated: true)
You can also use Quick Look Framework from Apple.
It is very flexible.
You can show your PDF file with zoom feature.
Also you have support for all other types (like png, jpg, docx, txt, rtf, xlsx, zip, mov, etc) of files and it is very easy to use.
Please refer
this answer if you want detail description of using QuickLook.framework
A modernized version of Akila's answer, with the benefit that it is a drop in, ready to use UIViewController that you can integrate into your app.
import UIKit
import PDFKit
final class PDFViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let pdfView = PDFView(frame: view.frame)
title = "Your_title_here"
if let url = Bundle.main.url(forResource: "document_name_here", withExtension: "pdf"),
let pdfDocument = PDFDocument(url: url) {
pdfView.displayMode = .singlePageContinuous
pdfView.autoScales = true
pdfView.displayDirection = .vertical
pdfView.document = pdfDocument
view.addSubview(pdfView)
}
}
}
It creates the PDFView during viewDidLoad and sets it to use the view's frame
The URL for the PDF file is safely unwrapped from the bundle and then a PDFDocument is created, if possible
Some common settings are used. Adjust as needed
Finally, it adds the PDFView as a subview of the controller's view
//In Swift 5
class PDFBookViewController: UIViewController, PDFViewDelegate {
override func viewDidLoad() {
super.viewDidLoad()
addPDFView()
}
private func addPDFView() {
let pdfView = PDFView()
pdfView.translatesAutoresizingMaskIntoConstraints = false
pdfContenerView.addSubview(pdfView)
pdfView.leadingAnchor.constraint(equalTo: pdfContenerView.safeAreaLayoutGuide.leadingAnchor).isActive = true
pdfView.trailingAnchor.constraint(equalTo: pdfContenerView.safeAreaLayoutGuide.trailingAnchor).isActive = true
pdfView.topAnchor.constraint(equalTo: pdfContenerView.safeAreaLayoutGuide.topAnchor).isActive = true
pdfView.bottomAnchor.constraint(equalTo: pdfContenerView.safeAreaLayoutGuide.bottomAnchor).isActive = true
pdfView.autoScales = true
pdfView.displayMode = .singlePageContinuous
pdfView.displayDirection = .vertical
///Open pdf with help of FileManager URL
if let dir = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first {
let bookWithPdf = "\(bookname).pdf"
let fileURL = dir.appendingPathComponent(bookWithPdf)
let document = PDFDocument(url: fileURL)
pdfView.document = document
}
#IBAction func backButtonPressed(_ sender: Any) {
navigationController?.popViewController(animated: true)
}
}
if let url: URL = URL(string: "http://developer.apple.com/iphone/library/documentation/UIKit/Reference/UIWebView_Class/UIWebView_Class.pdf") {
webView.loadRequest(URLRequest(url: url)) }
let openLink = NSURL(string : OtherContactorProfileVview.Result.CertificateList[index].CertificateFileLink)
if #available(iOS 9.0, *) {
let svc = SFSafariViewController(url: openLink! as URL)
present(svc, animated: true, completion: nil)
} else {
let port = UIStoryboard(
name: "Main",
bundle: nil
).instantiateViewController(withIdentifier: "PDFViewer") as! PDFViewer
port.strUrl = OtherContactorProfileVview.Result.CertificateList[index].CertificateFileLink
navigationController?.pushViewController(port, animated: true)
}