iOS Custom Button with Label
Implementing a custom button featuring a label, using Swift5 and UIKit 🕹
Creating our button class
We start by creating a new file, ButtonWithLabel
. To keep our button customizable, we initialize our class with a type and an action.
import UIKit
class ButtonWithLabel: UIView {
typealias Action = (() -> Void)?
private var action: (() -> Void)?
enum ButtonWithLabelType {
case info
case alert
}
private var type: ButtonWithLabelType
init(type: ButtonWithLabelType) {
self.type = type
super.init(frame: .null)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
@discardableResult
func with(action: Action) -> Self {
self.action = action
return self
}
@objc private func click() {
action?()
}
}
Creating the label and the icon
Because we want the entire class to be pressable, we create both our label and our button as UIButton
elements, and we add the previously created click
function as the buttons click-targets. I've also added some simple styling to the label button, giving it some padding and a background color. We will style the button in the next step.
private lazy var label: UIButton = {
let button = UIButton()
button.contentEdgeInsets = UIEdgeInsets(top: 6, left: 6, bottom: 6, right: 6)
button.backgroundColor = .white.withAlphaComponent(0.8)
button.addTarget(self, action: #selector(click), for: .touchUpInside)
return button
}()
private lazy var icon: UIButton = {
let button = UIButton()
button.addTarget(self, action: #selector(click), for: .touchUpInside)
return button
}()
Setting the label text and styling the icon
We want to create our label text and choose our icon based on the provided ButtonWithLabelType
. To do this, we create a helper function, updateStyling()
. In this example, I've used icons from SF Symbols.
private func updateStyling() {
switch type {
case .info:
label.setTitle("Info", for: .normal)
icon.setImage(UIImage(systemName: "questionmark.circle"), for: .normal)
case .alert:
label.setTitle("Alert", for: .normal)
icon.setImage(UIImage(systemName: "exclamationmark.circle"), for: .normal)
}
}
Positioning
We update our init
function to call the newly created updateStyling
. We also place our label and our icon in a UIStackView
.
init(type: ButtonWithLabelType) {
self.type = type
super.init(frame: .null)
updateStyling()
let stack = UIStackView(arrangedSubviews: [label, icon])
stack.alignment = .center
addSubview(stack)
stack.fillInSuperview()
}
Usage
We can now start using our new button by setting a type and giving it an action. In this example, I use it as a rightBarButtonItem.
let infoButton = ButtonWithLabel(type: .info).with(action: showInfoView)
navigationItem.rightBarButtonItem = UIBarButtonItem(customView: infoButton)
And that's it! Here is the completed class:
import UIKit
class ButtonWithLabel: UIView {
typealias Action = (() -> Void)?
private var action: (() -> Void)?
enum ButtonWithLabelType {
case info
case alert
}
private var type: ButtonWithLabelType
private lazy var label: UIButton = {
let button = UIButton()
button.contentEdgeInsets = UIEdgeInsets(top: 6, left: 6, bottom: 6, right: 6)
button.backgroundColor = .white.withAlphaComponent(0.8)
button.addTarget(self, action: #selector(click), for: .touchUpInside)
return button
}()
private lazy var icon: UIButton = {
let button = UIButton()
button.addTarget(self, action: #selector(click), for: .touchUpInside)
return button
}()
init(type: ButtonWithLabelType) {
self.type = type
super.init(frame: .null)
updateStyling()
let stack = UIStackView(arrangedSubviews: [label, icon])
stack.alignment = .center
addSubview(stack)
stack.fillInSuperview()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
private func updateStyling() {
switch type {
case .info:
label.setTitle("Info", for: .normal)
icon.setImage(UIImage(systemName: "questionmark.circle"), for: .normal)
case .alert:
label.setTitle("Alert", for: .normal)
icon.setImage(UIImage(systemName: "exclamationmark.circle"), for: .normal)
}
}
@discardableResult
func with(action: Action) -> Self {
self.action = action
return self
}
@objc private func click() {
action?()
}
}