How to use C library in Xcode Project? - ios

The library is written in C https://github.com/jmcnamara/libxlsxwriter
The pod file that I am using is
pod 'libxlsxwriter', '0.3.1'
I import the header into the Bridging Header
#import <libxlsxwriter/xlsxwriter.h>
I am getting this
'libxlsxwriter/xlsxwriter.h' file not found

It is actually quite simple, yet poorly documented (IMHO):
Install the library with CocoaPods:
Add pod 'libxlsxwriter', '~> 0.9' to your pod file.
Run pod install
You do not need a Swift-Bridging-Header, simply use the library like so:
import UIKit
import xlsxwriter
...
override func viewDidLoad() {
super.viewDidLoad()
createExcelFile()
}
override func viewWillAppear(_ animated: Bool) {
}
/*#objc*/ func createExcelFile(){
// Create a new workbook.
let documentDirectory = try! FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor:nil, create:false)
let fileURL = documentDirectory.appendingPathComponent("demo.xlsx")
//Ditch first 6 characters, because they are of the form file://
let workbook = workbook_new((fileURL.absoluteString.dropFirst(6) as NSString).fileSystemRepresentation)
// Add a worksheet with a user defined sheet name.
let worksheet1 = workbook_add_worksheet(workbook, "Demo")
// Add a worksheet with Excel's default sheet name: Sheet2.
let worksheet2 = workbook_add_worksheet(workbook, nil)
// Add some cell formats.
let myformat1 = workbook_add_format(workbook)
let myformat2 = workbook_add_format(workbook)
// Set the bold property for the first format.
format_set_bold(myformat1)
// Set a number format for the second format.
format_set_num_format(myformat2, "$#,##0.00")
// Widen the first column to make the text clearer.
worksheet_set_column(worksheet1, 0, 0, 20, nil)
// Write some unformatted data.
worksheet_write_string(worksheet1, 0, 0, "Peach", nil)
worksheet_write_string(worksheet1, 1, 0, "Plum", nil)
// Write formatted data.
worksheet_write_string(worksheet1, 2, 0, "Pear", myformat1)
// Formats can be reused.
worksheet_write_string(worksheet1, 3, 0, "Persimmon", myformat1)
// Write some numbers.
worksheet_write_number(worksheet1, 5, 0, 123, nil)
worksheet_write_number(worksheet1, 6, 0, 4567.555, myformat2)
// Write to the second worksheet.
worksheet_write_string(worksheet2, 0, 0, "Some text", myformat1)
// Close the workbook, save the file and free any memory
workbook_close(workbook)
loadingSpinner.stopAnimating()
self.shareExcelFile(filepath: fileURL)
}
func shareExcelFile(filepath: URL){
//Share the newly created file
var filesToShare = [Any]()
filesToShare.append(filepath)
let activityViewController = UIActivityViewController(activityItems: filesToShare, applicationActivities: [])
activityViewController.popoverPresentationController?.sourceView = self.view
activityViewController.popoverPresentationController?.sourceRect = CGRect(x: self.view.bounds.maxX, y: self.view.bounds.minY, width: 0, height: 0)
activityViewController.popoverPresentationController?.permittedArrowDirections = UIPopoverArrowDirection.init(rawValue: 0)
self.present(activityViewController, animated: true)
activityViewController.completionWithItemsHandler = { activity, completed, items, error in
if !completed {
// handle task not completed
return
}
self.dismiss(animated: false, completion: nil)
}
}
}
This should create an example .xlsx file, save it to the ~Documents directory and share it.
I did not test the code, so expect needing to change some bits.
I will setup a demo repository, once I get the time to do it...

Related

Swift iOS - Overlay text onto PDF with PDFKit and UI

