Dynamically Coloured UIButtons (and other views) with UIGraphicsImageRenderer

This is one of those APIs introduced in iOS 10 that I always forget about – maybe something to do with highly functional but less than catchy name of `UIGraphicsImagerenderer’ . However it’s one worth keeping in the toolbox , and hence this post, as a handy tool for creating block-coloured images. It’s especially useful for generating button backgrounds that change colour as their state changes.

The Swift documentation summarises very nicely what this class does:

You can use image renderers to accomplish drawing tasks, without having to handle configuration such as color depth and image scale, or manage Core Graphics contexts. You initialize an image renderer with parameters such as image output dimensions and format. You then use one of more of the drawing functions to render images that share these properties.

You pass the renderer’s image function a closure which contains Core Graphics drawing commands. There is the potential to perform complex graphical work within the closure, which will hopefully be the topic of a long-form blog at some point, but in this case we’re going to use the .fill function to return a single colour image.

extension UIColor {
    func image(_ size: CGSize = CGSize(width: 1, height: 1)) -> UIImage {
        return UIGraphicsImageRenderer(size: size).image { rendererContext in
            self.setFill()
            rendererContext.fill(CGRect(origin: .zero, size: size))
        }
    }
}

The extension has many possible uses throughout UIKit, but one simple but incredibly useful (IMHO anyway 😀) case is to extend UIButton to allow background colour to be linked to the button state in the same way as the title and text colours can be.

extension UIButton {
   func setBackroundColor(_ color: UIColor, for state: UIControl.State) {
      self.setBackgroundImage(color.image(), for: state)
   }
}

This can then be used like

let button = UIButton()
button.setBackroundColor(.darkGray, for: .normal)
button.setBackroundColor(.red, for: .highlighted)

Leave a Reply