I looked at the book.
On page 272, they add the IBAction by control-drag.
They do not declare an IBOutlet here, because they don't need it yet (just need the IBAction)
On page 287, they now need to reference the button, hence they have created the IBOutlet and connected it to the button in IB.
« In order to connect the button to a method programmatically, you’ll need a reference to the button in code. Use Interface Builder to create an IBOutlet for the button.»
You can do it, either by control drag from the button to the code and select IBOutlet (similar to what they explain for IBAction and to what is explained on page 282.
or declare IBOutlet in code and control- drag to the button in IB to connect it: That is what they assume Page 287:
In the book, they do not create the button programmatically. In fact, they create it in IB.
If you declare an IBOutlet, you have to connect to the object in IB.
Otherwise when you reference the button, you get the crash
Thread 1: Fatal error: Unexpectedly found nil while implicitly unwrapping an Optional value.
However, if you really wanted to create the button completely programmatically, you must not declare an IBOutlet, just a var. So change code as follows:
class ViewController: UIViewController {
@IBOutlet var toggle: UISwitch! // you may have to do as with button
@IBOutlet var slider: UISlider! // you may have to do as with button
var button: UIButton! // -- No IBOutlet
override func viewDidLoad() {
super.viewDidLoad()
button = UIButton(frame: CGRect(x: 80, y: 30, width: 100, height: 30)) // -- Create the button
button.setTitle("My button", for: UIControl.State.normal) // Select a better title
button.addTarget(self, action: #selector(buttonTapped(_:)), for: .touchUpInside)
view.addSubview(button) // -- so that button appears in view - could also write self.view to make it clearer
}
Note: when you connect the button from IB to its IBOutlet, that is somehow the code for initialising button is added automatically when the VC is loaded from storyboard.