Xcode 8: Automation Instrument Removed

November 14th, 2016

Filed under: Instruments, Xcode | Be the first to comment!

Apple removed the Automation instrument from Instruments in Xcode 8. Use Xcode’s user interface testing to test your iOS app’s user interface.

Facebooktwittergoogle_plusredditmail

Xcode 8: Address Sanitizer Supports Swift

September 16th, 2016

Filed under: Xcode | Be the first to comment!

I wrote about Xcode’s Address Sanitizer in my Xcode 7: Address Sanitizer post. Apple added Swift support to Address Sanitizer in Xcode 8.

Facebooktwittergoogle_plusredditmail

Xcode 8: New Project and New File Assistant Changes

September 14th, 2016

Filed under: Xcode | Be the first to comment!

Xcode 8 changes the look of the assistant windows for creating new projects and new files. The following screenshot shows the New Project Assistant:

Xcode8NewProjectAssistant

The following screenshot shows the New File Assistant:

Xcode8NewFileAssistant

Select an operating system at the top of the assistant to view the available templates for that operating system. If you look at the New Project Assistant screenshot, you will see a Cross-platform option at the top.

Xcode8CrossPlatformProjects

The external build system project template has moved to the Cross-platform section. There’s also a project template for making a cross-platform SpriteKit game.

Facebooktwittergoogle_plusredditmail

Simple Ways to Customize NSSavePanel

September 8th, 2016

Filed under: Cocoa | Be the first to comment!

Cocoa provides the NSSavePanel class to open a save panel when the user wants to save a document. Save panels are initially configured for saving. The default button’s name is Save, and the panel’s title is also Save.

Suppose your application exports documents to various file formats. Exporting is similar to saving so you would open a save panel. When exporting you would like the panel title and the default button’s name to be Export instead of Save. How do you make these changes?

NSSavePanel provides properties to customize the various fields in the panel. The properties you are most likely to change are prompt, title, and nameFieldLabel.

prompt

The prompt property stores the default button’s text. To change the button text from Save to Export, add the following Swift code:

let savePanel = NSSavePanel()
savePanel.prompt = "Export"

title

The title property stores the name of the panel title. To change the title to Export, add the following Swift code:

savePanel.title = "Export"

nameFieldLabel

The nameFieldLabel stores the label for the text field where the user enters the file name. To change the label from Save As: to Export As:, add the following Swift code:

savePanel.nameFieldLabel = "Export As:"
Facebooktwittergoogle_plusredditmail

Creating PDFs with Core Text and Quartz

August 28th, 2016

Filed under: Mac Development | Be the first to comment!

I’m working on a project where I need to create PDF files for long documents. When I searched online for information on Core Text and creating PDF files, I didn’t find much so I’m sharing what I’ve learned. I have not tried creating PDFs on iOS, but most of this material should apply to iOS as Core Text and Quartz are available on iOS.

One word of warning about this article: the code samples are for illustration only. If you copy and paste them into Xcode, they probably will not work. If you are looking for code to paste into your projects, stop reading now. This article shows the functions you need to call, but you have to write the code.

The code samples are written in Swift 3. If you are using Swift 2, you’ll get syntax errors. Swift 3 is in a state of flux so you might get errors in Swift 3 too. Xcode’s code completion should show you the proper function names and arguments. Pay attention to the data structures you need to know and the functions you need to call. They’re the most important material in this article.

Frameworks

Core Text and Quartz are not part of the Cocoa framework so you have to import them in your code.

import CoreText
import Quartz

Data Structures

To create PDF files with Core Text and Quartz, you need to know the following data structures: CGContext, CTFramesetter, and CTFrame. CGContext is a Quartz context (Quartz is also called Core Graphics in Apple’s documentation. That’s where the CG comes from) where you draw the PDF content.

CTFramesetter is a Core Text framesetter. You will have at least one framesetter for each page in your PDF. The framesetter’s job is to create a frame. CTFrame is a Core Text frame. You will draw one or more frames for each page. You need one frame for the main page content. If you want to draw any content in a page header or footer, you must create additional frames. A common reason to create an additional frame is to draw page numbers.

