Sometimes you need a control that doesn’t yet exist. For example, the UISwitch
control is far from customizable. I’d like a toggle switch that looks more like an old-fashioned switch or pushbutton. Let’s learn the basics of making your own controls by making a UIButton
into a toggle button.
Open the example file. You’ll find an empty class UIToggleButton
. in the UIToggleButton.swift file. To make our toggle button I’m going to subclass UIButton
, since much of what I want is there.
class UIToggleButton:UIButton{ }
I can’t use an extension because I’m going to add more properties to the button. The most important of those will be isOn
.
var isOn:Bool = false
UIButton
subclasses UIControl
. There is a series of tracking methods in UIControl
that handles touches. For controls, you don’t use UIResponder
for touch events, but these methods instead. The control will handle any target-action pairs in the class hierarchy. I’ll change the button when I stop tracking a touch using the endTracking
method.
override func endTracking(_ touch: UITouch?, with event: UIEvent?) { }
Always call super
when you use these methods.
super.endTracking(touch, with: event)
And I’ll change the state of the isOn
button.
isOn = !isOn
Head to the ViewController
code. You’ll see I made a button configuration method configureToggleButton
. One problem with custom controls like this is you will have to add them programmatically without more work, which I’ll cover in a future tip. I added two methods to configure the button and to lay out the button, which you can find in the example file or the listing below. I’ll use the button in the action I set up, where I’ll change the title on the toggle. This way you can see the action and the touch event can run independently, but see the effect of the touch event on the toggle.
if sender.isOn { sender.setTitle("On", for: .normal) } else { sender.setTitle("Off", for: .normal) }
Build and run. You’ll have a button in the center of the screen.
You can add more features. I’ll add images for the buttons.
I may not use an image. If I don’t I’ll want the image to be nil
, and use the superclass properties, so I’ll use optionals for the image in the UIToggleButtonClass
public var onImage:UIImage! = nil public var offImage:UIImage! = nil
In my UIToggleButton
method, II’ll make a method updateDisplay
which will change the button’s background image.
func updateDisplay() { if isOn { if let onImage = onImage{ setBackgroundImage(onImage, for: .normal) } } else { if let offImage = offImage{ setBackgroundImage(offImage, for: .normal) } } }
I’ll update the display once I set the image. I’ll use a property setter to do that.
var isOn:Bool = false{ didSet{ updateDisplay() } }
I’ll do the same for the off image, so I’ll copy and paste that
var offImage:UIImage! = nil { didSet{ updateDisplay() } }
And the same for the on Image
var onImage:UIImage! = nil{ didSet{ updateDisplay() } }
I added to the assets folder a toggle off and toggle on button image. Head to the ViewController
. In the cofigureToggleButton
, add two more lines of code.
toggleButton.offImage = UIImage(named: "Toggle off") toggleButton.onImage = UIImage(named:"Toggle on")
Build and run. Tap the button. You’ll get a cool looking button that glows when on, and dims when off.
You’ll find a few more examples of switches and toggles you can use in the downloads file. Give them a try like this switch
This is only scratching the surface of this subclass. You can add background colors the same way as images. You could add haptics to make the user feel the click of the button. You might also want to play with making some properties and methods public and private. Play around and see what else you can do.
The Whole Code
UIToggleButton.swift
// // UIToggleButton.swift // Toggle buttons and switches // // // An exercise file for iOS Development Tips Weekly // by Steven Lipton (C)2018, All rights reserved // For videos go to http://bit.ly/TipsLinkedInLearning // For code go to http://bit.ly/AppPieGithub // import UIKit class UIToggleButton:UIButton{ var isOn:Bool = false{ didSet{ updateDisplay() } } var onImage:UIImage! = nil{ didSet{ updateDisplay() } } var offImage:UIImage! = nil{ didSet{ updateDisplay() } } func updateDisplay(){ if isOn { if let onImage = onImage{ setBackgroundImage(onImage, for: .normal) } } else { if let offImage = offImage{ setBackgroundImage(offImage, for: .normal) } } } override func endTracking(_ touch: UITouch?, with event: UIEvent?) { super.endTracking(touch, with: event) isOn = !isOn } }
ViewController.swift
// // ViewController.swift // Toggle buttons and switches // // // An exercise file for iOS Development Tips Weekly // by Steven Lipton (C)2018, All rights reserved // For videos go to http://bit.ly/TipsLinkedInLearning // For code go to http://bit.ly/AppPieGithub // import UIKit class ViewController: UIViewController { @IBOutlet weak var basicSwitch: UISwitch! var toggleButton: UIToggleButton! //Action when button recieves touchUpInside @IBAction func didToggleButton(_ sender: UIToggleButton) { if sender.isOn { sender.setTitle("ON", for: .normal) } else { sender.setTitle("OFF", for: .normal) } } //Configure the toggle button for this app. func configureToggleButton()-> UIToggleButton{ let toggleButton = UIToggleButton() toggleButton.offImage = UIImage(named: "Toggle off") toggleButton.onImage = UIImage(named: "Toggle on") toggleButton.setTitleColor(.black, for: .normal) toggleButton.titleLabel?.font = UIFont(name: "Gill Sans", size: 36) toggleButton.setTitle("OFF", for: .normal) toggleButton.addTarget(self, action: #selector(didToggleButton(_:)), for: .touchUpInside) return toggleButton } //Add the button to the layout centered on the view. func addToggleButton(to view:UIView) { toggleButton = configureToggleButton() view.addSubview(toggleButton) toggleButton.translatesAutoresizingMaskIntoConstraints = false var constraints = [NSLayoutConstraint]() constraints += [NSLayoutConstraint.init(item: toggleButton, attribute: .centerX, relatedBy: .equal, toItem: view, attribute: .centerXWithinMargins, multiplier: 1.0, constant: 0)] constraints += [NSLayoutConstraint.init(item: toggleButton, attribute: .centerY, relatedBy: .equal, toItem: view, attribute: .centerYWithinMargins, multiplier: 1.0, constant: 0)] constraints += [NSLayoutConstraint.init(item: toggleButton, attribute: .width, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1.0, constant: 128)] constraints += [NSLayoutConstraint.init(item: toggleButton, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1.0, constant: 96)] NSLayoutConstraint.activate(constraints) } override func viewDidLoad() { super.viewDidLoad() addToggleButton(to: view) } }
Leave a Reply