I am creating a PDF and sharing it via UIActivityViewController.
func createFlyer() -> Data {
// 1
let pdfMetaData = [
kCGPDFContextCreator: "Flyer",
]
let format = UIGraphicsPDFRendererFormat()
format.documentInfo = pdfMetaData as [String: Any]
// 2
let pageWidth = 8.5 * 72.0
let pageHeight = 11 * 72.0
let pageRect = CGRect(x: 0, y: 0, width: pageWidth, height: pageHeight)
// 3
let renderer = UIGraphicsPDFRenderer(bounds: pageRect, format: format)
// 4
let data = renderer.pdfData { (context) in
// 5
context.beginPage()
// 6
let attributes = [
NSAttributedString.Key.font: UIFont.boldSystemFont(ofSize: 72)
]
let text = "I'm a PDF!"
text.draw(at: CGPoint(x: 0, y: 0), withAttributes: attributes)
}
return data
}
func sharePDF () {
let activityViewController = UIActivityViewController(
activityItems: [data], applicationActivities: nil)
activityViewController.excludedActivityTypes = [UIActivity.ActivityType.saveToCameraRoll]
if let popoverPresentationController = activityViewController.popoverPresentationController {
popoverPresentationController.barButtonItem = self.sharePDFButton
}
self.present(activityViewController, animated: true, completion: {
})
}
Everything is fine, except for the file name is generated by the system ('PDF Document.pdf').
Is there a way to have a custom file name?
Please try to use below code while presenting activityViewController, I hope it works.
let fileManager = FileManager.default
let documentoPath = getDirectoryPath().appendingPathComponent(url.lastPathComponent) as URL
print("documentoPath :\(documentoPath)")
if fileManager.fileExists(atPath: documentoPath.path) {
DispatchQueue.main.async {
let activityViewController: UIActivityViewController = UIActivityViewController(activityItems: [documentoPath], applicationActivities: nil)
activityViewController.popoverPresentationController?.sourceView = self.view
self.downloadNavController?.present(activityViewController, animated: true, completion: { () in
UIBarButtonItem.appearance().tintColor = UIColor.systemBlue
UINavigationBar.appearance().barTintColor = UIColor.white
})
}
}
You can find getDirectoryPath() here:
func getDirectoryPath() -> URL {
var nestedFolderURL: URL?
do{
let rootFolderURL = try FileManager.default.url(
for: .documentDirectory,
in: .userDomainMask,
appropriateFor: nil,
create: false
)
nestedFolderURL = rootFolderURL
if !FileManager.default.fileExists(atPath: nestedFolderURL!.relativePath) {
try FileManager.default.createDirectory(
at: nestedFolderURL!,
withIntermediateDirectories: false,
attributes: nil
)
}
return nestedFolderURL!
} catch (let error) {
print(error)
}
return nestedFolderURL!
}
Related
I open url using uiwebview then I show share sheet, but there is no create pdf button. I want show the create pdf button like safari, and create pdf from url then share the pdf. how to do it?
OR how to create pdf from url webview/uiview then save pdf to files?
this is my share sheet
and this is safari share sheet have create pdf button
and this my code to show share sheet
let items = [URL(string: "https://www.apple.com")!]
let ac = UIActivityViewController(activityItems: items, applicationActivities: nil)
present(ac, animated: true)
extension WKWebView {
// Call this function when WKWebView finish loading
func exportAsPdfFromWebView() -> String {
let pdfData = createPdfFile(printFormatter: self.viewPrintFormatter())
return self.saveWebViewPdf(data: pdfData)
}
func createPdfFile(printFormatter: UIViewPrintFormatter) -> NSMutableData {
let originalBounds = self.bounds
self.bounds = CGRect(x: originalBounds.origin.x,
y: bounds.origin.y,
width: self.bounds.size.width,
height: self.scrollView.contentSize.height)
let pdfPageFrame = CGRect(x: 0, y: 0, width: self.bounds.size.width,
height: self.scrollView.contentSize.height)
let printPageRenderer = UIPrintPageRenderer()
printPageRenderer.addPrintFormatter(printFormatter, startingAtPageAt: 0)
printPageRenderer.setValue(NSValue(cgRect: UIScreen.main.bounds), forKey: "paperRect")
printPageRenderer.setValue(NSValue(cgRect: pdfPageFrame), forKey: "printableRect")
self.bounds = originalBounds
return printPageRenderer.generatePdfData()
}
// Save pdf file in document directory
func saveWebViewPdf(data: NSMutableData) -> String {
let paths = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
let docDirectoryPath = paths[0]
let pdfPath = docDirectoryPath.appendingPathComponent("webViewPdf.pdf")
if data.write(to: pdfPath, atomically: true) {
return pdfPath.path
} else {
return ""
}
}
}
extension UIPrintPageRenderer {
func generatePdfData() -> NSMutableData {
let pdfData = NSMutableData()
UIGraphicsBeginPDFContextToData(pdfData, self.paperRect, nil)
self.prepare(forDrawingPages: NSMakeRange(0, self.numberOfPages))
let printRect = UIGraphicsGetPDFContextBounds()
for pdfPage in 0..<self.numberOfPages {
UIGraphicsBeginPDFPage()
self.drawPage(at: pdfPage, in: printRect)
}
UIGraphicsEndPDFContext();
return pdfData
}
}
You can create pdf from url webview/uiview then save pdf to files using this
I found the answer from other
first I create new class PDF.swift
class func generate(using printFormatter: UIPrintFormatter) -> String {
// assign the print formatter to the print page renderer
let renderer = UIPrintPageRenderer()
renderer.addPrintFormatter(printFormatter, startingAtPageAt: 0)
// static let ANSI_A = CGSize(width:612,height:792)
// static let ANSI_B = CGSize(width:792,height:1224)
// static let ANSI_C = CGSize(width:1584,height:1224)
// static let ANSI_D = CGSize(width:2448,height:1584)
// static let ANSI_E = CGSize(width:3168,height:2448)
//
// static let ISO216_A0 = CGSize(width:2384,height:3370)
// static let ISO216_A1 = CGSize(width:1684,height:2384)
// static let ISO216_A2 = CGSize(width:1190,height:1684)
// static let ISO216_A3 = CGSize(width:842,height:1190)
// static let ISO216_A4 = CGSize(width:595,height:842)
// static let ISO216_A5 = CGSize(width:420,height:595)
// static let ISO216_A6 = CGSize(width:298,height:420)
// static let ISO216_A7 = CGSize(width:210,height:298)
// static let ISO216_A8 = CGSize(width:148,height:210)
// assign paperRect and printableRect values
let page = CGRect(x: 0, y: 0, width: 1190, height: 842) // A3 landscape
// let page = CGRect(x: 0, y: 0, width: 2384, height: 800) // A4, 72 dpi
renderer.setValue(page, forKey: "paperRect")
renderer.setValue(page, forKey: "printableRect")
// create pdf context and draw each page
let pdfData = NSMutableData()
// UIGraphicsBeginPDFContextToData(pdfData, .zero, nil)
UIGraphicsBeginPDFContextToData(pdfData, page, nil)
for i in 0..<renderer.numberOfPages {
UIGraphicsBeginPDFPage()
renderer.drawPage(at: i, in: UIGraphicsGetPDFContextBounds())
}
UIGraphicsEndPDFContext();
// save data to a pdf file and return
guard nil != (try? pdfData.write(to: outputURL, options: .atomic))
else { fatalError("Error writing PDF data to file.") }
return outputURL.absoluteString
}
private class var outputURL: URL {
guard let directory = try? FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
else { fatalError("Error getting user's document directory.") }
let url = directory.appendingPathComponent(outputFileName).appendingPathExtension("pdf")
print("open \(url.path)")
return url
}
private class var outputFileName: String {
return "generated-\(Int(Date().timeIntervalSince1970))"
}
and call the function to generate pdf then share using share sheet to save files
let uiPDFData = PDF.generate(using: self.uiWebViewPrintFormatter())
print(uiPDFData)
DispatchQueue.main.async{
let fileURL = NSURL(fileURLWithPath: (uiPDFData))
// Create the Array which includes the files you want to share
var filesToShare = [Any]()
// Add the path of the file to the Array
filesToShare.append(fileURL)
// Make the activityViewContoller which shows the share-view
let activityViewController = UIActivityViewController(activityItems: filesToShare, applicationActivities: nil)
// Show the share-view
if UIDevice.current.userInterfaceIdiom == .pad {
if let popup = activityViewController.popoverPresentationController {
popup.sourceView = self.view
popup.sourceRect = CGRect(x: self.view.frame.size.width / 2, y: self.view.frame.size.height / 4, width: 0, height: 0)
}
}
self.present(activityViewController, animated: true, completion: nil)
}
I am trying to export a PDF with a password protection.
Currently, My app directly exports the PDF File to the iOS Files app...
How do I password protect it before exporting it?
Here's my current code, Please do let me know what I have to change to achive this.
func documentCameraViewController(_ controller: VNDocumentCameraViewController, didFinishWith scan: VNDocumentCameraScan) {
guard scan.pageCount >= 1 else {
controller.dismiss(animated: true)
return
}
var arrImages = [UIImage]()
for i in 0...scan.pageCount-1 {
let originalImage = scan.imageOfPage(at: i)
let fixedImage = reloadedImage(originalImage)
arrImages.append(fixedImage)
}
controller.dismiss(animated: true)
let documentDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
let docURL = documentDirectory.appendingPathComponent("ScannedImage.pdf")
let data = createNewPDF(arrImage: arrImages)
do{
try data?.write(to: docURL)
print("Success")
}catch(let error){
print("error is \(error.localizedDescription)")
}
}
The PDF Creation Functions
func createPDF(image: UIImage) -> NSData? {
let pdfData = NSMutableData()
let pdfConsumer = CGDataConsumer(data: pdfData as CFMutableData)!
var mediaBox = CGRect.init(x: 0, y: 0, width: image.size.width, height: image.size.height)
let pdfContext = CGContext(consumer: pdfConsumer, mediaBox: &mediaBox, nil)!
pdfContext.beginPage(mediaBox: &mediaBox)
pdfContext.draw(image.cgImage!, in: mediaBox)
pdfContext.endPage()
let activityViewController = UIActivityViewController(activityItems: [pdfData], applicationActivities: nil)
present(activityViewController, animated: true, completion: nil)
return pdfData
}
func createNewPDF(arrImage: [UIImage]) -> Data? {
let pdfDocument = PDFDocument()
for i in 0...arrImage.count-1 {
let pdfPage = PDFPage(image: arrImage[i])
pdfDocument.insert(pdfPage!, at: i)
}
let pdfData = pdfDocument.dataRepresentation()
let activityViewController = UIActivityViewController(activityItems: [pdfData!], applicationActivities: nil)
present(activityViewController, animated: true, completion: nil)
return pdfData
}
Please do let me know if you need anything else....
Thanks in advance!
I'm trying to open a pdf file that was generated from a UItableView and display it in iBooks.
I've used the following code
extension UITableView {
// Export pdf from UITableView and save pdf in drectory and return pdf file path
func exportAsPdfFromTable() -> String {
let originalBounds = self.bounds
self.bounds = CGRect(x:originalBounds.origin.x, y: originalBounds.origin.y, width: self.contentSize.width, height: self.contentSize.height)
let pdfPageFrame = CGRect(x: 0, y: 0, width: self.bounds.size.width, height: self.contentSize.height)
let pdfData = NSMutableData()
UIGraphicsBeginPDFContextToData(pdfData, pdfPageFrame, nil)
UIGraphicsBeginPDFPageWithInfo(pdfPageFrame, nil)
guard let pdfContext = UIGraphicsGetCurrentContext() else { return "" }
self.layer.render(in: pdfContext)
UIGraphicsEndPDFContext()
self.bounds = originalBounds
// Save pdf data
return self.saveTablePdf(data: pdfData)
}
// Save pdf file in document directory
func saveTablePdf(data: NSMutableData) -> String {
let paths = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
let docDirectoryPath = paths[0]
let pdfPath = docDirectoryPath.appendingPathComponent("tablePdf.pdf")
if data.write(to: pdfPath, atomically: true) {
return pdfPath.path
} else {
return ""
}
}
}
then I call the function from a button with an IBAction and tried to use the document directory's file path but it doesn't work.
#IBAction func export() {
let pdfFilePath = self.tableView.exportAsPdfFromTable()
print(pdfFilePath)
if let url = URL(string:"itms-books://var/mobile/Containers/Data/Application/3FF01C35-270E-49A0-88DD-E88175A93FCC/Documents/tablePdf.pdf") {
if UIApplication.shared.canOpenURL(url) {
UIApplication.shared.open(url, options: [:])
}
}
}
the pdf file is saved in the simulators document directory, is there a way to save it locally in the tablet? what is the best way to get the pdf opened in ibooks?
Here is an example.
import UIKit
class ViewController: UIViewController {
private static let filename = "myfile.pdf"
private var documentInteractionController: UIDocumentInteractionController?
override func viewDidLoad() {
super.viewDidLoad()
let pdfData = UIGraphicsPDFRenderer(bounds: .init(origin: .zero, size: .init(width: 400, height: 400))).pdfData { context in
context.beginPage()
let attributes = [
NSAttributedString.Key.font : UIFont.boldSystemFont(ofSize: 150)
]
let text = "Hello!" as NSString
text.draw(in: CGRect(x: 0, y: 0, width: 500, height: 200), withAttributes: attributes)
}
if let url = try? FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: false) {
let pdfURL = url.appendingPathComponent(ViewController.filename)
try? pdfData.write(to: pdfURL)
}
}
#IBAction func tapShareButton(_ sender: UIButton) {
if let url = try? FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: false) {
let pdfURL = url.appendingPathComponent(ViewController.filename)
documentInteractionController = UIDocumentInteractionController(url: pdfURL)
documentInteractionController?.presentOpenInMenu(from: sender.frame, in: view, animated: true)
}
}
}
Rather than using the pdf from my document directory, I passed the data on by using the UIActivityViewController to store information that can be copied.
let activityViewController : UIActivityViewController
if let personaImage = self.personas[indexPath.row].image, let imageToShare = UIImage(data: personaImage as Data){
//in swift if lets are used to se if an opional has a value or not
activityViewController = UIActivityViewController(activityItems: ["With personia I am sharing my persona named " + self.personas[indexPath.row].name!, imageToShare, "Project name: " + self.personas[indexPath.row].projectName!, "Age: " + self.personas[indexPath.row].age!, "Location: " + self.personas[indexPath.row].location!, "Gender: " + self.personas[indexPath.row].gender!, "Disabilities: " + self.personas[indexPath.row].disability!, "Occupation: " + self.personas[indexPath.row].occupation!, "Bio: " + self.personas[indexPath.row].bio!, "goals: " + self.personas[indexPath.row].goals!, "Motivation One: " + self.personas[indexPath.row].motivation!, "Motivation Two: " + self.personas[indexPath.row].motivationTwo!, "Motivation Three: " + self.personas[indexPath.row].motivationThree!, "Frustration One: " + self.personas[indexPath.row].frustration!, "Frustration Two: " + self.personas[indexPath.row].frustrationTwo!, "Frustration Three: " + self.personas[indexPath.row].frustrationThree!], applicationActivities: nil)
} else {
activityViewController = UIActivityViewController(activityItems: ["With personia I am sharing my persona named " + self.personas[indexPath.row].name!], applicationActivities: nil)
}
self.present(activityViewController, animated: true, completion: nil)
if let popOver = activityViewController.popoverPresentationController {
popOver.sourceView = self.view
}
}
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
}
}
The ActivityViewController works as expected, but I'm getting a memory leak if I pass a URL as an activityItem. This file (archivedFile) is not needed other than for the export.
let fileName = "test.myuti"
let tempDirURL: URL = FileManager.default.temporaryDirectory
let fullWriteURL: URL = tempDirURL.appendingPathComponent(fileName)
let exportCode = ExportCode(mainObject: theExpObject, mainObTitle: theTitle)
let dicToArchive = exportCode.createFileToExport()
let archivedFile = NSKeyedArchiver.archivedData(withRootObject: dicToArchive)
do {
try archivedFile.write(to: fullWriteURL, options: [.atomic])
}
catch {
errAlrt("Could not create an export file")
}
let activityVC = UIActivityViewController(activityItems: [fullWriteURL], applicationActivities: nil)
activityVC.excludedActivityTypes = [.assignToContact, .postToFacebook, .postToTwitter, .addToReadingList, UIActivityType(rawValue: "com.apple.mobilenotes.SharingExtension"), UIActivityType(rawValue: "com.apple.reminders.RemindersEditorExtension"), .postToFlickr, .postToWeibo, .saveToCameraRoll, .copyToPasteboard, .openInIBooks, .print, .postToTencentWeibo]
let popCntrl = activityVC.popoverPresentationController
popCntrl?.permittedArrowDirections = .any
popCntrl?.sourceView = self.view
popCntrl?.sourceRect = CGRect(origin: CGPoint(x: 0,y :0), size: CGSize(width: 200, height: 200))
self.present(activityVC, animated: false, completion: nil)