IOS Swift sharing PDF documents with images - swift

I'm getting a weird behaviour when sharing a simple PDF document using Swift on IOS. Basicaly if I create it and share it to be printed the image it should contain is not included. If I first display it using a UIViewController and then share it it's fine. I just don't get why !
Here are the interresting parts of my code :
func getHtml() -> String {
// Create a HTML document to be printed
// Save data to file
let fileName = "noteImage.jpeg"
let pathToInvoiceHTMLTemplate = Bundle.main.path(forResource: "note", ofType: "html")
let tmpDirectoryURL = URL(fileURLWithPath: NSTemporaryDirectory())
let fileURL = tmpDirectoryURL.appendingPathComponent(fileName)
let mergedImages = getMergedImages()
//let pngImageData = UIImagePNGRepresentation(image)
let imageData = UIImageJPEGRepresentation(mergedImages, 1.0) // if you want to save as JPEG
try? imageData!.write(to: URL(fileURLWithPath: fileURL.path), options: [.atomic])
var htmlText = "<html><body><b>Problem Retrieving Note Template</b></body></html>"
do {
// Load the note HTML template code into a String variable.
htmlText = try String(contentsOfFile: pathToInvoiceHTMLTemplate!)
// Replace the variables in HTML.
htmlText = htmlText.replacingOccurrences(of: "__PROJECT_NAME__", with: projectName!)
htmlText = htmlText.replacingOccurrences(of: "__NOTE_NAME__", with: note!.name)
htmlText = htmlText.replacingOccurrences(of: "__NOTE_IMAGE__", with: "file:"+fileURL.path)
}
catch {
print("Unable to open and use HTML template files.")
}
return htmlText
}
func getPdf() -> NSMutableData {
// Create a PDF document from the HTML to be shared
// Format HTML
let fmt = UIMarkupTextPrintFormatter(markupText: getHtml())
// Assign print formatter to UIPrintPageRenderer
let render = UIPrintPageRenderer()
render.addPrintFormatter(fmt, startingAtPageAt: 0)
// Assign paperRect and printableRect
let page = CGRect(x: 0, y: 0, width: 595.2, height: 841.8) // A4, 72 dpi
let printable = page.insetBy(dx: 0, dy: 0)
render.setValue(NSValue(cgRect: page), forKey: "paperRect")
render.setValue(NSValue(cgRect: printable), forKey: "printableRect")
// Create PDF context and draw
let pdfData = NSMutableData()
UIGraphicsBeginPDFContextToData(pdfData, CGRect.zero, nil)
for i in 1...render.numberOfPages {
UIGraphicsBeginPDFPage();
let bounds = UIGraphicsGetPDFContextBounds()
render.drawPage(at: i - 1, in: bounds)
}
UIGraphicsEndPDFContext();
return pdfData
}
#IBAction func shareNote(_ sender: UIBarButtonItem) {
// Called in direct sharing
let firstActivityItem = "Text int the message"
let docToShare = getPdf()
let activityViewController : UIActivityViewController = UIActivityViewController(
activityItems: [firstActivityItem, docToShare], applicationActivities: nil)
// This lines is for the popover you need to show in iPad
activityViewController.popoverPresentationController?.barButtonItem = sender
// This line remove the arrow of the popover to show in iPad
activityViewController.popoverPresentationController?.permittedArrowDirections = UIPopoverArrowDirection()
activityViewController.popoverPresentationController?.sourceRect = CGRect(x: 150, y: 150, width: 0, height: 0)
// Anything you want to exclude
activityViewController.excludedActivityTypes = [
UIActivityType.postToWeibo,
UIActivityType.assignToContact,
UIActivityType.saveToCameraRoll,
UIActivityType.addToReadingList,
UIActivityType.postToFlickr,
UIActivityType.postToVimeo,
UIActivityType.postToTencentWeibo,
]
self.present(activityViewController, animated: true, completion: nil)
}
#IBAction func shareDocument(_ sender: UIBarButtonItem) {
// Called in the preview controller when the HTML is displayed
let firstActivityItem = "Text in the message"
let docToShare = getPdf()
let activityViewController : UIActivityViewController = UIActivityViewController(
activityItems: [firstActivityItem, docToShare], applicationActivities: nil)
// This lines is for the popover you need to show in iPad
activityViewController.popoverPresentationController?.barButtonItem = sender
// This line remove the arrow of the popover to show in iPad
activityViewController.popoverPresentationController?.permittedArrowDirections = UIPopoverArrowDirection()
activityViewController.popoverPresentationController?.sourceRect = CGRect(x: 150, y: 150, width: 0, height: 0)
// Anything you want to exclude
activityViewController.excludedActivityTypes = [
UIActivityType.postToWeibo,
UIActivityType.assignToContact,
UIActivityType.saveToCameraRoll,
UIActivityType.addToReadingList,
UIActivityType.postToFlickr,
UIActivityType.postToVimeo,
UIActivityType.postToTencentWeibo,
]
self.present(activityViewController, animated: true, completion: nil)
}
Does anybody have a clue ?