I'm working in Swift 5 and on iOS. I'm trying to overlay text onto a current PDF I have. I'm essentially porting code I made from an app for macOS. This is the code from the Mac version:
func executeContext(at srcURL: URL, to dstURL: URL) {
// Confirm there is a document there
if let doc: PDFDocument = PDFDocument(url: srcURL) {
// Create a document, get the first page, and set the size of the page
let page: PDFPage = doc.page(at: 0)!
var mediaBox: CGRect = CGRect(x: 0, y: 0, width: 792, height: 612)
// This is where the magic happens. Create the drawing context on the PDF
let context = CGContext(dstURL as CFURL, mediaBox: &mediaBox, nil)
let graphicsContext = NSGraphicsContext(cgContext: context!, flipped: false)
NSGraphicsContext.current = graphicsContext
context!.beginPDFPage(nil)
// Draws the PDF into the context
page.draw(with: .mediaBox, to: context!)
// Parse and Draw Text on the context
//drawText()
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)
context!.saveGState()
context!.restoreGState()
context!.endPDFPage()
NSGraphicsContext.current = nil
context?.closePDF()
}
}
The drawText() function did most of the text overlaying that was needed, but I put another "draw "method below it to test it out.
I'm understandably getting an error Cannot find 'NSGraphicsContext' in scope since NSGraphicsContext doesn't exist on iOS. I've tried to find an equivalent translation with UIGraphicsPDFRenderer or UIGraphicsBeginPDFContextToData, and using some code from a Ray Wenderlich tutorial, I was able to create a new PDF and place text on it with the below code:
func createDocument(url: URL) -> Data {
//let pdfData = try? Data.init(contentsOf: url)
// 1
let pdfMetaData = [
kCGPDFContextCreator: "Timecard App",
kCGPDFContextAuthor: "Timecard App"
]
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
}
...but I couldn't find a way to load in current PDF "data" to the renderer and then draw from there. Does anyone have any suggestions on the proper way to do this?
Here is possible solution - actually you just need to operate with CoreGraphics context directly, set current, flip transform, etc. (style and conventions of original code preserved).
Tested with Xcode 12 / iOS 14.
func executeContext(at srcURL: URL, to dstURL: URL) {
// Confirm there is a document there
if let doc: PDFDocument = PDFDocument(url: srcURL) {
// Create a document, get the first page, and set the size of the page
let page: PDFPage = doc.page(at: 0)!
var mediaBox: CGRect = page.bounds(for: .mediaBox)
// This is where the magic happens. Create the drawing context on the PDF
let context = CGContext(dstURL as CFURL, mediaBox: &mediaBox, nil)
UIGraphicsPushContext(context!)
context!.beginPDFPage(nil)
// Draws the PDF into the context
page.draw(with: .mediaBox, to: context!)
let flipVertical: CGAffineTransform = CGAffineTransform(a: 1, b: 0, c: 0, d: -1, tx: 0, ty: mediaBox.size.height)
context!.concatenate(flipVertical)
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)
context!.endPDFPage()
context?.closePDF()
UIGraphicsPopContext()
}
}
Edit additional pages using the following function
// add a new page
func addPage(number: Int){
// index is one less than document page number
let index = number - 1
context.endPDFPage()
if let page = document.page(at: index) {
context.beginPDFPage(nil)
page.draw(with: .mediaBox, to: context)
context.concatenate(flipVertical)
}
}
Where document is the PDF document you wish to edit.
Then start editing that new page. The X and Y coordinates reset to 0,0 again for the new page.

How to Convert GMUFeature to GMSPath

I have drawn slots on google maps using Geojson. Now when the user long presses on the slot should ask the user whether to save the location or not. But I don't know how to enable long press only for the slots. Could anybody suggest me how to do this. Below is the code I have used.
`// the below is the code to render the slots on the google maps
let geoJsonParser = GMUGeoJSONParser(url: url!)
geoJsonParser.parse()
let renderer = GMUGeometryRenderer(map: self.mapview, geometries: geoJsonParser.features)
let style = GMUStyle(styleID: "random", stroke: UIColor.black, fill: UIColor.green, width: 2, scale: 1, heading: 0, anchor: CGPoint(x: 0, y: 0), iconUrl: nil, title: nil, hasFill: true, hasStroke: true)
for feature in geoJsonParser.features {
feature.style = style
}
renderer.render()
// The below code is to check whether the user has long pressed on the slot or not(written this code inside the didLongPressAt function).
for feature in geoJsonParser.features {
if GMSGeometryContainsLocation(coordinate, feature as! GMSPath, true) {// the app is crashing with an error in the console "Could not cast value of type 'GMUFeature' (0x1033573d0) to 'GMSPath' (0x1033585a0)"
print("YES: you are in this polygon.")
marker.title = "\(coordinate)"
marker.map = mapview
} else {
print("You do not appear to be in this polygon.")
}
}`
Finally, after lot of efforts I found the solution.
Note: If anyone is integrating Google-Maps-iOS-Utils library, then don't add header files in Bridging-Header file as suggested in Google developer document.
Wherever you are using the below code, just write import GoogleMapsUtils at top.
let path = Bundle.main.path(forResource: "dld_areas", ofType: "json")
let url = URL(fileURLWithPath: path!)
let data: Data = try! Data.init(contentsOf: url)
let json = try JSON(data: data)
//our json is having properties containing area name and area id, bacause the properties field inside the GMUFeature is retuning us nil, we have to pass it indiviually
let jsonFeatures: [JSON] = json["features"].array ?? []
geoJsonParser = GMUGeoJSONParser.init(data: data)
geoJsonParser.parse()
var index = 0
for feature in geoJsonParser.features {
var innerFeature = feature
while(innerFeature.geometry.type != "GeometryCollection"){
print("type is" + innerFeature.geometry.type)
innerFeature = innerFeature.geometry as! GMUGeometryContainer
}
let collection = innerFeature.geometry as! GMUGeometryCollection
let polygon:GMUPolygon = collection.geometries.first as! GMUPolygon
var isAreaObtained = false
for path in polygon.paths{
//check if the coordinates lie within the polygon
if (GMSGeometryContainsLocation(coordinates, path, true)) {
isAreaObtained = true
print(jsonFeatures[index]["properties"]["NAME_EN"])
print(jsonFeatures[index]["properties"]["AREA_ID"])
print("location inside polygon")
break;
}
}
if(isAreaObtained){
break
}
index = index + 1
}
I found the above code in the following link:
https://github.com/googlemaps/google-maps-ios-utils/issues/205
I have used this code and modified as per my requirement.