Coding Tasks

Creating a PDF file involves the following tasks:

  1. Create a URL for the PDF file.
  2. Create a context to draw the PDF content.
  3. Create a framesetter.
  4. Create a frame.
  5. Draw the frame in the context.
  6. Repeat Steps 3-5 until you run out of text to draw.
  7. Save the PDF file.

Create the URL

Every PDF you create requires a URL for the PDF file. In Mac applications you normally get the URL by opening a Save panel. You can also use the NSURL class to manually create a URL.

Create the Context

Call the function CGPDFContextCreateWithURL() to create a context for drawing PDF content. Fortunately in Swift all you have to do is create a CGContext object. The function to create a CGContext takes three arguments. The first argument is the URL for the PDF file. The second argument is a media box, which is a rectangle that specifies the PDF bounds. If you pass nil, Quartz assumes the page size is the standard paper size, 8.5 by 11 inches. The final argument is an optional dictionary that specifies additional information for the PDF context to use when creating the PDF file. Examples of the additional information include the title and author of the document.

let pdfContext = CGContext(fileURL, mediaBox: nil, nil)

If you use a custom page rectangle in Swift, you must make the rectangle variable a var instead of a let and pass the address of the variable using the & character like you would do in C or Objective-C. The following code uses a 6 by 9 inch page size:

var pageBox = CGRect(x: 0.0, y: 0.0, width: 432.0, height: 648.0)
let pdfContext = CGContext(fileURL, mediaBox: &pageBox, nil)

Quartz uses points as its unit of measurement. There are 72 points per inch.

Create the Framesetter

Call the function CTFramesetterCreateWithAttributedString() to create a framesetter. Supply an attributed string containing the text for the PDF.

let framesetter = CTFramesetterCreateWithAttributedString(text)

Create the Frame

Core Text has a handy function to determine how much text can fit in a page frame: CTFramesetterSuggestFrameSizeWithConstraints(). This function takes five arguments. The first argument is the framesetter you created. The second argument is the range of the text being laid out. This range is initially the range of the document text. As you draw pages of text, update the range’s starting location so you don’t draw the same content more than once.

The third argument is an optional dictionary of attributes. The fourth argument is the page size. Remember to factor in page margins when calculating the page size. The final argument is the range of text that fits on the page. CTFramesetterSuggestFrameSizeWithConstraints() returns its result in the final argument.

var pageRange = CFRange()
CTFramesetterSuggestFrameSizeWithConstraints(framesetter, textRange, nil, pageSize, &pageRange)

Call the function CTFramesetterCreateFrame() to create the frame. This function takes four arguments. The first argument is the framesetter. The second argument is the range that CTFramesetterSuggestFrameSizeWithConstraints() returns. The third argument is a path for drawing. You can create a CGPath from a rectangle and a nil transform. The final argument is another optional dictionary of attributes. The following code creates a frame for a standard 8.5 by 11 inch page with one inch margins:

let pageRect = CGRect(x: 72.0, y: 72.0, width: 468.0, height: 648.0)
let framePath = CGPath(rect: pageRect, transform: nil)
let frame = CTFramesetterCreateFrame(framesetter, pageRange, framePath, nil)

Draw the Page

Now you can draw the page. Call the function beginPDFPage() to begin drawing in a PDF page. Call the function CTFrameDraw() to draw a frame. Supply the frame and the context. Call the function endPDFPage() to finish drawing.

pdfContext?.beginPDFPage(nil)
CTFrameDraw(frame, pdfContext?)
pdfContext?.endPDFPage()

If you have multiple frames to draw in a page, add more CTFrameDraw() calls. Make sure the calls are between the calls to beginPDFPage() and endPDFPage().

Save the PDF

At this point you have created a PDF. Now you have to save it. Close the PDF context to save the file by calling the function closePDF().

pdfContext?.closePDF()

Additional Reading

To learn more about Quartz and Core Text, read Quartz 2D Programming Guide and Core Text Programming Guide. Both documents are part of Apple’s documentation. Quartz 2D Programming Guide will show you how to draw things besides text with Quartz.

Facebooktwittergoogle_plusredditmail