I found a workaround to my problem :
The only solution I found so far is to create a dummy UIWebView that I hide. I load the HTML in the UIWebView and then create the PDF. Here is my code :
override func viewDidLoad() {
super.viewDidLoad()
hiddenWebView.loadHTMLString(htmlText, baseURL: nil)
hiddenWebView.isHidden = true
}
override func viewDidAppear(_ animated: Bool) {
pdfDocument = getPdf()
let tmpDirectoryURL = URL(fileURLWithPath: NSTemporaryDirectory())
let fileURL = tmpDirectoryURL.appendingPathComponent("document.pdf")
pdfDocument.write(to: fileURL, atomically: false)
documentWebView.loadRequest(URLRequest(url: fileURL))
}

Related

How to share generated UIImage with siwft 4

Hi I use this function to create an UIImage of QRCode
func generateQRCode(string: String){
let data = string.data(using: String.Encoding.ascii)
if let filter = CIFilter(name: "CIQRCodeGenerator") {
filter.setValue(data, forKey: "inputMessage")
let transform = CGAffineTransform(scaleX: 3, y: 3)
if let output = filter.outputImage?.transformed(by: transform) {
imageQRCode.image = UIImage(ciImage: output)
qrImage = UIImage(ciImage: output)
self.tableView.reloadData()
}
}
}
After I have generated the image I want to save or print it.
I used this function
let shareText = NSLocalizedString("SHARE_QR_TITLE", comment: "")
if let image = qrImage {
let vc = UIActivityViewController(activityItems: [shareText, image], applicationActivities: [])
present(vc, animated: true)
}
but I can't share it. I received this error: "[ShareSheet] connection invalidate"
Try this it's work for me but not showing qrcode by sharing in whatsApp. Working fine with messages, mail, telegram..
import UIKit
class QRCodeGeneratorViewController: UIViewController {
#IBOutlet var qrImageView: UIImageView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
func generateQRCode(from string: String) -> UIImage? {
let data = string.data(using: String.Encoding.ascii)
if let filter = CIFilter(name: "CIQRCodeGenerator") {
filter.setValue(data, forKey: "inputMessage")
let transform = CGAffineTransform(scaleX: 3, y: 3)
if let output = filter.outputImage?.transformed(by: transform) {
return UIImage(ciImage: output)
}
}
return nil
}
#IBAction func generateQRAction(_ sender: Any) {
let image = generateQRCode(from: "iOS Developer")
qrImageView.image = image
}
#IBAction func btnShareClk(_ sender: Any) {
let shareText = "Hello, world!"
if let image = qrImageView.image {
let vc = UIActivityViewController(activityItems: [shareText, image], applicationActivities: [])
present(vc, animated: true)
vc.popoverPresentationController?.sourceView = self.qrImageView
}
}

How to give PDF Data a filename for user to save in Swift

I give my pdfData to user to save. He can save to files and make a file, but the default name of the pdf file is: PDF document.pdf. I want my own filename if this is possible. Perhaps I can change the filename within the pdfData before I give pdfData to UIActivityViewController?
Here is my code:
// Create page rect
let pageRect = CGRect(x: 0, y: 0, width: 595.28, height: 841.89) // A4, 72 dpi
// Create PDF context and draw
let pdfData = NSMutableData()
UIGraphicsBeginPDFContextToData(pdfData, pageRect, nil)
UIGraphicsBeginPDFPage()
// From here you can draw page, best make it in a function
PdfErstellung.PdfErstellen(auswahlZeilen, vitalstoffWerteListe, heuteString)
UIGraphicsEndPDFContext()
// Save pdf DATA through user
let activityViewController = UIActivityViewController(activityItems: [pdfData], applicationActivities: nil)
activityViewController.popoverPresentationController?.sourceView = self.view // für IPAD nötig
self.present(activityViewController, animated: true, completion: nil)
-- UPDATE --
My new idea ist, first try save the file and try a URL, and if this fail, then use the pdfData directly, because in some simulator use URL give no error and in other give error.
More here: https://stackoverflow.com/a/52499637/10392572
You just need to save your pdfData to a temporary fileURL and share that URL.
let temporaryFolder = FileManager.default.temporaryDirectory
let fileName = "document.pdf"
let temporaryFileURL = temporaryFolder.appendingPathComponent(fileName)
print(temporaryFileURL.path) // /Users/lsd/Library/Developer/XCPGDevices/E2003834-07AB-4833-B206-843DC0A52967/data/Containers/Data/Application/322D1F1D-4C97-474C-9040-FE5E740D38CF/tmp/document.pdf
do {
try pdfData.write(to: temporaryFileURL)
// your code
let activityViewController = UIActivityViewController(activityItems: [temporaryFileURL], applicationActivities: nil)
} catch {
print(error)
}
Note that you can also set a user title for AirDrop to be used as follows:
let temporaryFolder = FileManager.default.temporaryDirectory
let fileName = "Carburant Discount Historique des Prix au \(Date()).json"
let temporaryFileURL = temporaryFolder.appendingPathComponent(fileName)
print(temporaryFileURL.path)
do {
try? FileManager.default.removeItem(at: temporaryFileURL)
try json.write(to: temporaryFileURL)
}
catch let error {
print("\(#function): *** Error while writing json to temporary file. \(error.localizedDescription)")
alert("Export impossible")
return
}
/// Sets the **title** along with the URL
let dataToShare: [Any] = ["Historique des prix", temporaryFileURL]
let activityViewController = UIActivityViewController(activityItems: dataToShare, applicationActivities: nil)

print view controller [duplicate]

