Creating custom UIViews can be very useful for many reasons; code reusability, view controller code reduction and improve maintainability. It enables you to build one component and use it in multiple places around your application. This will reduce the total number of lines in your project and enable you to make changes to your component in one place as opposed to many. View controllers can often get bloated with code that can be delegated to the views of the controller.
Tutorial
Here is an example of a custom UIView XIB. The view contains one button, which when pressed fires a delegate method that should be handled inside your ViewController.
Here is the code for the TestView and the TestViewDelegate. The TestView has a container view, UIButton, and delegate. Ensure that the container view and UIButton are connected up to the XIB properly. You will need to create an IBAction to handle the button press on the TestView, inside this action method you will fire the delegate method which will be sent to the parent UIViewController that conforms to TestViewDelegate.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import UIKit | |
// MARK: TestViewDelegate | |
protocol TestViewDelegate: class { | |
func testView(_ testView: TestView, didSelect button: UIButton) | |
} | |
// MARK: TestView | |
class TestView: UIView { | |
// MARK: Delegates | |
weak var delegate: TestViewDelegate? | |
// MARK: Outlets | |
@IBOutlet var contentView: UIView! | |
@IBOutlet var button: UIButton! | |
// MARK: UIView Methods | |
override func awakeFromNib() { | |
super.awakeFromNib() | |
setupButton() | |
} | |
// MARK: Action Methods | |
@IBAction func buttonPressed(sender: Any?) { | |
guard let button = sender as? UIButton else { | |
return | |
} | |
delegate?.testView(self, didSelect: button) | |
} | |
// MARK: Helper Methods | |
private func setupButton() { | |
button.backgroundColor = .red | |
button.setTitleColor(.white, for: .normal) | |
button.layer.cornerRadius = 5 | |
button.clipsToBounds = true | |
} | |
// MARK: Init Methods | |
override init(frame: CGRect) { | |
super.init(frame: frame) | |
commonInit() | |
} | |
required init?(coder aDecoder: NSCoder) { | |
super.init(coder: aDecoder) | |
commonInit() | |
} | |
private func commonInit() { | |
Bundle.main.loadNibNamed(String(describing: TestView.self), owner: self, options: nil) | |
addSubview(contentView) | |
contentView.frame = self.bounds | |
contentView.autoresizingMask = [.flexibleWidth, .flexibleHeight] | |
} | |
} |
Now that our TestView is set up correctly, we simply have to place it onto our UIViewController, create an IBOutlet to it and set its delegate properly. Once we have done those three steps, the TestView will fire back a delegate call every time its “Action” button is pressed. Enabling our TestViewController to handle that button press further, in this case by printing a simple statement.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import UIKit | |
// MARK: TestViewController | |
class TestViewController: UIViewController { | |
// MARK: Outlets | |
@IBOutlet var testView: TestView! | |
// MARK: View Methods | |
override func viewDidLoad() { | |
super.viewDidLoad() | |
testView.delegate = self | |
} | |
} | |
// MARK: TestViewDelegate Methods | |
extension TestViewController: TestViewDelegate { | |
func testView(_ testView: TestView, didSelect button: UIButton) { | |
guard let buttonText = button.titleLabel?.text else { | |
return | |
} | |
print("\(buttonText) pressed") | |
} | |
} |
Here is the full source code for the project: