He has already published a few detailed investigations & deconstructions of user interface elements from recent interesting iOS apps.
Check out these excellent articles and follow him on Twitter.
He has already published a few detailed investigations & deconstructions of user interface elements from recent interesting iOS apps.
Check out these excellent articles and follow him on Twitter.
Recently I was creating a banner for an informational page in one of our iOS apps. The designer had specified three centered lines of information about a property, as a static header above a map and some scrolling information below. As you would think, a perfect case for contained view controllers and constraint-based layout. But what appeared to be the easiest part turned to contain an interesting problem…. The base view controller (“self”) starts by apply some basic constraints to a UIView named bannerView at the top of the screen. (The iOS6 case is not shown here for clarity):
1 2 3 4 5 6 7
The banner view is anchored to the top of the view controller’s view and is constrained to its full width. At this stage bannerView has no frame set and won’t display anything, so it needs to supply its own height constraint. This is achieved by relying on the intrinsic height of its contents – each label is placed in the banner view and centered horizontally by constraint, then bannerView has a constraint applied that stacks each view and at the same time gives the view its intrinsic height, the total of the heights of those views. You could say it really ties the view together. (This is one of the tough concepts in auto layout: constraints don’t apply in one direction, they are all applied and solved simultaneously. Conflicts and ambiguities are the only problem, either the parent or the subview can supply the dimension.) An example:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
The end result is the completed bannerView, anchored to the top of the containing view controller’s view. If the designer decides to alter the font size or order of the labels or the size of featureView then the banner view will be resized through its constraints automatically. In the view controller the next lower view is anchored to the base of the banner view so everything maintains its arrangement. Job done! Or so I thought. A look at the result shows not the three nicely centered elements expected, but two centered elements and one seemingly left aligned on the centre. As the bannerView has been selected, Reveal shows its bounds (in 2d mode) with the blue outline. That’s strange, since the view has the same NSLayoutAttributeCenterX constraint applied as the labels. The featureView is actually a view containing a single label. Once upon a time it was a view containing three icons and three labels, but to improve scrolling efficiency (remove transparency) a font was created containing glyphs for those icons, and the view created using the label’s attributedText property. For a quick sanity check, what if the view is a plain UIView?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
So everything is working with a plain view. Why doesn’t the features view work? The reason this view changed is because Instruments called it out as containing transparency. Image blending can really hurt scroll performance and you can find out which views have the feared transparency with Instruments – while running on a device with the Core Image instrument, check the “Color Blended Layers” box. You will find everything shaded green except for those views that use transparency, and they will be red. Red indicates SLOW. One way to get around this is to use opaque source images and arrange your layout so that no transparency is required. Another is to switch strategies, as in this case. A label using attributed strings created with a font designed with custom glyphs (replacing the original transparent UIImageViews) doesn’t trigger this warning. Examining the creation of the view reveals nothing to worry about. Strictly speaking this label could be added as a label rather than adding a label to a view and then using the view, but it remains the way it is to avoid touching too much other code that expects it to be a view. Experimenting with the internals of the view doesn’t change the way it left aligns on the centre either. Time to bring up the big gun, Reveal.
Looking at the UIView portion of Reveal’s inspector window for the feature view vs. the UILabel above it shows right away that there is no intrinsic content size set for the feature view, and it has a bounds of CGRectZero. This hasn’t mattered previously because the feature view has been used with conventional layout, explicit frame setting rather than constraints. What happens if an intrinsic content size is supplied for the custom class?
1 2 3 4 5 6 7 8
Running the app again shows that the view is just where it should be. A quick look in Reveal shows the intrinsic content size is as expected – clicking on the feature view shows its intrinsic content size is the same as for the label it contains, as we want. Adding a new view controller using constraint-based layout exposed an issue that didn’t cause problems in frame-based layout. Reveal uncovered the problem and verified the solution.
We are a mobile consulting and product development company based in Melbourne Australia. If you’re an iOS developer, you might know us from our runtime introspection tool Reveal. If you’re in Australia and you’ve ever used the realestate.com.au or SEEK apps, you’ve seen some of our handiwork.
We are passionate about great software engineering, UX and design. You will need to be too, and more than that, you’ll need to be able to point to products you’ve worked on that reflect your own high standards.
We are intimately involved in the iOS and Mac development community in Australia, organising Melbourne Cocoaheads meetups which regularly attract 80+ attendees each month. We’ve also been instrumental in organising Swipe, Australia’s first iOS and Mac developer conference. You will need to have a similar passion for community and knowledge sharing.
Our work environment is relaxed but focussed on achieving great results for our clients and delivering amazing development tools to our customers. Most of our consulting engagements are on-site with clients, where we can help shape the design and implementation of their mobile products. You’ll need to be enthusiastic about working embedded with their teams and in their environments (largely agile).
Reveal product development is done in-house, and whether you join us as an iOS or Android developer, you will have opportunities to work on this amazing product.
Our office is located in one of Melbourne’s best laneways for food and coffee in the heart of the city. You will not be left wanting for single origin or cold drip coffee, if that’s your thing.
We can sponsor international applicants via the 457 Temporary Work Visa and permanent residency applications. You will need to have an undergraduate degree to be considered for sponsorship. This could be the opportunity you’ve been looking for to move to one of the most liveable cities in the world.
Candidates with a track record of Open Source contributions and a mastery of multiple programming languages and platforms will be highly regarded.
If you’re interested in applying for any of the following roles, send an email to firstname.lastname@example.org telling us about your experience and links to examples of your work. Salary packages are commensurate with experience.
No recruiters please.
Are you an iOS developer looking to challenge yourself and work with a team of A+ players? This is an opportunity to join one of the most respected iOS development companies in Australia and take your skills to a new level.
Have you been doing Android development for a few years? Looking for your next challenge? This is an opportunity to join us as a lead Android developer, share your knowledge with the team and develop some amazing products.
Are you an old hand at AppKit? Been cutting Objective-C since the NeXT days? Are you constantly telling those UIKit whippersnappers to get off your lawn? Have you been instrumental in delivering complex Mac apps to market? If so, you could be the one to help take Reveal to the next level. This is a unique opportunity to become a core member of the Reveal development team.
If business reasons require you to continue supporting iOS 6, this means you may need to work with both iOS 6 and 7 for quite a while. Developers always hate this, as it may increase the code base cyclomatic complexity (e.g. if iOS 6 do this, else if iOS 7 do that…) . I love it, since my wife, mum, dad and manager are still using iOS 6.
Here is a very simple app running on iOS 6.
After switch the simulator to iOS 7, the label is missing
Why? Let’s reveal it.
The label is actually behind the NavigationBar. In iOS 7, apple introduced a new property called
[UIViewController setEdgesForExtendedLayout:] and the default value is
UIRectEdgeAll. When your container is navigation controller, the default layout will start from the top of navigation bar. This is why all of the UI elements had been shift up 44pt.
A quick way to fix this issue is add the following snippet to method
Now, it’s been fixed.
Let’s run the app on iOS 6 then, we found the following runtime exception.
All iOS 7 only API invokes need to be wrapped with proper guarder.
1 2 3 4
Some machines might still running with a Xcode 4.6. When they pull the latest code, they will fail to compile then.
To avoid the compile stage error, we need to create the following macro.
1 2 3
Then wrap the iOS 7 code path if needed.
1 2 3
As a UILabel, iOS 7 default background color is clearColor (which make sense most of the time), while the iOS 6 default value is white. So we’d better to explicit set the label background color.
In iOS 6, when we call
presentViewController, the default modal screen will be full screen. (
In order to achieve a consistent experience on iOS 7, we add the following code to modal controller.
1 2 3 4
Coming to iOS 7, the keyboard skin becomes lighter, so we need different barStyle for various iOS version.
1 2 3 4 5 6 7 8
It is definitely more than that. The above tips are just some of the most common issues I have seen when working on iOS 6 & 7 codebases.
So you use Reveal and you love it. But you’ve just fired up a new app in Xcode’s debugger and you want to have a quick look at the internals of the app’s view hierarchy, but you haven’t integrated Reveal via the static or dynamic library yet. What to do?
No problem! LLDB to the rescue.
LLDB can execute arbitrary code inside your running application. So you can just load Reveal into any iOS process you are currently debugging.
Just hit ^⌘Y to pause the iOS app in the Xcode debugger and then at the
(lldb) prompt type:
1 2 3
OK, so maybe not so simple and easy to remember. Wouldn’t it be better if we could just
reveal_load at the LLDB prompt or something like that?
Well, we can! LLDB supports command aliases. Just like with bash, you can create an alias
in LLDB for a more complex command. The syntax is a bit different but the concept is the
same. Also, just like bash, LLDB has a ‘dot file’ that it loads every time it starts
which is the ideal place to put your LLDB command alises. That dot file is
Open up your favourite text editor, create the file
.lldbinit file in your home
directory and chuck the following LLDB command aliases into it:
1 2 3 4
The above snippet creates four command aliases:
reveal_load_sim– This alias only works when running your app on the iOS Simulator. It loads the
libReveal.dylibfrom the Reveal application bundle (assuming you put Reveal in your system’s Applications folder). If you have Reveal elsewhere change the alias to reflect this.
reveal_load_dev– This alias works when running your app on device or in the iOS Simulator. It however assumes you have added the
libReveal.dylibbundled with Reveal to your application’s Copy Resources build phase. (Make sure it is not in your Link Binary with Libraries build phase. Yes, I was fibbing a little about not having to modify your Xcode project. Sorry.
reveal_start– This alias posts a notification via NSNotificationCenter to start the Reveal server.
reveal_stop– This alias posts a notification via NSNotificationCenter to stop the Reveal server.
Note: *Invoking the
reveal_start command is only required if you invoke one of the
commands after iOS has posted the
i.e. After your application delegate has handled
With these aliases defined you can now issue these quick commands at the LLDB prompt in Xcode. LLDB even auto-completes them for you!
Ok so now you have some quick LLDB command aliases and can trigger them manually, how do you make it more automatic?
Xcode (LLDB really) can execute commands in response to breakpoints. Using this knowledge we can add breakpoints to our application that load and start Reveal automatically.
The image above shows how you can add the Reveal LLDB commands to a break point in Xcode.
Some things to note about this breakpoint:
application:didFinishLaunchingWithOptions:you don’t have to actually issue the
reveal_startcommand. Reveal automatically listens for
UIApplicationDidFinishLaunchingNotificationand starts automatically when this notification is fired by an app. There is no harm in trying to start Reveal twice. You might just see duplicate log messages.
When the breakpoint commands are successfully executed by LLDB you will see something like the following in the debugger console:
Now you’re armed with LLDB and Reveal super powers!
Recently I was coding in Xcode 5 (not something I tend to do much as an AppCode user), and I noticed a bug when trying to view the quick help for a symbol. This is usually done by option clicking on a symbol in the Xcode editor pane. The quick help popup window would show up and then rapidly shrink down to nothing. Most mysterious.
You can see this effect in the embedded video below.
This behaviour is not good, so I filed it as a bug with Apple (rdar://15285710). Apple’s bug report team helpfully got back to me today and I was informed that if I removed an Xcode preference the problem would go away.
I made sure Xcode wasn’t running and then issued the following
defaults command at the
terminal command line:
On relaunching Xcode the quick help popup was back to behaving as it should.
And here is the tl;dr video:
The outline view gives you a detailed one-for-one representation of the application’s view hieararchy. For a lot of apps, their view hierarchy will be massive, and sometimes scrolling and clicking just doesn’t cut it. My most used shortcut in Reveal has to be selecting the current view’s superview for those times I clicked the wrong view in the canvas.
These are the shortcuts that help you pop around the tree with ease:
Because the outline uses native OS X controls, system defaults for expanding and collapsing nodes are the same as Finder
Now, one of the most eye-zappingly-awesome parts of Reveal is the 3d Canvas, which has quite a few features that people are yet to discover. There is a collection of mouse/trackpad combos to pan, rotate and increase the z-depth of the 3d representation.
Easy management of Zoom levels:
Reveal lists every* UIView in the view hierarchy from the UIScreen down. Because that easily becomes an excessive amount of views to scroll through, Reveal allows you to drill-down and isolate your focus on a particular subset by simply double clicking a view:
I personally use this focussing a lot, especially when iterating with designers or any time I have to manipulate a UITableViewCell.
Once ‘focussed’ on a subset of the view hierarchy, you can navigate your way back up the tree using the path-bar at the top of the canvas. This is really helpful for those times you accidently double-clicked a view that was in front of the view you wanted to focus on.
You can also go back and forth in your focus history with the left/back buttons or the key-board shortcuts:
* Reveal cannot see into a UIRemoteViewControllers’ views such as
UITextInputTraits protocol has methods for setting what sort of keyboard
should be shown when a user taps on a
UITextView and other such things.
iOS 7 also introduced a new
selectable property on
UITextView for controlling whether
text selection is enabled. This is much better than the methods required in previous
releases of iOS, where you had to subclass UITextView and return no for
canBecomeFirstResponder or alternatively set an
handle the appropriate delegate callbacks to stop selection for occurring.
Unfortunately, setting both
NO in iOS 7 breaks all of the
UITextInputTraits methods. If you attempt to call any of them your app will crash
instance does not respond to selector exception. Which is weird because the
UITextView instance will return
YES if you call
respondsToSelector: for any of the
methods declared in the
This appears to me to be a bug in iOS 7. I’ve reported it to Apple as radar://15063164.
I’ve also noticed that for UITextView’s created via code (rather than in Storyboards) this bug
doesn’t express. I’m not sure why yet. There may be additional properties at work or
the fact that the in a Storyboard a UITextView is initialised via
initWithCoder: rather than
initWithFrame: and may be in a different state due to this.
Some code that shows the bug:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65
I have a confession to make. I’m an AppCode user. I hardly use Xcode at all.
If you’ve never used AppCode before I suggest you take a look at it, and in particular play with its support for refactoring. Some people can’t get past AppCode’s slightly ugly Java based UI, but if you do get to know AppCode’s keyboard shortcuts and power features I think you will agree it is hard to beat as a Cocoa development tool. It makes me a much more productive developer. I feel like some of my fingers have been removed when using Xcode.
But I digress, this post isn’t about AppCode, it is about Xcode. How to get it to act more like AppCode. Err…. I mean, expose Xcode’s (somewhat limited) refactoring abilities in a better way than is the default, and also to learn about the small in number but still useful code navigation shortcuts Xcode has.
Xcode has some limited support for global code refactoring. Tucked away in the Edit menu is the Refactor sub-menu. None of the items in this menu have keyboard shortcuts by default which makes them about as useful as a poke in the eye.
We are going to correct this considerable oversight.
You can edit any of Xcode’s keybindings in its Preferences on the Key Bindings tab. Here you
can search the list for all the Refactoring items by typing
refactor in the search
field. Then double click in the
Key column to enable the shortcut editor and then
press the key combo you want to use. Xcode will tell you if you’ve created a conflict
with an existing key binding. Finding a key combo you like that doesn’t have an existing
binding, and thus conflict, can involve a bit of trial and error.
Apple has some documentation on the refactoring workflows here. It is always a good idea to consult the official documentation. Below I provide a quick synopsis of the main refactoring actions.
Rename– Rename should be obvious. It lets you rename methods, classes, etc and have those changes applied across your entire codebase. Think of it as a smart, semantic, find and replace.
Extract– Extract gives you the ability to extract a piece of selected code into a new method or function. It will try and smartly determine what parameters and return values to give the new method based on surrounding code.
Move Up– These actions allow you to move a method, property or instance variable up to its super class.
Move Down– You would think that
Move Downwould do the inverse of
Move Up, and it does, kinda. Unfortunately it only works with instance variables. Which makes it not as useful as
Move Upwhich can also move methods.
Encapsulate– Encapsulate is useful for when you want to convert all uses of an instance variable to into accessor methods, thereby encapsulating that instance variable behind those methods.
Once you’ve added key bindings for these refactoring actions I encourage you to experiment with what they do. Try them out on parts of your code. You may discover that you quickly come to rely on some of them in your day-to-day coding activities.
When you first use a refactoring action in Xcode it prompts you to enable Snapshots. I don’t know if anyone has actually clicked the Enable button in the entire history of Xcode. But if you’re worried about Xcode accidentally obliterating your code, it is there if you want to use it. But you use source control right? So that shouldn’t be a problem, right?
Important: One thing to be aware of however if you do not enable snapshots is that Xcode’s support for undoing changes made by a refactoring is a bit limited without a snapshot. Even when you do save a snapshot restoring the snapshot doesn’t always restore your project perfectly. It might leave you with changes you will need to un-stage in your git repo. This is probably most important to remember when performing a rename refactoring on a class or method used widely in your project.
If you do want to enable/disable snapshots for mass edits like refactoring actions you can
do so in
File -> Project Settings (or
File -> Workspace Settings if you’re using a
Note also that you can create a snapshot at anytime with ^⌘S. Restoring a
snapshot doesn’t have a default key binding. It is in the Xcode
File menu. You can
manage your project’s snapshots in the Xcode Organizer on the
Although Xcode provides a
Rename refactoring action. This action, like all
the Xcode refactoring actions, has quite a heavy UI. A quicker more constrained way of
renaming things like variables is to use
Edit All in Scope ^⌘E which is in the
Editor menu. This gives you a quick in-line rename action for renaming variables,
parameters, methods and the like. It only changes items in the current scope (and file) so it
won’t rename methods in the header or usages in other files like the
action does but it is good for quick local scope renames.
Another essential keyboard (and mouse) shortcut to know is
Jump to Definition ^⌘J
When your cursor is on a symbol (method, variable, whatever) you can press this key combo
to quickly jump to the definition (or if you are on the definition to the declaration)
of that symbol. Very handy.
You can do this with the mouse too, just hold ⌘ when you click on a symbol in your code.
Probably the most essential keyboard shortcut in your Xcode arsenal is
Open Quickly ⇧⌘O.
You should be using this keyboard combo all day to quickly jump around your code. You can
search for symbols and filenames in this dialog. Also note that you can use abbreviations
and partial matching in the open quick search field. So if you have a View Controller
AwesomeViewController in your project you can search for
Open Quickly and it will be found.
Hopefully you’ve found these Xcode tips and hints from an AppCode user to be useful.
One of the significant visual changes in iOS 7 is the ‘flattening’ of the UIPicker, and by association the UIDatePicker. On the surface, the new picker looks much cleaner. I’ve never been a fan of the heavy-handed visual treatment given to the iOS 6 picker, and the pseudo 3D rotational effect using gradient overlays probably grated on most anal engineering and designer types. ‘They’re faking it!’, I thought. So when the new iOS 7 picker arrived on the scene there was a piece of me that thought, ‘cool, they’ve cleaned it up and done the 3D effect properly’. That was until I got to use the thing.
I found the new picker quite visually pleasing initially but having used it, I’m not so sure. For example, the UIDatePicker has simplified the colour palette to black and grey, which makes it harder to distinguish the day from the month. The colour highlight on ‘Today’ has been lost too. They’ve also squeezed in an extra two rows above and below the selection, which from an information density perspective is great, but it has reduced the hit areas of each row. Worse than that, the hit areas on the UIDatePicker are inconsistent. Marc Edwards summarised some of these issues a few weeks ago in this tweet.
Say what you will about the visual styling, but iOS 6’s picker was far easier to use. http://t.co/9gzINNGWqM— Marc Edwards (@marcedwards) September 1, 2013
As you can see from Marc’s diagram, you can no longer drag above and below the AM/PM column to move it. This is a huge pain because now you have to place your finger over the content in order to make your selection. In iOS 6 I’d often push or pull underneath the selected AM/PM value to change it. You’re also not able to tap the AM/PM or minute items to select them, which is completely inconsistent because you can tap the day and hour items! WAT
To add insult to injury, the hit area in the iOS 7 UIDatePicker does not extend to the boundaries of the control. The actual hit area is highlighted in blue below. So now you can’t fat-finger the edges of the picker either.
The inset tap area causes real problems when the picker is placed inline within a UITableView. It’s the same old ‘scroll views within scroll views’ conundrum developers have been struggling with since the dawn of graphical user interfaces, but made worse because you can easily miss the tap area within the control’s bounds and end up inadvertently scrolling the whole UITableView.
Don’t get me wrong, I think inline pickers are the way to go. It puts them in context with what you’re doing and avoids the issues of placement consistency across the iPhone and iPad. I just wish the tap targets were easier to hit and extended out to the bounds of the control.
Another issue I have with the picker is that Apple have stripped back so much of the ornamentation and deferred so much to the content that pickers with only a small amount of content can look downright weird. Without fully populated rows the transformed text looks out of place and at a glance it’s hard to tell why. It’s not until you move it that it makes sense.
From a usability point of view, the new UIPicker has taken one step forward and a few steps back. But the fun really starts when you take a look under the covers.
Here’s the iOS 6 UIDateTime picker view hierarchy displayed in Reveal.
…and here’s the iOS 7 UIDateTime picker.
As you can see, they’ve gone to town with the new implementation. It’s pretty obvious why performance would suck on lower-end devices. Impressive as the 3D transforms on the cells are, it’s hard not to feel like this is overkill for the magnified tumbler effect. The UIDatePicker contains no less than 12 UITableViews with each cell having its own unique 3D transform. The hierarchy is further bloated by the fact that UITableViewCell in iOS 7 introduces a UITableViewCellScrollView that contains the UITableViewCellContentView, neither of which appear to be utilised in the UIDatePicker.
Whether the UIPicker was implemented this way on purpose or not, I don’t know, but it’s certainly not optimised. Maybe Apple really did want the latest version of iOS to push the performance boundaries, but unlike the navigation bar blur effects which were ramped down on poorer performing devices, the implementation of the UIPicker doesn’t support this kind of fallback.
So that’s the new iOS 7 UIPicker. I was left wondering whether the 3D rotation effect was really worth it, both in terms of performance and usability. I’m not even sure the effect has a place in a predominantly flat UI. I can’t see it changing any time soon but I wouldn’t be surprised to see a few fixes and optimisations from Apple in subsequent releases to address some of the issues mentioned above. Watch this space.
If you want to have a peek under the covers yourself, get on over to http://revealapp.com and give Reveal a spin.