I am developing an app which requires visitor passes to be generated and printed directly from an iPad over AirPrint.
I have looked everywhere to find out how to print a view but I can only find how to print text, webKit and mapKit.
Is there a way of printing an entire view? If not, what would be a good solution to print a visitor pass which will be plain text, boxes and a photograph. Thanks.
I have found the answer to my question by modifying the code found here: AirPrint contents of a UIView
//create an extension to covert the view to an image
extension UIView {
func toImage() -> UIImage {
UIGraphicsBeginImageContextWithOptions(bounds.size, false, UIScreen.mainScreen().scale)
drawViewHierarchyInRect(self.bounds, afterScreenUpdates: true)
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return image
}
}
//In your view controller
#IBAction func printButton(sender: AnyObject) {
let printInfo = UIPrintInfo(dictionary:nil)
printInfo.outputType = UIPrintInfoOutputType.General
printInfo.jobName = "My Print Job"
// Set up print controller
let printController = UIPrintInteractionController.sharedPrintController()
printController.printInfo = printInfo
// Assign a UIImage version of my UIView as a printing iten
printController.printingItem = self.view.toImage()
// If you want to specify a printer
guard let printerURL = URL(string: "Your printer URL here, e.g. ipps://HPDC4A3E0DE24A.local.:443/ipp/print") else { return }
guard let currentPrinter = UIPrinter(url: printerURL) else { return }
printController.print(to: currentPrinter, completionHandler: nil)
// Do it
printController.presentFromRect(self.view.frame, inView: self.view, animated: true, completionHandler: nil)
}
I think you have to look print photo sample code with Swift:
https://developer.apple.com/library/ios/samplecode/PrintPhoto/Introduction/Intro.html
What exactly is your view, imageView or UIView? If you are interested in imageView or UIImage, Print Photo sample from Apple is for you. If your subject is UIView you can create pdf context from view.layers and send to AirPrint func like WebKit, text or you can print to create pdf data.
The best solution is Create Pdf file is in here for swift
Generate PDF with Swift
Print pdf file is for swift implementation:
var pdfLoc = NSURL(fileURLWithPath:yourPdfFilePath)
let printController = UIPrintInteractionController.sharedPrintController()!
let printInfo = UIPrintInfo(dictionary:nil)!
printInfo.outputType = UIPrintInfoOutputType.General
printInfo.jobName = "print Job"
printController.printInfo = printInfo
printController.printingItem = pdfLoc
printController.presentFromBarButtonItem(printButton, animated: true, completionHandler: nil)
Swift 5:
let info = UIPrintInfo(dictionary:nil)
info.outputType = UIPrintInfo.OutputType.general
info.jobName = "Printing"
let vc = UIPrintInteractionController.shared
vc.printInfo = info
vc.printingItem = UIImage.image(fromView: self.view) // your view here
vc.present(from: self.view.frame, in: self.view, animated: true, completionHandler: nil)
extension UIImage {
/// Get image from given view
///
/// - Parameter view: the view
/// - Returns: UIImage
public class func image(fromView view: UIView) -> UIImage {
UIGraphicsBeginImageContextWithOptions(view.frame.size, false, 0)
view.drawHierarchy(in: view.bounds, afterScreenUpdates: false)
let image: UIImage = UIGraphicsGetImageFromCurrentImageContext()!
UIGraphicsEndImageContext()
return image
}
}
Hier in Swift 3.x
func prt() {
let printInfo = UIPrintInfo(dictionary:nil)
printInfo.outputType = UIPrintInfoOutputType.general
printInfo.jobName = "My Print Job"
// Set up print controller
let printController = UIPrintInteractionController.shared
printController.printInfo = printInfo
// Assign a UIImage version of my UIView as a printing iten
printController.printingItem = self.view.toImage()
// Do it
printController.present(from: self.view.frame, in: self.view, animated: true, completionHandler: nil)
}
}
extension UIView {
func toImage() -> UIImage {
UIGraphicsBeginImageContextWithOptions(bounds.size, false, UIScreen.main.scale)
drawHierarchy(in: self.bounds, afterScreenUpdates: true)
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return image!
}
}

open PDF URL in iOS 8?

I have write this code to show pdf using UIDocumentInteractionController.But,I don't know how to search pdf at local directory and open in iOS 8 and below..Any help?
let filename = history.invoiceLongDate // 01223642
if !filename.isEmpty{
let paths = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)
let docs = paths[0]
let pathURL = NSURL(fileURLWithPath: docs, isDirectory: true)
if #available(iOS 9.0, *) {
let fileURL = NSURL(fileURLWithPath: "\(filename)_my_invoice.pdf", isDirectory: false, relativeToURL: pathURL)
self.docController = UIDocumentInteractionController(URL: fileURL)
self.docController?.delegate = self
self.docController?.presentOptionsMenuFromRect(sender.frame, inView: self.view, animated: true)
} else {
// Fallback on earlier versions
// Any Help with that?
}
}
You can view PDF in iOS 8 by using webview. Try below code,
if let pdf = NSBundle.mainBundle().URLForResource("myPDF", withExtension: "pdf", subdirectory: nil, localization: nil) {
let req = NSURLRequest(URL: pdf)
let webView = UIWebView(frame: CGRectMake(0,0,self.view.frame.size.width,self.view.frame.size.height))
webView.loadRequest(req)
self.view.addSubview(webView)
}
OR
if let baseUrl = NSURL.fileURLWithPath(pathURL) {
let fileURL = baseUrl.URLByAppendingPathComponent(NFConstants.NFCoreDataStringIdentifiers.CoreDataStoresPathComponent.rawValue)
}
Hope this will be helpful to you.
UIDocumentInteractionController is available with (iOS 3.2, *).
For Viewing PDF file:
var documentInteractionController: UIDocumentInteractionController!
#IBAction func openDocument(sender: UIButton) {
let URL: NSURL = NSBundle.mainBundle().URLForResource("pdf-sample", withExtension: "pdf")!
if (URL != "") {
// Initialize Document Interaction Controller
self.documentInteractionController = UIDocumentInteractionController(URL: URL)
// Configure Document Interaction Controller
self.documentInteractionController.delegate = self
// Present Open In Menu
self.documentInteractionController.presentOptionsMenuFromRect(sender.frame, inView: self.view, animated: true)
//presentOpenInMenuFromRect(button.frame, inView: self.view, animated: true)
}
}
// MARK: UIDocumentInteractionControllerDelegate
func documentInteractionControllerViewControllerForPreview(controller: UIDocumentInteractionController) -> UIViewController {
return self
}

Add Instagram to UIActivityViewController

I'm trying to share an image using standard UIActivityViewController, it's fine to share on Facebook, Twitter and Save Image using this code:
let firstActivityItem = "foo text"
let secondActivityItem : UIImage = image!
let activityViewController : UIActivityViewController = UIActivityViewController(
activityItems: [firstActivityItem, secondActivityItem], applicationActivities: nil)
activityViewController.excludedActivityTypes = [
UIActivityTypePostToWeibo,
UIActivityTypePrint,
UIActivityTypeAssignToContact,
UIActivityTypeAddToReadingList,
UIActivityTypePostToVimeo,
UIActivityTypePostToTencentWeibo
]
self.presentViewController(activityViewController, animated: true, completion: nil)
I need one more thing, Instagram:
If UIApplication.sharedApplication().canOpenURL(instagramURL!) {
// Success
var img = image!
var savePath: String = NSHomeDirectory().stringByAppendingPathComponent("Documents/Test.igo")
UIImageJPEGRepresentation(img, 1).writeToFile(savePath, atomically: true)
var imgURL = NSURL(string: NSString(format: "file://%#", savePath) as! String)
docController = UIDocumentInteractionController(URL: imgURL!) // 1
docController.UTI = "com.instagram.exclusivegram" // 2
docController.delegate = self
docController.annotation = ["InstagramCaption":"testsss"] // 3
docController.presentOpenInMenuFromRect(self.view.frame, inView: self.view, animated: true) // 4
} else {
// Error
}
Both these codes work fine separately, how can I add Instagram to the UIActivityViewController? Is it possible at all?
I think it would be very easier to add other social shares to the code you wrote for Instagram. The ".igo" extension is exclusive for Instagram so other apps will not support it. Just change this extension from ".igo" to ".ig" and other apps will read it:
var savePath: String = NSHomeDirectory().stringByAppendingPathComponent("Documents/Test.ig")
But Instagram also have an exclusive UTI to avoiding other apps to appear in the same Document Interaction View. So you will also need to change it from "exclusivegram" to "photo":
docController.UTI = "com.instagram.photo"
I have an app with a similar functionality and this is my original code:
#IBAction func shareOnIntagram(sender: UIButton) {
let finalImage: UIImage = UIImage.imageWithView(photoView)
let instagramURL = NSURL(string: "instagram://app")
if (UIApplication.sharedApplication().canOpenURL(instagramURL!)) {
let imageData = UIImageJPEGRepresentation(finalImage, 1)
let captionString = "caption"
let writePath = (NSTemporaryDirectory() as NSString).stringByAppendingPathComponent("instagram.ig")
if imageData?.writeToFile(writePath, atomically: true) == false {
return
} else {
let fileURL = NSURL(fileURLWithPath: writePath)
self.documentController = UIDocumentInteractionController(URL: fileURL)
self.documentController.delegate = self
self.documentController.UTI = "com.instagram.photo"
self.documentController.annotation = NSDictionary(object: captionString, forKey: "InstagramCaption")
self.documentController.presentOpenInMenuFromRect(self.view.frame, inView: self.view, animated: true)
}
} else {
print(" Instagram is not installed ")
}
}
To make this code work, don't forget to add UIDocumentInteractionControllerDelegate in the UIViewController class.
It seems it's not possible, because of .igo extension which is needed by Instagram.