The Curious Case of the Missing Vertical Slider

Apple’s operating systems are severely out of sync and global refactoring is badly needed. So what happened? Well, for starters the success of iOS came as a true surprise. Originally a subset made from what is now known as macOS, iOS was lacking in certain regards. Mac developers were constantly running into the need to rewrite components for iOS that already existed on the parent side. Over the first couple of years Apple filled in some functionality as the iPhone’s and later the iPad’s processors became more powerful. But at the same time there was also a bifurcation of teams. The iOS team worked largely independently from its Mac counterpart and grew at a dramatically faster rate. Pretty soon the boundaries of the parent system were left behind and new functionality was added in iOS that was now lacking  in macOS. One area in which iOS, however, is still far behind its parent system is that of controls and one of the most blatant oversights in this regard is the lack of a vertical UISlider. On macOS sliders can be toggled to horizontal or vertical appearance, but no such luck on iOS. There is probably a “to do” list sitting in someone’s drawer at Apple containing an entry in this regard, but after ten years one would have to be fairly optimistic to assume that it will ever see the light of day. So we are going to present three ways of addressing the issue and also delve into the world of IBInspectable and IBDesignable.

 

1 ViewController Rotation

The most common way to get to a vertical slider is to rotate the control in its view controller. The nice thing about this approach is that it only takes one line of code which can be executed in the didSet clause of an outlet.

The problem here is that the rotation is not reflected in any xibs or storyboards which in turn makes it fairly useless for non-trivial layouts.

 

2 UISlider Subclass

Subclassing an UISlider solves the layout conundrum to a degree. Combining the subclass with a @IBDesignable keyword makes it rotate correctly and show up OK-ish in a storyboard. But – the container rectangle still sports its original bounds. And that is not fantastic for auto-layout. But we get away with just a couple of lines of code:

 

 

3 Custom UIControl Subclass

This approach is a bit more involved, but then the rewards are much bigger. With two pages of code we get a custom control that renders nicely in any storyboard and can be extended to include custom graphics and more. In the following couple of paragraphs we are going to add the pertinent sections bit by bit. For a complete listing however, please grab a copy of the complete project from GitHub.

Since we are going to use @IBDesingable as well as @IBInspectable keywords in the following, please make sure that “Automatically Refresh Views” in Xcode is checked. Also use the “Refresh All Views” whenever things seems to get out of sync. If you haven’t heard about @IBDesingable and @IBInspectable – these are keywords we can use in front of scalar variables like CGFloats in our code to make them editable in Xcode’s storyboard editor.

3.1 Variables

To start things out we create a new UIControl subclass and call it VerticalSlider. At the very top of the class before the class declaration we have to add the @IBDesignable keyword to let Xcode know that we want to edit these in the storyboard editor:

Next we add a whole bunch of inspectables:

The size of the thumb in a UISlider is not adjustable. We are following that with our own declaration:

We also need to keep track of the thumbRect as it travels up as well as a global variable to indicate whether the thumb is moving:

3.2 Inits

There are a bunch of inits that are needed to get our class off the ground. These are fairly standard fare and don’t really merit much comment. With one exception that is. A call to prepareForInterfaceBuilder. This method lets our draw function work in Xcode’s storyboard editor. The draw function is still stubbed out as we will get to that in a bit. So here goes:

3.3 UIControl Methods

At its most basic a UIControl subclass should implement 3 overrides:

func beginTracking(_ touch: UITouch, with event: UIEvent?) -> Bool
func continueTracking(_ touch: UITouch, with event: UIEvent?) -> Bool
func endTracking(_ touch: UITouch?, with event: UIEvent?)

We don’t have all that much to add in these overrides. In beginTracking and endTracking we set our isMoving variable accordingly and in continueTracking we’ll grab the location of the touch and pass it on to a utility to calculate our y-value:

3.4 Utilities

We need the valueFromY method we called in the previous paragraph as well as a reverse method to position the thumb correctly at the current value. We also add a one-liner method to calculate our current thumb rectangle:

As a final bit of house-keeping we are also updating our thumbRect and add some clamping for the control’s value itself. Here is the update declaration of our slider’s value:

3.5 Draw

We could import images residing in the project’s assets folder for our thumb and track images. Or – we could draw them ourselves. We are opting for the latter in this project to be able to further investigate the interaction between code and storyboard editor. So here finally is the draw method for our UIControl subclass. The code is very straightforward. We draw two rectangles for our min and max tracks by filling a UIBezierPath with different colors and then add the thumb:

 

3.6 Storyboard

Now that we are done with coding our VerticalSlider control we can add it in the main storyboard. One way of doing this is to add a regular slider. Then in the Identity Inspector set the custom class property to VerticalSlider and hook the slider to an action like any other UISlider.

It lays out pretty nicely and we have the entire array of our inspectables to play with.

 

4 Final Thoughts

So there you have it – three perfectly valid methods to overcome the embarrassing lack of a vertical slider in the iOS control arsenal. Any of these will do depending on your circumstance. Our VerticalSlider is by no means a perfect class as is, but having implemented it in code allows for easy customization. Since we have skipped over some minor details and not listed the complete ViewController class for brevity, go and get your own copy of the source code. The full project is available on GitHub. Happy coding everyone :]