Using UIEdgeInsets to layout a UIButton

Jeroen de Vrind
Feb 13, 2019 · 5 min read
Photo by James Sutton on Unsplash

You can use insets to layout the content of system or custom buttons. With insets you can add or remove space to the button’s title (titleEdgeInsets), image (imageEdgeInsets) and both the title and image together (contentEdgeInsets).

To see how these three properties work, create a button (UIButton) via a xib-file or in code. Give it only a center vertically and horizontally in superview constraint, but not a width constraint for this first example. Give the contentView a blue background color, the imageView a red background color and the titleLabel a yellow background color. On top of that you can also give the border of the button a green color.

button.backgroundColor = .blue
button.imageView?.backgroundColor = .red
button.titleLabel?.backgroundColor = .yellow
button.layer.borderColor = UIColor.green.cgColor
button.layer.borderWidth = 2

The contentEdgeInsets is behaving as you might expect. You can add space around both the imageView and titleLabel by using positive values.

let spacing: CGFloat = 8.0
button.contentEdgeInsets = UIEdgeInsets(top: 0, left: spacing, bottom: 0, right: spacing)

The rule when it comes to titleEdgeInsets or imageEdgeInsets is to add equal and opposite offsets to the left and right insets. So if you add for example 8 points to the left title inset, you need to apply -8 points to the right inset. In this way you use the insets only to offset the title or image and not to resize them in any way. If you don’t do this, the calculated layout rect may become too small and the title can be truncated and the spacing will show some weird behaviour.

button.titleEdgeInsets = UIEdgeInsets(top: 0, left: spacing, bottom: 0, right: 0)

To flip the title with the image we can use the imageEdgeInsets and titleEdgeInsets to move the imageView behind the titleLabel. First we move the imageView the same amount as the button width to the right and then we subtract the amount of the image width so that the imageView will stay in the content-view. It will look like this:

We set the opposite value of the left inset for the right inset:

let buttonWidth = button.frame.width
let imageWidth = button.imageView!.frame.width
button.imageEdgeInsets = UIEdgeInsets(top: 0, left: buttonWidth-imageWidth, bottom: 0, right: -(buttonWidth-imageWidth))

Then we can move the titleLabel with the same amount as the width of the imageView to the left:

button.titleEdgeInsets = UIEdgeInsets(top: 0, left: -imageWidth, bottom: 0, right: imageWidth)

If you want to have some spacing between the imageView and titleLabel, you need to add half of the spacing to the titleLabel and half of the spacing to the imageView like this:

let buttonWidth = button.frame.width
let imageWidth = button.imageView!.frame.width
let spacing: CGFloat = 8.0 / 2
button.imageEdgeInsets = UIEdgeInsets(top: 0, left: buttonWidth-imageWidth + spacing, bottom: 0, right: -(buttonWidth-imageWidth) - spacing)button.titleEdgeInsets = UIEdgeInsets(top: 0, left: -imageWidth - spacing, bottom: 0, right: imageWidth + spacing)

But the titleLabel and imageView now exceeds the content-view. We can compensate for that with the contentEdgeInsets:

button.contentEdgeInsets = UIEdgeInsets(top: 0, left: spacing, bottom: 0, right: spacing)

But what if we want to have the imageView with a certain margin from the trailing edge and the titleLabel centered?

We can make the button bigger by giving it a bigger spacing, for example 128 points. Then we need to move the imageView the width of the button plus the spacing to the right minus a margin that we want the image to be from the right edge (16 points in our case). We then have to compensate for that same but opposite amount of spacing in the right edgeInset to keep the imageView the same size.

When adding an image the title is pushed to the right with the amount of the image width. In the example above the imageView together with the titleLabel were centered, but now we only need to center the titleLabel. To do so we set the left and right titleEdgeInset half the amount of the imageView’s width. We do it at both sides to keep the titleLabel the same size, otherwise you get a truncated title.

button.imageEdgeInsets = UIEdgeInsets(top: 0, left: buttonWidth + spacing — imageWidth — 16, bottom: 0, right: -spacing)button.titleEdgeInsets = UIEdgeInsets(top: 0, left: -imageWidth/2, bottom: 0, right: imageWidth/2)button.contentEdgeInsets = UIEdgeInsets(top: 0, left: spacing, bottom: 0, right: spacing)

When you give the button a width constraint, you can set the margin to 0 or leave all the margins out. The width constraint will already broaden the content view.

Btw…is your app already accessible? Legislation is coming up in Europe and your app should meet the Web Content Accessibility Guidelines. You can read all about accessibility in my book: ‘How to develop accessible iOS apps’ available in the Apple Book Store.

Short Swift Stories

Articles about Swift that take less than 10 minutes to…

Medium is an open platform where 170 million readers come to find insightful and dynamic thinking. Here, expert and undiscovered voices alike dive into the heart of any topic and bring new ideas to the surface. Learn more

Follow the writers, publications, and topics that matter to you, and you’ll see them on your homepage and in your inbox. Explore

If you have a story to tell, knowledge to share, or a perspective to offer — welcome home. It’s easy and free to post your thinking on any topic. Write on Medium

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store