PdfKit highlight annotation

I'm trying to add highlight annotations to documents using PDFKit on iOS.
let highlight = PDFAnnotation(bounds: selection.bounds(for: page),
forType: PDFAnnotationSubtype.highlight,
withProperties: nil)
highlight.color = color
page.addAnnotation(highlight)
page.displaysAnnotations = true
When adding them using the code above, they appear as two differently shaped layers. When saving them to the PDF file and reopening it they display correctly.
In this screen capture
The top and bottom highlights have been added the same way using the snippet provided here. The top one has been saved to the pdf document and appears as expected when reopening it, the bottom one has just been added.
Does anyone know how to have them display correctly (i.e. like the top one) without resorting to saving and reopening the file?
So this is a know bug in 10.13. There is a workaround by scrolling the page away and then back to the highlight
You can create the highlight using this code:
let page = self.pdfDocument?.page(at: 10)
let bounds = CGRect(x: 85.8660965, y: 786.8891167, width: 298.41, height: 12.1485)
let annotation = PDFAnnotation(bounds: bounds, forType: .highlight, withProperties: nil)
annotation.color = NSColor.blue
page?.addAnnotation(annotation)
Then you need to scroll away from the page and back to the highlight
func annotationScrollHack(page: PDFPage) {
guard let pdfDocument = self.pdfDocument else { return }
//When adding highlights to macOS 10.13 it seems like 2 highlights are added.
//If you scroll to a different page and back the "extra" highlight is removed
//This function scrolls to the first/last page in the book and then back to the current page
//rdar://34784917
let bookScrollView = self.pdfView.documentView?.enclosingScrollView
let currentVisibleRect = bookScrollView?.contentView.documentVisibleRect
if (0 ... 3).contains(pdfDocument.index(for: page)) {
if self.pdfView.canGoToLastPage {
self.pdfView.goToLastPage(self)
}
} else {
if self.pdfView.canGoToFirstPage {
self.pdfView.goToFirstPage(self)
}
}
if let currentVisibleRect = currentVisibleRect {
bookScrollView?.contentView.scroll(to: CGPoint(x: currentVisibleRect.origin.x, y: currentVisibleRect.origin.y))
}
}
Response from Apple:
There is no workaround other than the “hack" they described: scrolling
away then back is the best option. Changing zoom factor and reverting
it might fix it in the same way, but not guaranteed.
I've ended up with this hack method which takes into consideration all cases in my opinion. Hope that will help.
private func hackScroll(to page: PDFPage) {
switch (pdfView.canGoToFirstPage(), pdfView.canGoToLastPage()) {
case (true, false):
pdfView.goToFirstPage(nil)
pdfView.go(to: page)
case (false, true):
pdfView.goToLastPage(nil)
pdfView.go(to: page)
case (false, false):
guard let dir = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first else { return }
let file = "temp.pdf"
/// Save to temp file
let fileURL = dir.appendingPathComponent(file)
pdfView.document?.write(to: fileURL)
/// Read from temp file
guard let document = PDFDocument(url: fileURL) else { return }
pdfView.document = document
pdfView.autoScales = true
default:
pdfView.goToFirstPage(nil)
pdfView.go(to: page)
}
}

Swift 3, Display some lines of the HTML text

