Let The User Change UIButton Location

This post illustrates a solution for giving users the ability to control the location of a high value UIButton in an app. In fact, the same can apply to any UIView, but I’ve used this technique specifically for a UIButton.

The ability to control the location of an important UIButton or UIView facilitates operating the phone with one hand, and accommodates left and right handed usage according to a specific user. Some example use cases are a panic button, or a fire button in a game.

The video below shows you what the end result will be for this demo app.

Auto layout and constraints are perfect for this feature, so let’s get started. The full demo app and code are here in GitHub.

I’ve created a Storyboard user interface, configured a dashboard on the screen, as well as an area for the UIButton. I won’t go into too much detail on the layout, but I will mention a few important aspects. First, this is what the project and storyboard look like after being configured.

Here are a few notes to explain what is going on.

  1. I’ve added a pleasing background in the Storyboard, which I quickly created with the Sketch App. I typically use Sketch for at least a few design elements on virtually all projects I work on.
  2. I make good use of UIStackView in this project. I’ve configured a dashboard style top area which is called Stack View Status Data in the storyboard. This area is designed with aligned controls, using the same technique I’ve described in my iOS Form Design tutorial. In the dashboard area, there are horizontal and vertical sliders which will be used to move the UIButton based on user preference. Keeping the sliders in this dashboard area is convenient for when a user wants to switch hands and quickly move their button to be in the perfect location for their anatomy.
  3. I’m using a subclass of UIButton, which I called RoundButton. It is very simple, and I have a separate post and demo app here on my blog. The user will be able to change the placement of the GO button in this project. The space available for the button is everything below the darker separator line. The separator line is positioned in ViewLine2.
  4. The button has an aspect constraint of 1:1, and is sized to be 0.33 of the overall view width.
  5. The key constraints in this project are that the UIButton is centered within the large ViewButton area. By changing the constraint constants via the sliders, we’ll move the view horizontally and vertically.
  6. The Internet and Location Sharing displays are fictional and not tied to actual device status values.

With the design of the UI in place, let’s now look at the code necessary to wire up the desired functionality.

First we setup the required IBOutlets.

When the user moves the button, we want to save the location in UserDefaults, so we need a key for each of the X and Y values.

We will want to do initial setup of the sliders.

I added lots of comments into the horizontal slider setup to explain how it works. The vertical slider setup is almost the same, except I wanted the left side of the slider to represent the button being lower, and the right side the button being higher visually. Since lower visually really means a positive offset from the center, the constant needs to be set to minus whatever the offset value should be, since the lower slider value will be minus, but we want a positive value of offset.

The setup methods need to be called from viewDidLayoutSubviews. The reason it is here, and not viewWillAppear or viewDidLoad, is because the setup is dependent on bounds, which are not properly set until the auto layout has done the layout of the main view and subviews. I’ve described this in more detail in my When Should You Override viewDidLayoutSubviews post. Here is the code for calling the setup methods.

In the viewDidLoad method there is additional setup code.

Nothing earth shattering here. Both sliders are configured to call the methods to handle slider value changes, and just for completeness, I’ve configured the button in this example to activate whatever action would be tied to the button, by using long press and double tap. I could have chosen a standard Touch Up Inside action, but went with the other two, just for illustrating the case that you don’t want to mistakenly click the button and trigger something.

One notable concept in the above code for handling changed slider values is that we only write the value to UserDefaults once the slider movement has ended.

if you’ve read this far and are wondering how the button actually moves to track the slider movement, this is accomplished via the following two methods.

As the sliders are moving, the constants for the center X and Y constraints are changing, and the button location is updated accordingly.

With the values being saved to UserDefaults when the slider stops, whenever you leave the screen and come back, or terminate the app and restart it, the button will be set to the last saved location. The user can leave it there indefinitely, or change it whenever the need arises.

I’ve gone through this fairly quickly, but it is one of those projects where it is probably easier to grab the demo, run it, and analyze what the code is doing. As mentioned at the top of this post, you can download the code from GitHub here.

Leave a Comment: