Posted at

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
}
}