I try to add html code inside webView:
webView.loadHTMLString(myHTML, baseURL: nil)
But last line displays cropped:
What is the best way to hide this cropped bottom line in my webView?
I recommend you too use UILabel
Full example
ViewController.swift
import UIKit
class ViewController: UIViewController {
var label: UILabel?
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = UIColor.lightGray
var bounds = view.bounds
bounds.origin.y = 44
bounds.size.height = 100
label = UILabel(frame: bounds)
label?.backgroundColor = UIColor.white
view.addSubview(label!)
let filename = "HTMLPage.html"
if let path = Bundle.main.path(forResource: filename, ofType: nil) {
do {
let text = try String(contentsOfFile: path, encoding: String.Encoding.utf8)
label?.attributedText = text.htmlToAttributedString
label?.numberOfLines = 5
label?.lineBreakMode = .byTruncatingTail
} catch {
print("Failed to read text from \(filename)")
}
} else {
print("Failed to load file from app bundle \(filename)")
}
}
}
extension String {
var htmlToAttributedString: NSAttributedString? {
do {
let data = self.data(using: String.Encoding.utf8, allowLossyConversion: true)
if let d = data {
let str = try NSAttributedString(data: d,
options: [NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType],
documentAttributes: nil)
return str
}
} catch {
}
return nil
}
}
HTMLPage.html
<div style="font-family:'Helvetica'; font-size: 11pt;"><div class="feed-description"><p>The apple tree (Malus pumila, commonly and erroneously called Malus domestica) is a deciduous tree in the rose family best known for its sweet, pomaceous fruit, the apple. link It is cultivated worldwide as a fruit tree, and is the most widely grown species in the genus Malus. The tree originated in Central Asia, where its wild ancestor, Malus sieversii, is still found today. Apples have been grown for thousands of years in Asia and Europe, and were brought to North America by European colonists. Apples have religious and mythological significance in many cultures, including Norse, Greek and European Christian traditions. link text text text</p></div></div>
Result

Cannot Find Pdf File being created in Swift XCode

Iv used the following Code from Tom to create a Pdf File and
PDF Generation Swift
im having the same problem finding the document I created using Tom's code. Iv search my Mac HD and still no document and the programme does not crash. And when i look at the log it shows it has been stored at the following path
pdfPathWithFileName String "/Users/cleota/Library/Developer/CoreSimulator/Devices/B72A97D0-339E-4E79-AED6-4991D3B7C4B7/data/Containers/Data/Application/7A7789C4-27C5-45FB-8604-E36865609764/Documents/Ezy Essay Test.pdf"
but when I go there these folders(Library/Developer/CoreSimulator/Devices)are not there where it says it has been stored. Thoughts please?
#IBAction func createPdfDocument(sender: AnyObject) {
let fileName: String = "Taumaoe.pdf"
let path:NSArray = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)
let documentDirectory: AnyObject = path.objectAtIndex(0)
let pdfPathWithFileName = documentDirectory.stringByAppendingPathComponent(fileName as String)
print(pdfPathWithFileName)
generatePdf(pdfPathWithFileName)
}
func generatePdf(filepath: String){
UIGraphicsBeginPDFContextToFile(filepath, CGRectZero, nil)
UIGraphicsBeginPDFPageWithInfo(CGRectMake(0, 0, 850, 1100), nil)
drawBackground()
}
func drawBackground(){
var context:CGContextRef = UIGraphicsGetCurrentContext()!
// var rect:CGRect = CGRectMake(0, 0, CGSize.height, CGSize.width)
//CGContextSetFillColorWithColor(context, UIColor.greenColor().CGColor)
//CGContextFillRect(context, rect)
var testView = UIView (frame: CGRectMake(0,0,850,1100))
testView.backgroundColor = UIColor.yellowColor()
var label = UILabel (frame: CGRectMake(10,10,100,20))
label.textColor = UIColor.redColor()
label.backgroundColor = UIColor.lightGrayColor()
label.text = "I'am a test label"
testView.addSubview(label)
testView.layer.renderInContext(context)
UIGraphicsEndPDFContext()
//Title of 1st option of MainController
let sexyKeyWordscontroller = UIAlertController(title: "Ezy Essay Saved!", message: "Well done! Your Ezy Essay Pdf document has been saved at the following path: ", preferredStyle: UIAlertControllerStyle.Alert)
let closeButton : UIAlertAction = UIAlertAction(title: "Close",
style: UIAlertActionStyle.Cancel,
handler: {
(alert: UIAlertAction!) in sexyKeyWordscontroller.dismissViewControllerAnimated(true,
completion: nil)
})
sexyKeyWordscontroller.addAction(closeButton)
self.presentViewController(sexyKeyWordscontroller, animated: true, completion: nil)
}
Thanks
If you print out pdfPathWithFileName in the button event you will get the file path. When you have the file path open a finder window and in the file menu click: Go > Go to folder
The path that you have printed out will look something like this:
/Users/USERNAME/Library/Developer/CoreSimulator/Devices/...
Copy this into the Go to folder from the path
~/Library/Developer/CoreSimulator/Devices/...
And of course skip the last "/xp.pdf" in the path if you only want to open the location of the folder and not the file itself.

Resources