iOS
UIToolBar
Swift

A auto-fitting custom iOS toolbar with labelled buttons

In iOS 11+, it is possible to provide custom UIBarButtonItems to the Toolbar. The custom CustomToolbarButtonClass used in this example has a text label centered and directly below the button icon.

The main challenge involves fitting the toolbar to its custom items. THe solution presented requires a subclass of the UIToolbar, which automatically fits the height of its items whenever there is a layout change.

This solution is backwards compatible with iOS <11 versions.

if #available(iOS 11.0, *) {
    let button =  CustomToolbarButtonClass(
        image: image,
        label: label,
        action: #selector(onButtonClicked(sender:)
    )
}
else {
    let button = UIBarButtonItem(
        image: image,
        style: .plain,
        target: self,
        action: #selector(onItemClicked(sender:)))
}

A custom toolbarClass which handles layout changes

class ToolbarClass: UIToolbar {

    var defaultHeight: CGFloat {
        get {
            return 44.0
        }
    }

    private var maxContentHeight: CGFloat? {
        get {
            return self.items?.map({ (button) -> CGFloat in
                (button as? ToolbarButtonClass)?.height ?? 0
            }).max()
        }
    }

    override func layoutSubviews() {
        super.layoutSubviews()

        if let contentHeight = self.maxContentHeight {
            var newFrame = self.frame
            newFrame.size.height = max(defaultHeight, contentHeight)
            if(self.frame != newFrame) {
                self.frame = newFrame
            }
        }

    }

    override func sizeThatFits(_ size: CGSize) -> CGSize {
        var size = super.sizeThatFits(size)
        if let contentHeight = self.maxContentHeight {
            let newHeight = max(defaultHeight, contentHeight)
            if(newHeight != size.height) {
                size.height = newHeight
            }
        }
        return size
    }
}