We have a few toolbar items which appear in the same place on multiple different screens. In particular, the Add Task button appears at the bottom right of most screens.
Using Storyboards, an easy first pass is to recreate the desired toolbar item for each view controller. Trouble is that has a major problem: when transitioning between screens, those items in the toolbar give the appearance of momentarily fade out a little and back in again.
What’s happening? Despite the toolbar items being identically set up, UIToolbar doesn’t recognise them as being the same. As for the transition between any two arbitrary items, it’s fading between them, giving the overall effect of fading out a moment.
UIToolbar relies on pointer equality for its items; you have to have the exact same item instance between screens for the transition to be handled neatly.
As a first attempt, it seems logical that the top-most view controller can hand on toolbar items of its choosing as part of a segue:
This works pretty neatly, right up until the moment you introduce State Restoration into your app.
During state restoration, the view controllers in your app are reconstructed, in the structure they were in when the user last left the app (or at least a subset of this structure; each controller has to opt in to restoration).
The trouble is that restoration completely bypasses the segue mechanism used by storyboards, and as used by our example code above. As a result, the view controller comes neatly back on screen, but minus its vital toolbar items.
It seems then, that we need some sort of shared location where any of our view controllers can grab their desired toolbar items from. An easy solution to that seems to be some sort of singleton, but that comes with two downsides:
- Global state. Bleaugh.
- I lose the ability to nicely view and configure the items in Interface Builder
What then, to do?
I was dithering over this at much length a few months ago, when Mark Brindle was kind enough to point out a simple solution:
Right there in the storyboard editor, there’s already a neat little spot exposed for toolbar items to be added to, and which is shared between multiple view controllers: The Navigation Controller!
Yep, on its own, assigning toolbar items to a navigation controller isn’t terribly useful (there’s nothing to actually present those items onscreen!). But for our purposes, it works as a convenient central location.
The items aren’t global; instead they’re nicely confined to the portion of the view hierarchy that actually needs them. Indeed, if I wanted, I could instantiate and present multiple of these navigation hierarchies at once, and the toolbar items would work nicely with that.
And of course it works regardless of how the view controller makes its way on screen, be it by a segue or state restoration. (Alright, this won’t work if you instantiate just the view controller directly, but I don’t care about that particular case)
I’ll agree this does seem slightly hacky, but it’s not too bad. And besides, I haven’t come across anything better, yet.