Using UIEdgeInsets to layout a UIButton

Jeroen de Vrind
Feb 13, 2019 · 5 min read
Image for post
Image for post
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
Image for post
Image for post

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)
Image for post
Image for post

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)
Image for post
Image for post

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:

Image for post
Image for post

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:

Image for post
Image for post
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)
Image for post
Image for post

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)
Image for post
Image for post

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)
Image for post
Image for post

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…

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade

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