It depends on what you are trying to do.
Case #1: You want this change to happen when the button is highlighted, but in a normal state have a different set of properties.
let theButton = UIButton()
// set common properties and layout code
theButton.setTitle("Normal", for: .normal)
theButton.setTitle("Highlighted", for: .highlighted)
In addition, you have setTitleColor(), setAttributedTitle, setTitleShadowColor(), setImage(), and setBackgroundImage() that you can code directly.
Border color in this case would need a subclass (not an extension, you want public properties) where you will check self.layer.hitTest() after wiring up a tap gesture on self.
Case #2: You want the button state to change when clicked, and stay changed.
You are part way there. If you supply the button in IB, make sure you add an IBAction for event touchUpInside. If you are working in code, here's the Swift 3 syntax.
theButton.addTarget(self, action: #selector(changeButton), for: .touchUpInside)
func changeButton(sender: UIButton) {
sender.setTitle("New Title", for: .normal)
sender.layer.borderColor = UIColor.red.cgColor
}
My preference (but only that) is to more strongly-type the sender (I think that's the correct term) for my actions. I'm sure there are pros and cons for using a specific sender (like UIButton) over AnyObject, but in this case I think the biggest reason is you don't need to force-cast the sender to UIButton.