Quirks

When does Auto Layout kick in?

In my usual loveable (perhaps somewhat annoying) style, this is a rambling story of my struggles with Auto Layout. If you want to cut to the chase, feel free to skip to the last few paragraphs.

I must admit that I have long resisted the charms of Auto Layout. When I tried to use it I felt as though I was fighting it to get it to do what I wanted, and that it was a liability rather than an assistance. Of course this is often the way when you first start using a powerful feature. It takes time to learn enough to be productive.

Recently I sat myself down and gave myself a stern talking to about this situation. Having mulled over the wisdom of my words I buckled down to come to grips with Auto Layout.

I created a small testbed app to simulate the main behaviours I wanted to implement in an existing app. Within a couple of hours I had that working fine, so I started to implement it in the existing app. That went pretty smoothly but one seemingly strange behaviour kept bugging me …

I laid out my UI in the standard 600x600 view that Interface Builder presents. Of course this meant that many of the UI elements were a different size than would be the case on an actual device. But that’s OK because that is the entire point of Auto Layout. Right ? Put the UI on a device and Auto Layout does it’s thing and all is well.

The problem was that “the first time around” my code was seeing sizes for UI elements that were appropriate for the 600x600 layout, but not for the device. At “some later point” in the code Auto Layout seemingly kicked in and the sizes were as expected. It wasn’t immediately clear when this happened.

On the whole the app was working fine, which in itself was a bit confusing if it was initially thinking it was on a 600x600 view. The issue showed up in some minor ways, but not enough to stop me cold. Nevertheless this niggled at me. Obviously all was not working as I expected or could explain - never a good situation if you want stable code.

My first thought was to force Auto Layout to kick in earlier in the process. I played around with a few options until in desperation I resurrected a real kludge that was commented out in my code, left over from trying to troubleshoot a rotation problem a few years ago. This involved presenting and dismissing a modal view controller with no view (yes, yes, I know).

This came up with a warning about presenting a view on view controller that was not in the view hierarchy. Bingo! Everything suddenly clicked into place.

The code that was seeing the non-updated layout was being called from viewDidLoad, so was occurring before the view was added to the window. Without being part of the view hierarchy Auto Layout had no overall size to play with so could do nothing. When I moved the code from loadView to viewDidAppear: it worked as expected.

So the moral of this story is do not interrogate sizes and positions of UI elements that you expect Auto Layout to manage until after the view is in the view hierarchy (ie. viewDidAppear or later).

A corollary to this is do not remove a view from the hierarchy and expect it to keep it's Auto Layout controlled attributes. I had a bit of logic that swapped a view out of the hierarchy to rebuild the subviews and then swapped it back in. Under Auto Layout it appeared to get a zero size when swapped out.

The other moral of this story is never accept unexplained behaviour in your code. It can be monumentally frustrating at times but when the lightbulb goes on, all becomes clear and it is marvellous. These moments are what I love about being a developer.

Quirk of the day - UIButton states and Images

UIButtons and I don't seem to get on. Embarrassing but true. Controls don't come much simpler than a button, but I still have spent an inordinate amount of time wrestling with them.

My latest episode of button distraction was trying to get a button to show different images in two states. Simple, right? Done it many times before. You just set different images for different states and toggle the states. I traditionally use the selected state for this.

First attempt

That works fine ... until I disabled the button while selected. At that point the button showed a greyed-out version of the unselected image. When enabled again the selected image came back.

So I spent some time making sure that I wasn't toggling the selected state myself as part of the disable/enable logic. No that wasn't the problem.

Time for something new and radical - I reread the documentation and thought about was what was going on.

As often happens the reason for the behaviour is simple and obvious ... disabled is just another state and it gets added (or'ed actually) into the button's state. Disabled | selected is not the same as selected so my explicit selected image was not used.

The obvious solution was to set the same image for selected | disabled as well. As with many obvious solutions, it was wrong.

Second attempt

Now when I disabled the button I got the right image, but it wasn't greyed out in the normal manner for disabled buttons! Apparently greying-out the image is only the behaviour when an image is not specified for the disabled state and the normal state image is being used.

Of course this makes sense. In most cases when you explicitly specify a disabled image you want to use that image as is, not have a greyed-out version forced upon you.

I could see two alternatives - (1) create a greyed-out version of my selected icon for use when disabled, or (2) abandon using the selected state mechanism completely and set the image for the normal state as required and let the default disable behaviour grey them out.

Unsatisfactory result

Fiddling around with another icon image and getting it to match the system generated greyed-out icons seemed like a lot of work and destined to not look quite right, so I went with option 2.

Not a particularly satisfying resolution as instinctively it feels a bit clunky, but it works and I now know a little more about button behaviour than I did.

Quirk of the day - NSMakeRange

Oscar Wilde may have said "Consistency is the last refuge of the unimaginative", but consistency in method and function names has a lot to recommend it.

For example, given the ever popular and useful …

CGTypeMake

… you would be forgiven for expecting there to be …

NSRangeMake

But no, instead it's …

NSMakeRange

My poor old neurones are taxed enough without this.

As a random aside, years ago (decades in fact) I enjoyed exploring IBM's AS/400. The command language had a beautifully consistent system of concatenating three letter abbreviations to form commands. So if you didn't know the command to power the system down, you could take a pretty good guess that it would be "pwrdwnsys". Want to do it right now, just add a "*immed". Hours of fun.