Archive for the 'Cocoa' Category

New service: Make Obj-C Accessors

Thursday, April 19th, 2007

A pair of services, actually: One to generate declarations, and the other to generate definitions. Simply select a run of instance variable declarations (ideally copied and pasted into the area where you want the methods to go), then invoke the service. Couldn’t be simpler—at least until Obj-C 2.0 arrives.

Why Mac programmers should learn PostScript

Saturday, April 7th, 2007

I’ll follow this up with a tutorial called “PostScript for Cocoa programmers”, but today brings my list of reasons why you should care in the first place.

(more…)

Mac app checklist

Sunday, March 18th, 2007

A list of things that you must do for every app. Most of these should be done up front; some can be put off a little bit; some can be put off a long time, but ought to be done at some point rather than put off into the indefinite future.

Also available in TaskPaper format, in case you’re a TaskPaper user.

  1. Version control is mandatory. I recommend Subversion (which is free and open-source). The advantage of version-control is time travel: You can easily go back to any previous version of your code, or even certain files. This lets you delete with confidence. Be sure to follow the trunk+branches+tags layout for your repository; you can create branches for experimentation and tags for released versions. Both these operations are cheap in Subversion. The easy way to install Subversion is to use Martin Ott’s Subversion Installer package.

  2. Resolution independence. In particular, shun raster images (except for your Finder icons). Use vector graphics, either created in an app like Lineform or written in PostScript or Cocoa.

  3. Contextual menus. Much like AS support (listed below), effective contextual-menu support is a great way to make your app more efficient for its power users. In IB, create a top-level menu by dragging it from the palette into your nib, then ctrl-dragging from the view to the top-level menu and connecting to the menu outlet. If you create your views programmatically, you’ll need to override -menu (either returning a programmatically-created menu, or adding support for a -setMenu: method that you provide).

  4. Localize everything. Localize every string you show to the user, even if it’s only in English for now. If your app achieves any popularity (and you should hope it does), people will volunteer to localize it into their preferred language. If you use NSLocalizedString on every hard-coded string as you write it, your localizers will be able to localize your entire app, without having to ask you for app-side support because it will already be there.

  5. Maintain a bug- and feature-tracking system. Ideally, something like a locally-hosted Trac (don’t open it to the users, or you will be fighting lots of duplicates), but even a simple OmniOutliner or Opal document will save you having to remember all those things you need to fix/add.

    Its benefits aren’t as immediate as version-control: Version-control pays off the moment you want to bring back something you deleted, whereas a bug-tracker pays off months down the road, when you’re trying to remember what that last feature you wanted for 1.0 was, or what that one guy reported with the bug that only happened when he clicked on one of the windows with his left hand. But it’s the same kind of time-saver.

  6. Sell your shares in __MyCompanyName__. Especially important for open-source projects, since other people will be reading your code. Do this now, so you don’t need to do it after you create the project.

  7. Burninate NewApplication. The template nibs (including those in a new project) use “NewApplication” throughout the menus, where your application name is supposed to go. Make sure you replace “NewApplication” with your actual application name. (Even better, use Daniel Jalkut’s app template instead of Apple’s.) Take it from me, it’s embarrassing to ship an app whose menus still use the name “NewApplication”. ☺

  8. Thoroughly test everything in the stock menus. Especially the application menu (edit: used to say ; thanks Jesper) and Help menu. Services should work in any text view or text field, and ideally should work in any view that has data that your users may want to run a Service over. The Preferences menu item should be hooked up or deleted. Same for “Application Name Help”.

  9. Edit the credits. (No pun intended.) These are stored in the “Credits” file in Contents/Resources. The default Credits file is an RTF file; TN2179 says that the About panel looks for Credits.{html,rtf,rtfd}, in that order.

  10. An application icon. If you can’t draw a good icon, hire someone to draw one for you. (If yours is an open-source project and it becomes popular, somebody may volunteer.)

  11. Document icons. (For doc-based apps only, obviously.) One document icon for each document type you can write out, with different classes of type (e.g. movie vs. audio vs. text) being depicted in significantly different ways (e.g. QuickTime Player’s icons having sprocket-holes for video files vs. a waveform for audio files).

  12. Pick a creator code and register it. This still matters, especially for associating a specific document file with your app. (Say you’re writing a multi-track video editing app—please—and you want to have movie files that you save be owned by your app rather than QTP. That’s what a creator code is for.) Your creator code goes in the Properties tab of Target Info in Xcode. Keep in mind that creator codes are limited to MacRoman characters.

  13. Make sure your bundle ID, version, short version, and Get Info string are correct.

    • Your bundle ID should be something unique to you, and should use your domain—not Apple’s, not anyone else’s, yours. If you don’t have one, make one up. Be sure to use the domain that you’ll eventually actually own; when you finally register that domain (even if you don’t do it for years, but you should do it as soon as you can, IMO), you won’t have to change all your bundle IDs.
    • Dave Dribin discusses what to put in CFBundleVersion.
    • The short version string is the version that appears in Finder’s List View; it should be a simple major.minor.maint version-number string (e.g., 1.0), and nothing else.
    • For Get Info strings, include a copyright statement and your version—for awhile, Apple said to leave out the version, but they’ve since reverted that, as users (well, at least former Mac OS users like me) expect to see a version number there. The version number you have here should be the same one you have in the short version string.
  14. Run it with no prefs. The goal here is to see how your app looks and works the very first time it is run. In particular, anything fed by NSUserDefaultsController needs to handle nil values correctly if you didn’t put some default values into it in main.

  15. Do at least one private beta or release-candidate. This is your final attempt to discover any bugs that you haven’t found yourself. Maybe it’s a byte-order bug, or something that only happens in certain network situations, or a crash that doesn’t happen on your machine because xyz. Regardless, you’ll find the bug only when somebody else runs your program, and hopefully you’ll do that in a private beta or release-candidate phase rather than in your actual public release. (You’re welcome to do a public beta as well, but don’t stall—get to release quickly.)

  16. Include a license and a ReadMe. For open-source projects, picking a license is easy: Use new-BSD, MIT, LGPL, or GPL. I don’t know what you would do for a closed-source project—I suggest consulting an intellectual-property lawyer. And definitely include a ReadMe. Your ReadMe should cover the following points:

    • What does the app do? (This is in case it was delivered on a CD or other compilation. In that case, they didn’t read the description on your webpage.)
    • How would I use the app in its most basic operation?
    • Who owns this app? (Copyright statement.)
    • Where can I get a newer version of this app? (Link to your website for the product.)

    Also, if you present your ReadMe as an RTF file, make sure you choose “Prevent Editing” from TextEdit’s Format menu, so that your ReadMe file is read-only. This prevents your users from accidentally deleting important swaths of the file.

  17. Maintain a detailed version history. Include the version number, release date, and list of changes. Version control will help you here: It’s tedious, but not hard, to build a complete list of changes from an svn log. The version history should be on a page of your website.

  18. AppleScript support. This isn’t something you need right away, but it’s a definite plus, especially if you’ll be selling your app. Daniel Jalkut has good info on adding AppleScript support. Even just being able to script the preferences is a good start.

  19. Check for leaks. The leaks(1) utility will scan your app’s heap looking for memory that isn’t pointed-to anymore. It’s very easy to leak objects, especially when your autorelease-fu is not strong. Be sure to use MallocStackLogging, so you can see what allocated the leaked memory; this may give you a hint as to where the leak lies. You may also find heap(1) useful.

  20. Get a real website. No, your GeoCities website won’t cut it. Expect lots of traffic, and be on a server that can handle it. No such server is free. (Also, don’t host it yourself—most ISPs won’t allow you to run a server on a consumer account, you don’t have the bandwidth anyway, and professional hosting is cheaper than an ISP server-grade account.) I recommend TextDrive, though others[1] [2] [3] have their own recommendations.

