Archive for the ‘iOS’ Category

That UIAlertView thing: Deprecation and dinosaurs

Are you surprised at how many devs are focused on developing new iOS 7-specific applications this late into the game? iOS 7 has a 2 – 3 month ticking time bomb on it and yet every day, I encounter developers whose primary focus is getting their iOS 7 apps into shape for this autumn rather than building iOS 8 for launch this fall.

Sure iOS 7 and iOS 7 compatibility aren’t going away for a while. Even those devs aware of UIAlertView deprecation are focused on bigger deployment issues. Nearly all of them have irrational partners and bosses who insist on backwards compatibility to roughly the jurassic period. If a new iOS 7 app will work on iOS 8 that’s all they want or need. Taking advantage of iOS 8 features, well that’s for other people.

When you work on books, it’s awfully easy to get stuck in Apple headspace. Take UIAlertView. It’s been deprecated and I keep coming back to that, mostly because deprecated classes aren’t even available in Swift these days. Somehow this keeps resonating to my “It’s dead, Jim! I’m a doctor not a miracle worker” senses. I have to keep reminding myself of the day-to-day realities of the app store as it exists, not as Apple would have it. And yet when developers come to me asking how to build HUDs based on UIAlertView this late in the game, I start going UIAlertController ballistic.

The way I see it, ever since App Store started offering platform-specific upgrades, the pressure to back-support all devices all the time was gone. Unless you’re developing something new but specific to older devices (there’s a big market out there if you’re prepared to fight the tool suite, hi Chris S!), why not keep pushing forward with new releases? It’s not as if older systems can’t still do business with you. And when they upgrade, your newer versions are ready for them to download.

How much back compatibility do you think is necessary, especially if you put out regular updates?  If the world is upgrading with you, what are you gaining from supporting those 10% of users who have access to your product but may not be running the latest version of your software? What am I missing here?

I’m really curious to hear what you have to say.

Beta: Apps and Traits, some thoughts

The iOS family continues to grow. Although iOS targets are not nearly as splintered as Android’s multitude, iOS apps must adapt interfaces to numerous configurations for universal deployment. Routines specific to iPads or iPhones, to landscape or portrait orientation, and to retina or non-retina screens have transformed many iOS apps into a tangle of special-purpose code.

To push back, iOS 6 introduced Auto Layout, a descriptive system for interface design. Auto Layout enabled developers to use rule-based view placement that automatically adjusts to screen dimensions and orientation.

Now iOS 8 adds the notion of adaptive deployment. New classes and protocols enable apps to retrieve the specifics of the current runtime environment. This enables them to adapt not only to hardware limitations but also to whatever screen space has been allocated to their presentations. A truly adaptive app gracefully responds with a well designed and engaging interface, ready for the user at any size.

A iOS 8 trait collection describes a single point in deployment space. This space represents the range of possible conditions an interface might encounter in the real world. By making these traits concrete, Apple enters into a somewhat implicit contract with the developer – these are the types and ranges of flexibility you must design for.

Even if Apple introduces multi-windowing, you only have to worry about “compact” and “regular” interface sizes under the current system. There’s a (vague and legally unenforcable) guarantee that for at least the next year Apple will not add some “Ultra Compact” game-changer where you suddenly have to worry about designing forms for 100-pixel destinations. And, if Apple does introduce that windowing, the traitCollectionDidChange callback means you can switch your tablet presentations to phone ones for side-by-side execution and then back again.

No matter what kind of apps you design, a tension always exists between pixel-perfect control and adaptability. An interface that looks stunning on a 4″ iPhone may look cramped on a 3.5″ screen and sparse on a tablet. This has led some developers to build what is essentially multiple codebases under a single app umbrella.

With trait collections and standardized callbacks, iOS 8 is attempting to bring sanity back to those apps. So long as the presentation specifics don’t get too ridiculous, Auto Layout and trait-driven assets should be able to handle those specifics both at launch time and during run time.

Will Apple introduce a 3x display resolution? I think it unlikely but if they do, the technology is already in-place to handle that trait collection with a 3.0 display scale. Will Apple add a new geometry to the phone family? That’s a rumor that seems to have more appeal — but Auto Layout is ready for that, and the compact-regular layout traits will support any baby-dolphin-sized phablet that might hit the market. Will iOS 8 support multi-app display? The technology to support it is ready for developers if they take advantage of those features.

I admit that some of Apple’s design choices for trait collections baffle me. For example, how is it that a 480-point height is “regular” and a 586-point width is “compact”? But despite that, I find it comforting to  receive a concrete set of interface expectations via the trait API.

What do you think? Please do let me know.

Beta: Auto Layout Constraint Activation

I’m pretty excited about the new active constraint property. To date, a lot of my work involves adding and removing constraints. Now with activateConstraints: and deactivateConstraints: you can enable and disable constraint groups all at once. I can see this being used in a variety of ways:

  • Disabling constraints for dynamic animators — you don’t have to layout and then remove items
  • Choosing constraint sets for various states — you can use one set for example when a view is visibly presented and another if it is hidden or create closed and open presentations.
  • Bringing individual constraints on-line or off-line that have competing priorities, so you can disable rather than remove a higher-priority item until it is needed again.

I’m not sure if this is animatable so I have to test that bit, but I’m really hoping it is.

One final note: the two activator/deactivator methods are documented to be more efficient than accessing constraints one-by-one.

UPDATE: WHOA! Self-installing constraints! Finally!

UIView *v = [UIView new];

v.autoLayoutEnabled = YES;

[self.view addSubview:v];

NSLayoutConstraint *c = [NSLayoutConstraint constraintWithItem:v attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeCenterX multiplier:1 constant:0];

NSLog(@”%@”, self.view.constraints);

c.active = YES;

NSLog(@”%@”, self.view.constraints);

2014-07-11 16:44:09.138 Hello World[18020:1138706] (

)

2014-07-11 16:44:09.141 Hello World[18020:1138706] (

    “<NSLayoutConstraint:0x7fed2d809090 UIView:0x7fed2d9449b0.centerX == UIView:0x7fed2d93da70.centerX>”

)

 

Beta: Coordinate spaces

New in beta 3, coordinate spaces are a UIScreen property. There’s a UICoordinateSpace protocol for UIViews and APIs to support point conversion, presumably for split-screen app development.

UIScreen.h

Added UIScreen.coordinateSpace
Added
UIScreen.fixedCoordinateSpace

UIView.h

Added UICoordinateSpace
Added UICoordinateSpace.bounds
Added -[UICoordinateSpace convertPoint:fromCoordinateSpace:]
Added
-[UICoordinateSpace convertPoint:toCoordinateSpace:]
Added -[UICoordinateSpace convertRect:fromCoordinateSpace:]
Added -[UICoordinateSpace convertRect:toCoordinateSpace:]

 

Building custom extension templates

I recently posted about building file templates to create custom playgrounds. Today, I found myself facing a similar dilemma with application extensions. Working with project templates is a bit fussier than file templates, mostly because of the way you customize and register items.

To start, copy a base template from the built-in templates folder. If working with normal Xcode, this is usually /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/Library/Xcode/Templates. With Xcode 6, substitute the Xcode6-Beta file name into the path.

If needed, build a path down from ~/Library/Developer/Xcode/Templates/Project Templates/Application Extension and place the copied template there.

Screen Shot 2014-06-30 at 11.58.15 AM

 

I wanted to build an action extension that wasn’t based on storyboards. You’ll notice that there are quite a lot of files in the modern Xcode 6 template. Since I was only interested in Objective C at this time, I ignored everything that wasn’t part of the ActionViewController.[hm] material. I also deleted the MainInterface.storyboard file from the copied extension.

Next, I had to edit the TemplateInfo.plist file so it would create the right kind of Info.plist for me — that is, one that had a primary class to create (NSExtensionPrincipalClass) and won’t try to load a storyboard (NSExtensionMainStoryboard). The problem is that the property list is actually a meta-property list, one that tells Xcode how to create the actual Info.plist from its template. I recommend highly that you copy the default version someplace before you start editing, so it’s easier to recover from mistakes and try again.

For me, the Info.plist section I ended up with after editing looked like this. And yes, this included all the ampersand-escape sequences. (Escaping the escape sequences for this blog post was a lot of fun. not.) That’s because the template file entry for “Info.plist:NSExtension”  includes escaped content in its string values.  So be aware that you’re diving into areas that Apple either never intended to become public or did a really poor job of making publicly editable.

&lt;dict&gt;
    &lt;key&gt;NSExtensionPrincipalClass&lt;/key&gt;
    &lt;string&gt;ActionNavigationController&lt;/string&gt;
    &lt;key&gt;NSExtensionPointIdentifier&lt;/key&gt;
    &lt;string&gt;com.apple.ui-services&lt;/string&gt;
    &lt;key&gt;NSExtensionAttributes&lt;/key&gt;
    &lt;dict&gt;
        &lt;key&gt;NSExtensionActivationRule&lt;/key&gt;
        &lt;string&gt;TRUEPREDICATE&lt;/string&gt;
        &lt;key&gt;NSExtensionPointName&lt;/key&gt;
        &lt;string&gt;com.apple.ui-services&lt;/string&gt;
        &lt;key&gt;NSExtensionPointVersion&lt;/key&gt;
        &lt;string&gt;1.0&lt;/string&gt;
    &lt;/dict&gt;
&lt;/dict&gt;

Make sure you include any tweaks needed to make your template sound here, including adding and removing file references needed to build the project. Once you’ve done that, go ahead and create or modify the files you want generated in the template’s folder. For me, I edited ActionViewController.[hm] to produce a skeletal no-storyboard version, centered around a navigation controller so that I didn’t have to resort to a standalone navigation bar with a done button. (If you don’t know what I’m talking about, build a new action extension in some project and see how the built-in skeleton is built. It’s not pretty.)

Finally, you’ll need to update the project template’s identifier (Identifier key at the top of the TemplateInfo.plist file) so it doesn’t conflict with the standard template and add a custom description (Description key) to distinguish its use-case. Quit and restart Xcode, and your new template should be visible and ready to use.

iOS 8: traits, assets, ridiculousness

Screen Shot 2014-06-19 at 4.52.53 PM

iOS 8 has all the right intentions and none of the right implementation when it comes to asset management. While I get that people will want art that adapts from “compact” to “regular” sizing, creating catalogs with 20 images at a time (for 5 possible platforms: iPhone, iPhone Retina, iPhone 4″ Retina, iPad, iPad Retina, for compact and regular sizing, for both Retina and non-Retina screens) is frankly ridiculous.

Yes, there are items you’ll want to tweak based on orientation, overall size, and whether the application is compacted into just a portion of the screen (or displaying, perhap,s on a Mac or Apple TV) — but you can do this far better with vectors and in-app sizing and layout requests than generating and dropping dozens of images. Even with bitmap rendering, a sensible developer will likely want at most two copies of the asset — one large, one small, that will work on Retina displays. Just let the OS squeeze or downsample or whatever. What’s more, the JSON that powers the asset system is pretty scary at the moment, inconsistent, and undocumented.

I’m feeling pretty cranky right now as I just spent a large part of today exploring the whole trait collection system and finding that it’s very (unsurprisingly) beta and fussy. While I applaud Apple for separating device- and target-specific characteristics into a system of traits, I don’t really feel it is currently put together in a thoughtful and holistic way. It would be way easier if you could just specify the size differential to the layout system rather than dealing directly with twisty little images all mostly alike.

There’s so much I love about adaptable views, the image system so far isn’t one of them.

Xcode 6: Loving the IB-Renderable Views

Despite my tendency to do most things in code, this stuff is really amazing. Here’s my quick “how-to”, followed by sample code, followed by a few final thoughts.

  1. Create app (I used single view)
  2. Select the app, in TARGETS, add a new Framework & Library > Cocoa Touch Framework target, e.g. CustomViews
  3. Add the .h/.m files to your project, making sure the framework box is checked and not the app one.
  4. Drag a view into your main storyboard. Color the background so you can see it, and adjust its size.
  5. With the view selected, open the Identity inspector. Change the Custom Class > Class to your class name. For example, TestView as in the following code.
  6. Switch to the attributes inspector and adjust the border color, width, and corner radius. The view should update in real time.
  7. Save. Compile. Run.
#import <UIKit/UIKit.h>

IB_DESIGNABLE
@interface TestView : UIView
@property (nonatomic) IBInspectable UIColor *borderColor;
@property (nonatomic) IBInspectable NSInteger borderWidth;
@property (nonatomic) IBInspectable NSInteger cornerRadius;
@end

@implementation TestView
- (instancetype) initWithFrame:(CGRect)frame
{
    if (!(self = [super initWithFrame:frame])) return self;
    _borderColor = [UIColor blackColor];
    _borderWidth = 0;
    _cornerRadius = 0;
    
    return self;
}

- (void) setBorderColor:(UIColor *)borderColor
{
    _borderColor = borderColor;
    self.layer.borderColor = _borderColor.CGColor;
}

- (void) setBorderWidth:(NSInteger)borderWidth
{
    _borderWidth = borderWidth;
    self.layer.borderWidth = _borderWidth;
}

- (void) setCornerRadius:(NSInteger)cornerRadius
{
    _cornerRadius = cornerRadius;
    self.layer.cornerRadius = cornerRadius;
}
@end

Key things to notice:

  • The IB_DESIGNABLE keyword before the subclass interface declaration
  • The IBInspectable keywords for the items that show up in the attributes inspector

Beyond that, be aware that the frame is constantly being redrawn. Although you don’t have to use drawRect:, despite what the docs show, you can add live feedback by updating the text for labels or using string drawing in a custom drawRect: routine. Use the TARGET_INTERFACE_BUILDER target test if you want to comment that stuff out for any other use, though. Auto Layout is available both in code for building up the view and in IB for coordinating this view with other views in storyboards and nibs with this stuff. Views don’t have to be simple like this example. You can add subviews and they’ll show up just fine, both in execution and in IB. Although you can’t interact with any controls (for example, buttons or switches) that you add as subviews in your class definition file,  you can connect them to action targets in IB.

iOS 8: Building custom simulators

Right now in Xcode 6, there are plenty of simulators to choose from but did you know that you could create your own simulators and customize them for specific testing needs?

The simctl command line utility, which you find at /Applications/Xcode6-Beta.app/Contents/Developer/usr/bin/simctl, enables you to create new custom simulators built to your specifications.

You start by creating a device, setting its class and its firmware. You discover the list of installed device types and simulator runtimes by issuing the simctl list command.

== Device Types ==
iPhone 4s (com.apple.CoreSimulator.SimDeviceType.iPhone-4s)
iPhone 5 (com.apple.CoreSimulator.SimDeviceType.iPhone-5)
iPhone 5s (com.apple.CoreSimulator.SimDeviceType.iPhone-5s)
iPad 2 (com.apple.CoreSimulator.SimDeviceType.iPad-2)
iPad Retina (com.apple.CoreSimulator.SimDeviceType.iPad-Retina)
iPad Air (com.apple.CoreSimulator.SimDeviceType.iPad-Air)
Resizable iPhone (com.apple.CoreSimulator.SimDeviceType.Resizable-iPhone)
Resizable iPad (com.apple.CoreSimulator.SimDeviceType.Resizable-iPad)
== Runtimes ==
iOS 7.0 (7.0 - Unknown) (com.apple.CoreSimulator.SimRuntime.iOS-7-0) (not installed)
iOS 7.1 (7.1 - 11D167) (com.apple.CoreSimulator.SimRuntime.iOS-7-1)
iOS 8.0 (8.0 - 12A4297e) (com.apple.CoreSimulator.SimRuntime.iOS-8-0)

For example, I might want a 7.1 install on an iPhone 5 to test my Fidget software. You provide simctl with the reverse-domain specifiers for each. So with these constants, the command I issue to create this simulator is as follows:

simctl create Fidget71iPhone5 com.apple.CoreSimulator.SimDeviceType.iPhone-5 com.apple.CoreSimulator.SimRuntime.iOS-7-1

Once built, issue the list command again to retrieve the UUID for your new device. You supply this UUID with each command to control your simulated device.

...
== Devices ==
-- iOS 7.0 --
-- iOS 7.1 --
    iPhone 4s (07357684-4C14-49A1-BD57-8C435CE7611E) (Shutdown)
    Fidget71iPhone5 (D6E1DD7F-712D-4273-A083-F9C9A12AED38) (Shutdown)
    iPhone 5 (F3FE995D-4808-4153-8800-E4EBB94DCB95) (Shutdown)
    ...

Each simulator is stored in ~Library/Developer/CoreSimulator/Devices. Each device folder stores its contents in the data subfolder. This enables you to hop in and inspect files as you run, for example looking at preferences or examining files in the Documents folder.

Once installed, Xcode presents your custom simulator along side the other standards. You select the simulator in Xcode and run apps as normal. Simctl keeps a running list of device states, so as you test, your device is listed as (Booted). Otherwise it is (Shutdown).

You run the simulator by opening it from its default location.

open /Applications/Xcode6-Beta.app/Contents/Developer/Applications/iOS\ Simulator.app

Then,from the command line, you can issue commands. For example, you can have it open a URL:

simctl openurl D6E1DD7F-712D-4273-A083-F9C9A12AED38 https://ericasadun.com

or add a photo to the photo library on the device:

simctl addphoto D6E1DD7F-712D-4273-A083-F9C9A12AED38 ~/Desktop/test.png

or launch an application:

simctl launch D6E1DD7F-712D-4273-A083-F9C9A12AED38 com.sadun.SwiftWorld

The utility comes with a pretty good help system. Just issue simctl without any arguments (or with –help) for more details.

Found some other cool tricks? Please let me know.
Thanks, “A”

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.

 

Calculating marching ants

UnknownAlthough I use a CADisplayLink to update my dashed-selection overlay, I don’t want my marching ants overlay to depend on that timer for phase.

To that end, I worked away at a solution where I specify the speed of the progression (the seconds it takes to complete a frame) and calculate an offset based on the current time. I like this timing solution a lot better than ones that update a counter every time the display link pings, plus it lets me slow down the link’s frame interval without worrying about changing the update speed.

I’ve been using this to power a user-selected area today and it’s had no problem on-device with slightly older units in testing using CA Instruments. I’m still not in love with marching ants — moving things on my screen bother me a bit — but I did like the bit about using real time you see here.

- (void) drawRect:(CGRect)rect
{
    CGContextRef context = UIGraphicsGetCurrentContext();
    CGContextClearRect(context, rect);

    CGFloat dashes[] = {12, 3};
    CGFloat distance = 15;
    CGFloat secondsPerFrame = 0.75f; // adjust as desired

    NSTimeInterval ti = [NSDate timeIntervalSinceReferenceDate] / secondsPerFrame;

    BOOL goesCW = YES;
    CGFloat phase = distance * (ti - floor(ti)) * (goesCW ? -1 : 1);
    [path setLineDash:dashes count:2 phase:phase];
    [path stroke:1 color:WHITE_LEVEL(0.75, 1)];
}