Archive for the ‘OS X’ Category

Swift: Today’s Wow moment. Adding menus to playgrounds

I finally figured out how to get a nib/xib loaded into a playground and use it to power an app menu. It’s ridiculous how long this took and how simple the solution turned out to be.

Each playground, regardless of whether you’re building iOS or OS X code, runs as a tiny independent app on your computer. You can find that executable, and the app it is running in by peeking at NSBundle.mainBundle().executablePath.

Playground apps are tiny.  OS X versions consist of a minimal bundle with a stripped-down Info.plist, a Resources folder, and a MacOS executable.

You can throw a xib file into your Resources, but the playground cannot read it or run it because it’s not in compiled form. Today, I (finallly!) thought of using ibtool to pre-compile my MainMenu.xib file into nib and then load that. When you install Xcode’s command line tools, ibtool gets added to /usr/bin. So all you need to do to compile your nib is issue the following command:

ibtool --compile MainMenu.nib MainMenu.xib

Throw that resulting nib into your playground’s resources folder and you’re ready to load it up.

The trickiest bit was trying to get all the unsafe autoreleasing mutable pointer stuff working. Swift is like a perfect haiku. It all looks simple and obvious when you’ve finished writing it but getting it debugged is a pain. The answer, by the way is this, which hopefully people will be able to google up without having to go through an hour or two of tweakage:

var array = NSArray()
var outputValue = AutoreleasingUnsafeMutablePointer<NSArray?>(&array)
NSBundle.mainBundle().loadNibNamed("MainMenu", owner: nil, topLevelObjects: outputValue)

A few further points:

I built a simple compile command line shell script so I don’t have to remember the order of the files or the arguments for ibtool. You can see it in the video in the Resources folder.

Add your xib source into your Resources as well as your compiled nib. You can then edit the xib using Xcode editors directly from the playground workspace. You still have to compile outside it. I keep a terminal window open to simplify the process.

Every now and then Xcode automatically moves things around, so use a full path in the terminal. Then use the terminal’s history to re-issue the same command as needed:

cd /Users/ericasadun/Desktop/Playgrounds/X\ -\ Playgrounds/OSX\ Menu.playground/Resources ; ./compile

Don’t compile when your nib is in use.  Comment out the XCP indefinite execution line and the loadNibNamed line, and then compile in place from the command line.

You can’t much in the way of binding in your xib but you can design stuff there and then add the targets and behavior in the playground.

Utility: Finding untagged files and folders at the command-line

How do you find untagged items? This question popped up in #macdev for someone working on a Dropbox-based backup system of all things. So how do you find just those items in a folder that are untagged? Use mdfind:

mdfind -onlyin . "kMDItemUserTags != '*'" | open -f

If you switch that inequality to ==, you’ll retrieve all tagged items instead.

Folderol makes extensive use of tags. You can search for specific tags using names within the single parens, e.g. to find all the Green system tags:

mdfind -onlyin . "kMDItemUserTags == 'Green'" | open -f

The built in tag names are Red, Orange, Yellow, Green, Blue, Purple, and Gray, but you can assign any tag name you like in Get Info or by using the tag field in Folderol. Then, with a liberal use of asterisks, you can search for, for example, items with tags that contain the word Neon or neon (the [c] means case insensitive).

mdfind -onlyin . "kMDItemUserTags ==[c] '*Neon*'" | open -f

Or tags that start with Neon

mdfind -onlyin . "kMDItemUserTags == 'Neon*'" | open -f

Or end with Neon

mdfind -onlyin . "kMDItemUserTags == 'Neon*'" | open -f

In addition to the [c], you can use [d] (or [cd]) to add diacritical insensitivity, so a search for “café” would also match “cafe”.

To find all Folderol-set custom color tags, search for “(*)”. This excludes standard system tags, although if you’ve tagged with both a custom tag and a system one, the file or folder will appear in the results.

mdfind -onlyin . "kMDItemUserTags == '*(\*)*'" | open -f

Unreadable PDF. Who knew? (An ode to @ecammtweets)

Screen Shot 2015-02-23 at 11.57.34 AM

My son’s school recently emailed me some forms. I was about to acknowledge their receipt when I discovered I could not read them. Instead of seeing the forms they’d sent, I could only view some message about my PDF viewer (Mail and Finder in this case) not being able to display the file.

“If this message is not eventually replaced by the proper contents of the document, your PDF viewer may not be able to display this kind of document.” It then instructed me to upgrade to the latest version of Adobe Reader, a suggestion that in context seemed slightly hilarious as I wasn’t using Adobe Reader in any fashion at the time.

It turns out that some Adobe PDFs have this marvelous property: They can only be read by Adobe software. It took a bit of googlage until I finally found a site that explained the situation. No Adobe, no read.

Fortunately, I have a version of Adobe CS 4 still installed on my Mac, although it’s held together these days by spit and bandaids. Hello 2008. I was able to get Acrobat Pro running and sure enough, I could open the doc in the app and it was readable.

Naturally, I tried to use OS X’s built-in PDF rendering to print to file. Bzzzzzt. That (of course) was not allowed.

Screen Shot 2015-02-23 at 12.03.17 PM

Thank goodness for Ecamm. Instead of printing to “file”, I used my copy of their Printopia extension ($19.99) to print to my Mac instead. Instantly my unreadable PDF file transformed to a readable one. I simply pulled the rendered PDF out of my Documents/Printopia folder and it was ready to use. Printopia saved the day. Thanks, Ecamm. And hopefully this write-up will be googlable for anyone who encounters the same kind of problem with a PDF file that cannot be read in OS X mail.

As a final note, I’m told that you might be able to contact the person who prepared the PDF and ask them to switch the limitation off and resave. This would allow the document to be opened in any PDF viewer. When dealing with low-level administrative staff employed in the public school system, however, you might just want to know about alternative approaches so you can go-it on your own.

Development: Old Macs

No matter how many old OS X installers you end up keeping on your backup data drives, trying to create legacy systems for development testing can be more of a pain than I expected. Recent versions of OS X do everything they can to prevent downgrades, I did better installing early releases onto fresh partitions and upgrading them.

This explains why I just wasted an entire morning creating and upgrading Lion partitions to enable me to have some fresh 10.7, 10.8, and 10.9 on-hand. Fortunately, I long ago wrote up a how-to for creating a stand-alone USB installer stick for TUAW, back when TUAW still existed. I was thrilled to find that stick exactly as I left it because it was the thing that let me boot one of my Yosemite systems (still running Beta 4, apparently) to a suite of tools that actually allowed me to install these OS’s. Yosemite’s recovery partition was not as accommodating.

I set up a fresh USB drive with many partitions, installed Lion into one of them and then cloned that into the other partitions. From there, it was just a matter of having the right upgrades on-hand. (I had them all except Mavericks, but Mike Ash, bless him, pointed out that I could grab a copy from App Store from my old purchases.)

I know there are lots of other solutions including virtual machines and such, but I like having native systems running on native hardware, using a bootable USB hard drive that can move from machine to machine to machine.

I’m still waiting on my Mavericks upgrade but soon my test systems will be all back in shape, even those I thoughtlessly yozed last Summer.

Folderol and Yosemite

Screen Shot 2014-10-20 at 9.42.53 AM

A last second (very last second) Yosemite API change is fixed and a new version of Folderol is awaiting review at iTunes Connect. I apologize for the inconvenience. Email me if you need a beta version to hold you through until the fix is approved.

Auto Layout: iOS 8

It’s been a pretty exciting summer as far as Auto Layout goes. I’m busy going over material so I can get some dates and work estimates to Trina on a possible 3rd edition of Auto Layout. Between Swift, OSX, iOS 8, and whatever is going to be announced in September, it looks like a particularly interesting place to be.

I can’t tell you how thrilled I am about active, the property that self-installs and removes constraints to their natural destination. I submitted my first radar on this back before the first edition was even published. Now with the third edition, I suppose I’ll be all “Yeah, that’s my radar baby!”

The long iOS 8 summer first gave us left and right layout guides, a la the top and bottom guides we first encountered in iOS 7 but they soon disappeared and have since been replaced by a suite of Margin-based layout attributes:

    NSLayoutAttributeLeftMargin NS_ENUM_AVAILABLE_IOS(8_0),
    NSLayoutAttributeRightMargin NS_ENUM_AVAILABLE_IOS(8_0),
    NSLayoutAttributeTopMargin NS_ENUM_AVAILABLE_IOS(8_0),
    NSLayoutAttributeBottomMargin NS_ENUM_AVAILABLE_IOS(8_0),
    NSLayoutAttributeLeadingMargin NS_ENUM_AVAILABLE_IOS(8_0),
    NSLayoutAttributeTrailingMargin NS_ENUM_AVAILABLE_IOS(8_0),
    NSLayoutAttributeCenterXWithinMargins NS_ENUM_AVAILABLE_IOS(8_0),
    NSLayoutAttributeCenterYWithinMargins NS_ENUM_AVAILABLE_IOS(8_0),

Comments and properties in the UIView header file indicate a layout tree of margins. I think it’s fair to assume this ties into the adaptive application that runs properly both on full-screen as well as side-by-side.

/*
 -layoutMargins returns a set of insets from the edge of the view's bounds that denote a default spacing for laying out content.
 If preservesSuperviewLayoutMargins is YES, margins cascade down the view tree, adjusting for geometry offsets, so that setting the left value of layoutMargins on a superview will affect the left value of layoutMargins for subviews positioned close to the left edge of their superview's bounds
 If your view subclass uses layoutMargins in its layout or drawing, override -layoutMarginsDidChange in order to refresh your view if the margins change.
 */
@property (nonatomic) UIEdgeInsets layoutMargins NS_AVAILABLE_IOS(8_0);
@property (nonatomic) BOOL preservesSuperviewLayoutMargins NS_AVAILABLE_IOS(8_0); // default is NO - set to enable pass-through or cascading behavior of margins from this view’s parent to its children
- (void)layoutMarginsDidChange NS_AVAILABLE_IOS(8_0);

Unfortunately, the material from WWDC is light on details and these updates didn’t take place until fairly recent betas. Got any insights into how this material will play out? I’d love to hear it. (And thanks in advance!)

That Vector Thing: Xcode 6, PDF assets, and unfortunate outcomes

During the iOS 8/OS X 10 10 Keynote, someone mentioned that Xcode assets would now support PDF vector art. I was thrilled. I’ve been waiting for a long time for vector art to form part of the Cocoa touch development arsenal. Unfortunately, the reality is not nearly as interesting as the promise.

Yes, you can add PDF art to asset groups but the size of the art is fixed. When you create a UIImage with a PDF source it gets rasterized. The result stretches with the underlying bits, not vectors:

Screen Shot 2014-06-09 at 1.31.38 PM

Compare that to the original art, which was 35×40 in size, scaled for preview on OS X:

Screen Shot 2014-06-09 at 1.31.52 PM

So what do you do? The simplest answer is to create multiple PDF images and treat them the way you would PNG images, whether using a simple retina pair or building icons and launch assets. So your asset’s contents can end up looking like this:

{
  "images" : [
    {
      "size" : "29x29",
      "idiom" : "iphone",
      "filename" : "AppIcon29x29.pdf",
      "scale" : "1x"
    },
    {
      "size" : "29x29",
      "idiom" : "iphone",
      "filename" : "AppIcon29x29@2x.pdf",
      "scale" : "2x"
    },
    {
      "size" : "29x29",
      "idiom" : "ipad",
      "filename" : "AppIcon29x29@2x~ipad.pdf",
      "scale" : "2x"
    },
    {
      "size" : "29x29",
      "idiom" : "ipad",
      "filename" : "AppIcon29x29~ipad.pdf",
      "scale" : "1x"
    },
    {
      "size" : "40x40",
      "idiom" : "iphone",
      "filename" : "AppIcon40x40@2x.pdf",
      "scale" : "2x"
    },
    {
      "size" : "40x40",
      "idiom" : "ipad",
      "filename" : "AppIcon40x40@2x~ipad.pdf",
      "scale" : "2x"
    },
    {
      "size" : "40x40",
      "idiom" : "ipad",
      "filename" : "AppIcon40x40~ipad.pdf",
      "scale" : "1x"
    },
    {
      "size" : "50x50",
      "idiom" : "ipad",
      "filename" : "AppIcon50x50@2x~ipad.pdf",
      "scale" : "2x"
    },
    {
      "size" : "50x50",
      "idiom" : "ipad",
      "filename" : "AppIcon50x50~ipad.pdf",
      "scale" : "1x"
    },
    {
      "size" : "57x57",
      "idiom" : "iphone",
      "filename" : "AppIcon57x57@2x.pdf",
      "scale" : "2x"
    },
    {
      "size" : "57x57",
      "idiom" : "iphone",
      "filename" : "AppIcon57x57.pdf",
      "scale" : "1x"
    },
    {
      "size" : "60x60",
      "idiom" : "iphone",
      "filename" : "AppIcon60x60@2x.pdf",
      "scale" : "2x"
    },
    {
      "size" : "72x72",
      "idiom" : "ipad",
      "filename" : "AppIcon72x72@2x~ipad.pdf",
      "scale" : "2x"
    },
    {
      "size" : "72x72",
      "idiom" : "ipad",
      "filename" : "AppIcon72x72~ipad.pdf",
      "scale" : "1x"
    },
    {
      "size" : "76x76",
      "idiom" : "ipad",
      "filename" : "AppIcon76x76@2x~ipad.pdf",
      "scale" : "2x"
    },
    {
      "size" : "76x76",
      "idiom" : "ipad",
      "filename" : "AppIcon76x76~ipad.pdf",
      "scale" : "1x"
    }
  ],
  "info" : {
    "version" : 1,
    "author" : "arthelper"
  }
}}

That’s silly on several levels. For one thing, no matter how compact your vector art is, 17 copies of it can become substantially larger than 17 very tiny PNGs. (The 19 items in the following includes the app icon set folder and the JSON contents file.)

Screen Shot 2014-06-09 at 1.26.03 PM

 

Thinking myself clever, I tried creating a version where all items in the icon set pointed to the same image resource. I figured, why not have each item load the same material since it’s vector based to begin with. The tl;dr version is this: it didn’t work. Xcode would not load assets formatted in that way.

The only bright side arrives in the default image side of things. With default images, art follows many geometries — or at least at this time, who knows what it will be at the end of this summer, or by the time I finish catching up with the WWDC videos. Because of this and their naturally large sizes, my PDF tests regularly improved over the PNGs they replaced for launch images. It’s still pretty redundant for retina and non-retina screens, but it was less bad than a full suite of launch PNGs in my tests.

 

Art Helper: Batch Retina Pairs

Screen Shot 2013-03-29 at 2.23.44 PM

At Lyle Andrew’s request, I’ve added a batch processor utility into Art Helper. It’s still in testing (ping me by email if you’d like to help), but it works like this:

You pick a folder, it searches that folder for art files. They have to use an even pixel size in both dimensions. Art Helper then creates an output folder for the new pairs and builds them there. This way, he doesn’t have to drag in each item to process it.

Want new features? Just let me know and I’ll see what I can do. This is basically a crowd-sourced tool.

Art Helper 1.0.3 submitted to Mac App Store

Screen Shot 2013-03-12 at 8.30.56 AM

Art Helper generates Icon and Default files for iOS development and Iconset folders for OS X from base art that you supply. The new version:

• Adds Retina-pair creation. Build @2x (original resolution) and regular (quarter resolution, half in each direction) versions.
• Addresses Retina-generation bug (thanks to Matt Stevens and Lyle Andrews)

With luck, the update should go live in a week or two.