Feel free to suggest more items in the comments.

UPDATE 2007-04-02 15:15 PDT: The old #5 was controversial, and I concede that it’s too early to give that advice yet. I’ve replaced it with one that I posted in a comment. For posterity, here’s the old #5:

Test on both architectures. If you can’t do this, make your app Intel-only. Testers with ICBMs are easy to come by. Testers with PowerPC Macs aren’t so much, and they get harder to find every day. So if you have only a PowerPC, you can recruit somebody with an ICBM to test your UB, but if you have only an ICBM, it’s easiest to just make it Intel-only.

UPDATE 2007-04-02 20:17 PDT: Added a link to Martin Ott’s Subversion Installer package.

UPDATE 2007-07-23 20:15 PDT: Added discussion of the short version string to point #13.

UPDATE 2007-07-23 20:27 PDT: Formatted point #13 and clarified its discussion of Get Info strings.

UPDATE 2007-08-06 04:30 PDT: Added links to OmniOutliner and Opal.

UPDATE 2007-08-08 17:15 PDT: Added TaskPaper version.

UPDATE 2007-08-08 21:51 PDT: Added suggestion to Prevent Editing on your ReadMe.rtf file.

It works for sets, too

Saturday, February 17th, 2007

You probably know that NSArray supports KVC as a sort of pseudo-HOM:

NSArray *words = …;
NSArray *lengths = [words valueForKey:@”length”]; //Invokes -[NSString length] for every element, assuming that each element is a kind of NSString

What you may not know is that this works for NSSet as well. As -[NSArray valueForKey:] returns an NSArray, so does NSSet return an NSSet.

Apple Bug Friday! 47

Friday, February 16th, 2007

You thought it was dead! But no! Apple Bug Friday is back!

This bug is Drawing text changes the fill color. It was filed on 2006-08-18 at 20:35 PDT.

(more…)

What’s the resolution of your screen?

Sunday, February 4th, 2007

A few weeks ago, I installed Adobe Reader to view a particular PDF, and noticed something interesting in its Preferences:

Its Resolution setting is set by default to “System setting: 98 dpi”.

“Wow”, I thought, “I wonder how it knows that.” So I went looking through the Quartz Display Services documentation, and found it.

The function is CGDisplayScreenSize. It returns a struct CGSize containing the number of millimeters in each dimension of the physical size of the screen. Convert to inches and divide the number of pixels by it, and you’ve got DPI.

Not all displays support EDID (which is what the docs for CGDisplayScreenSize say it uses); if yours doesn’t, CGDisplayScreenSize will return CGSizeZero. Watch for this; failure to account for this possibility will lead to division-by-zero errors.

Here’s an app to demonstrate this technique:

ShowAllResolutions' main window: “Resolution from Quartz Display Services: 98.52×96.33 dpi. Resolution from NSScreen: 72 dpi.”

ShowAllResolutions will show one of these windows on each display on your computer, and it should update if your display configuration changes (e.g. you change resolution or plug/unplug a display). If CGDisplayScreenSize comes back with CGZeroSize, ShowAllResolutions will state its resolution as 0 dpi both ways.

The practical usage of this is for things like Adobe Reader and Preview (note: Preview doesn’t do this), and their photographic equivalents. If you’re writing an image editor of any kind, you should consider using the screen resolution to correct the magnification factor so that a 8.5×11″ image takes up exactly 8.5″ across (and 11″ down, if possible).

“Ah,”, you say, “but what about Resolution Independence?”.

The theory of Resolution Independence is that in some future version of Mac OS X (possibly Leopard), the OS will automatically set the UI scale factor so that the interface objects will be some fixed number of (meters|inches) in size, rather than some absolute number of pixels. So in my case, it would set the UI scale factor to roughly 98/72, or about 1+⅓.

This is a great idea, but it screws up the Adobe Reader theory of automatic magnification. With its setting that asks you what resolution your display is, it inherently assumes that your virtual display is 72 dpi—that is, that your UI is not scaled. Multiplying by 98/72 is not appropriate when the entire UI has already been multiplied by this same factor; you would essentially be doing the multiplication twice (the OS does it once, and then you do it again).

The solution to that is in the bottom half of that window. While I was working on ShowAllResolutions, I noticed that NSScreen also has a means to ascertain the screen’s resolution: [[[myScreen deviceDescription] objectForKey:NSDeviceResolution] sizeValue]. It’s not the same as the Quartz Display Services function, as you can see; it seemingly returns { 72, 72 } constantly.

Except it doesn’t.

In fact, the size that it returns is premultiplied by the UI scale factor; if you set your scale factor to 2 in Quartz Debug and launch ShowAllResolutions, you’ll see that NSScreen now returns { 144, 144 }.

The Resolution-Independent version of Mac OS X will probably use CGDisplayScreenSize to set the scale factor automatically, so that on that version of Mac OS X, NSScreen will probably return { 98.52, 98.52 }, { 96.33, 96.33 }, or { 98.52, 96.33 } for me. At that point, dividing the resolution you derived from CGDisplayScreenSize by the resolution you got from NSScreen will be a no-op, and the PDF view will not be doubly-magnified after all. It will be magnified by 133+⅓% by the UI scale factor, and then magnified again by 100% (CGDisplayScreenSize divided by NSDeviceResolution) by the app.

Obviously, that’s assuming that the app actually uses NSScreen to get the virtual resolution, or corrects for HIGetScaleFactor() itself. Adobe Reader doesn’t do that, unfortunately, so it suffers the double-multiplication problem.

So, the summary:

  • To scale your drawing so that its size matches up to real-world measurements, scale by NSDeviceResolution divided by { 72.0f, 72.0f }. For example, in my case, you would scale by { 98.52, 96.33 } / { 72.0, 72.0 } (that is, the x-axis by 98.52/72 and the y-axis by 96.33/72). The correct screen to ask for its resolution is generally [[self window] screen] (where self is a kind of NSView).
  • You do not need to worry about HIGetScaleFactor most of the time. It is only useful for things like -[NSStatusBar thickness], which return a number of pixels rather than points (which is inconvenient in, say, your status item’s content view).

Two ways to strengthen your Cocoa-fu

Friday, February 2nd, 2007
  1. Chris Forsythe’s Weekly Cocoa App Challenge, wherein you must clone an existing app with no source. “Week 3” is on now, since over two weeks ago. At the end of each logical week, Chris reveals the source, so you can check your answer (and his).

  2. Colin Barrett’s Koan. The Koan that Colin poses is a verbal challenge, and is much harder than anything that Chris has put up so far. You basically need to already have strong Cocoa-fu in order to find the answer to this one within you.

Weekly Cocoa app challenge #3 solution

Thursday, January 18th, 2007

HERE THERE BE SPOILERS. If you intend to participate in the challenge, read no further because my solution follows.

DudeMenu-PRH-1.0-source.tbz

Source and executable are both included. It took me about an hour and 19 minutes to complete. Source-control-wise, this is revision 7 of its SVN repository.

Things used therein:

  • Acquisition of and intake from a plist
  • Definition of custom symbols for the auto-documentation of dictionary keys
  • Dynamically building a menu
  • Validation of input, both with alerts and assertions
  • Many, many comments
  • String localization
  • String localization using a custom table
  • Primitive token-replacement (primitive because there is no provision for escaping)
  • Inspection of the current locale
  • A status item
  • Menu items representing other objects
  • An app without a main menu (as in NSApp’s mainMenu)
  • LSUIElement
  • A menu item that messages NSApp directly
  • Retrieval of images from the search path by name
  • Walking arrays
  • Auto-incrementation

A Core-Image-less Image Unit

Wednesday, January 17th, 2007

Can you imagine an Image Unit that didn’t actually use Core Image?

I just wrote one.

Well, OK, so I did use CIFilter and CIImage — you can’t get away without those. But I did not use a CIKernel. That’s right: This simple filter does its work without a kernel.

For the uninitiated, a kernel is what QuartzCore compiles to either a pixel shader or a series of vector (AltiVec or SSE) instructions. All Image Units (as far as I know) use one — not only because it’s faster than any other way, but because that’s all you see in the documentation.

But I was curious. Could an Image Unit be written that didn’t use a kernel? I saw nothing to prevent it, and indeed, it does work just fine.

The image unit that I wrote simply scales the image by a multiplier, using AppKit. I call it the AppKit-scaling Image Unit. Feel free to try it out or peek at the source code; my usual BSD license applies.

Obviously, this Image Unit shouldn’t require a Core Image-capable GPU.

The Cocoa Memory Management Regular Expression

Thursday, January 11th, 2007

Any method whose selector matches this regular expression gives you a reference to its return value, which you must later release.

/^retain$|^(alloc|new)|[cC]opy/

As an added extra bonus, here’s the CF equivalent.

/Create|Copy|Retain/

Carbon adds several exceptions, such as GetControlData (which returns a reference to CF things like the string value of a text field). Use Cocoa instead and your memory-management life will be simpler. ;)

How to detect a click

Sunday, January 7th, 2007

If you’ve ever written something that needs to simulate menu behavior, you’ve probably noticed that it would be nice to know, portably to future OS versions, how long you should wait before rejecting a mouse-down/mouse-up pair as not a click, as well as how far the user must move the mouse for it to not be a click.

The way to do this is:

EventTime clickTimeout;
HISize clickMaxDistance;
OSStatus err = HIMouseTrackingGetParameters(kMouseParamsSticky, &clickTimeout, &clickMaxDistance);

There is no Cocoa API for this.

On initializing static variables

Thursday, January 4th, 2007

Did you know that you don’t have to initialize static variables? (If you’ve done any Cocoa programming, you know that statics are commonly treated as class ivars, most commonly to hold singleton instances.)

Quoth C99 (§6.7.8 paragraph 10):

… If an object that has static storage duration is not initialized explicitly, then:

  • if it has pointer type, it is initialized to a null pointer;
  • if it has arithmetic type, it is initialized to (positive or unsigned) zero;
  • if it is an aggregate [array or structure —PRH], every member is initialized (recursively) according to these rules;
  • if it is a union, the first named member is initialized (recursively) according to these rules.

And I’ve prepared a test app that shows that gcc 4.0.1 on OS X 10.4.8 does seem to comply with this handy part of the standard.

Happy not-initializing!

Compiling EPS files to PDF files using Xcode

Wednesday, December 6th, 2006

You may remember Daniel Jalkut’s blog post about resolution independence, with its demonstration of the superiority in quality of vector graphics over raster graphics (using an EPS file that I created for him ☺).

The problem with using an EPS file, as I pointed out to him when I emailed him the file originally, is that EPS files are very slow on OS X. The current (Tiger) implementation of NSEPSImageRep uses Quartz’s CGPSConverter to convert the EPS data to PDF data, then instantiates an NSPDFImageRep (kept in a private ivar) that it has do all the real work.

That PostScript-to-PDF conversion is the bottleneck. You don’t want that multiple-second delay (yes, really) at runtime. Better to take it before runtime. At the time, I suggested that he use pstopdf to convert to a PDF file, then put the PDF file into his Xcode project to be copied into the app bundle.

But what if you decide later to change the PostScript file? You need to remember to run pstopdf again. EPS files are just PostScript source code, so why not treat them like any other source code file? Or, a better question: How?

The answer is quite simple: Add a custom rule to your target in Xcode that runs pstopdf over EPS files. Then, you add the EPS file — not the PDF file — to the project, and Xcode will exhibit normal build-only-when-necessary behavior on the EPS file and copy the generated PDF file for you. And of course, you can have as many EPS files as you want — they’re source code files just like any others.

Screenshot of Xcode's Target Info window, showing the custom rule. The rule matches the glob pattern “*.eps” and runs the shell script “pstopdf ${INPUT_FILE_NAME} -o ${DERIVED_FILES_DIR}/${INPUT_FILE_BASE}.pdf”. It has one output file, which is “${DERIVED_FILES_DIR}/${INPUT_FILE_BASE}.pdf”. In the background is an Xcode project window showing a couple of EPS files in the “Other Sources” group.

Wanted for abuse of operators

Saturday, December 2nd, 2006

From the stock app delegate in Apple’s Core Data application template:

[managedObjectContext release], managedObjectContext = nil;
[persistentStoreCoordinator release], persistentStoreCoordinator = nil;
[managedObjectModel release], managedObjectModel = nil;

And in case you were wondering how Apple could possibly ship code that doesn’t even compile in the app templates…

… that’s legal code.

Yes, there is a comma operator, and that’s what’s at work there. There is a warning (-release returns void, whereas assignment of nil returns id, so the types are mismatched, which GCC doesn’t like), but the code is legal.

Legal doesn’t always mean correct, however. The comma operator is supposed to be used for things like this:

printf(“%s\n”, (-‍-argc, *++argv));

Though in modern usage it is out of favor, replaced on sight with less obfuscative code:

printf(“%s\n”, *++argv);
-‍-argc;

printf(“%s\n”, argv[idx++]);

It is not for performing a void function call followed by an assignment. A void expression should never appear in a comma expression. The code would be greatly improved by replacing the comma with a semicolon, dividing each statement into the two statements that it should be.

Why you should use -availableData rather than -readDataToEndOfFile

Sunday, October 8th, 2006

(All of this only applies to regular files, not to “communications channels”, which I interpret to mean sockets and pipes. Also, this only matters on a 64-bit Mac; on a 32-bit system, you will run out of memory long before seeing any difference between these two methods.)

-[NSFileHandle availableData]:
“If the receiver is a file, returns the data obtained by reading the file from the file pointer to the end of the file.”
-[NSFileHandle readDataToEndOfFile]:
“The data available through the receiver up to UINT_MAX bytes (the maximum value for unsigned integers) …”

In other words, if there are more than UINT_MAX bytes in the file, -availableData will return all of them, whereas -readDataToEndOfFile will only return the first UINT_MAX of them.

ISO 8601 parser, version 0.4

Thursday, August 31st, 2006

I’ve released version 0.4 of my ISO 8601 parser and unparser.

Colin noticed that 0.3 didn’t work so well for parsing the datestamp in the filename on an Adium log file (those datestamps being in ISO 8601 format). You see, Adium generates the filename with a ‘.’ instead of a ‘:’, since ‘:’ is special on HFS/HFS+. 0.3 expects a ‘:’ and only a ‘:’.

So I added new methods that take a time-separator character. Now you can pass ‘.’ (or anything else, except NUL) when you need to. The old methods are still around, in case you don’t. This is the case, for example, for datestamps inside a chatlog file, as opposed to in its filename.

Report-an-Apple-bug Friday! 46

Friday, August 18th, 2006

This bug is +graphicsContextWithBitmapImageRep: doesn’t properly map bitmap format to Quartz. It was filed on 2006-08-17 at 19:07 PDT.


Summary:

+[NSGraphicsContext graphicsContextWithBitmapImageRep:] always returns a RGBA context, even when the image rep is ARGB.

Steps to Reproduce:

  1. Create an NSBitmapImageRep with an RGB color space and a bitmapFormat that contains the NSAlphaFirstBitmapFormat bit and not the NSFloatingPointSamplesBitmapFormat bit.
  2. Call +[NSGraphicsContext graphicsContextWithBitmapImageRep:] with the image rep created in step 1.
  3. Set the current graphics context to the graphics context created in step 2.
  4. Draw.

Expected Results:

WYD (draw) IWYG.

Actual Results:

The components in each pixel come out of the image rep permuted, since you expect ARGB from the image rep, but the graphics context put RGBA into it instead.

Regression:

None known.

Notes:

I did not test with the NSFloatingPointSamplesBitmapFormat bit; therefore, I do not know whether the bug exists with it or only without it.

Workaround:

Reinvent the +graphicsContextWithBitmapImageRep: wheel.


Technorati tags: ,

A vim quickie

Saturday, July 22nd, 2006

:g/^[-+].\+{\s*$/,/^}/fold ←Folds all Obj-C methods in the current file.

Technorati tags:

Image Shadow Adder

Tuesday, June 27th, 2006

My newest application, Image Shadow Adder, adds a shadow to an image. I plan on using this instead of Photoshop to restore shadows to my window screenshots (OS X’s window screenshot command, ⇪⇧⌘4, does not preserve the shadow).

Also, the source code (BSD license) demonstrates how to use Bindings (used to insert the original and shadowed images into the document window’s image views), use NSShadow, and capture an NSImage to an NSBitmapImageRep. Few comments in the code, I’m sorry to say, as this is just a quick-and-dirty application that I wrote when it dawned on me that I could use NSShadow when drawing an NSImage, rather than emulating my old Photoshop technique.

For reference, that technique was:

  1. Take one regular window screenshot, and one rectangular screenshot of the window with shadow on a white background.
  2. Drag and drop the window screenshot into the rectangular screenshot. Position so that the two window images line up.
  3. Knock out the black corners on the window screenshot. These are masked out by the alpha channel in the PNG file, but Photoshop sucks at alpha channels.
  4. Create a new layer. Fill with black.
  5. Load the window screenshot as a selection.
  6. Add a layer mask (reveal selection) to the black layer.
  7. Copy Merged.
  8. Add an alpha channel. Paste into it, and invert it. The area of the window should be white, with the shadow fading to black.
  9. Return to the Layers list and disable or delete the layer mask. Move the black layer down beneath the window-screenshot layer.
  10. Export using SuperPNG.

The only disadvantage to the new way is that AppKit’s PNG-interlacing setting is broken. The generated PNG data is not interlaced. I’ll have to use OptiPNG to interlace it.

Technorati tags: , .

Another quickie

Saturday, June 24th, 2006

If you use -[NSAttributedString drawAtPoint:], you may be surprised to find that it doesn’t respect NSParagraphStyles in the receiver’s attributes. The documentation hints at why:

The width (height for vertical layout) of the rendering area is unlimited, unlike drawInRect:, which uses a bounding rectangle. As a result, this method renders the text in a single line.

Well, it will actually respect any newlines when measuring and drawing the text. You won’t just get a single line. What it really means is that you don’t get paragraph styles applied.

Solution: Use -[NSAttributedString drawInRect:] instead.

I imagine that the same rule applies to -[NSString drawAtPoint:withAttributes:] and -[NSString drawInRect:withAttributes:] as well, since the former method has the same note in its documentation (and is probably implemented using -[NSAttributedString drawAtPoint:]).

Technorati tags: , , , .