Delegation Pattern
Delegation
The other Decorator design pattern, Delegation, is a mechanism in which one object acts on behalf of, or in coordination with, another object. For example, when you use a
UITableView
, one of the methods you must implement is tableView(_:numberOfRowsInSection:)
.
You can’t expect the
UITableView
to know how many rows you want to have in each section, as this is application-specific. Therefore, the task of calculating the amount of rows in each section is passed on to the UITableView
delegate. This allows the UITableView
class to be independent of the data it displays.
Here’s a pseudo-explanation of what’s going on when you create a new
UITableView
:
The
UITableView
object does its job of displaying a table view. However, eventually it will need some information that it doesn’t have. Then, it turns to its delegates and sends a message asking for additional information. In Objective-C’s implementation of the delegate pattern, a class can declare optional and required methods through a protocol. You’ll cover protocols a bit later in this tutorial.
It might seem easier to just subclass an object and override the necessary methods, but consider that you can only subclass based on a single class. If you want an object to be the delegate of two or more other objects, you won’t be able to achieve this by subclassing.
Note: This is an important pattern. Apple uses this approach in most of the UIKit classes:
UITableView
, UITextView
, UITextField
, UIWebView
, UIAlert
, UIActionSheet
, UICollectionView
, UIPickerView
, UIGestureRecognizer
, UIScrollView
. The list goes on and on.How to Use the Delegate Pattern
Open up ViewController.swift and add these private properties to the class:
private var allAlbums = [Album]() private var currentAlbumData : (titles:[String], values:[String])? private var currentAlbumIndex = 0 |
Next, replace
viewDidLoad
with this code:override func viewDidLoad() { super.viewDidLoad() //1 self.navigationController?.navigationBar.translucent = false currentAlbumIndex = 0 //2 allAlbums = LibraryAPI.sharedInstance.getAlbums() // 3 // the uitableview that presents the album data dataTable.delegate = self dataTable.dataSource = self dataTable.backgroundView = nil view.addSubview(dataTable!) } |
Here’s a breakdown of the above code:
- Turn off translucency on the navigation bar.
- Get a list of all the albums via the API. Remember, the plan is to use the facade of
LibraryAPI
rather thanPersistencyManager
directly! - This is where you setup the
UITableView
. You declare that the view controller is theUITableView
delegate/data source; therefore, all the information required byUITableView
will be provided by the view controller. Note that you can actually set the delegate and datasource in a storyboard, if your table view is created there.
Now, add the following method to ViewController.swift:
func showDataForAlbum(albumIndex: Int) { // defensive code: make sure the requested index is lower than the amount of albums if (albumIndex < allAlbums.count && albumIndex > -1) { //fetch the album let album = allAlbums[albumIndex] // save the albums data to present it later in the tableview currentAlbumData = album.ae_tableRepresentation() } else { currentAlbumData = nil } // we have the data we need, let's refresh our tableview dataTable!.reloadData() } |
showDataForAlbum()
fetches the required album data from the array of albums. When you want to present the new data, you just need to call reloadData
. This causes UITableView
to ask its delegate such things as how many sections should appear in the table view, how many rows in each section, and how each cell should look.
Add the following line to the end of
viewDidLoad
self.showDataForAlbum(currentAlbumIndex) |
This loads the current album at app launch. And since
currentAlbumIndex
was previously set to 0, this shows the first album in the collection.
Now it’s time to implement the data source protocol! You can add the list of protocols implemented by the class right on the class declaration line. Or, to keep things tidy you can add them as extensions, which you’re already familiar with.
Add the following extensions to the bottom of the file. Make sure you add these lines after the closing brace of the class definition!
extension ViewController: UITableViewDataSource { } extension ViewController: UITableViewDelegate { } |
This is how you make your delegate conform to a protocol — think of it as a promise made by the delegate to fulfill the method’s contract. Here, you indicate that
ViewController
will conform to the UITableViewDataSource
and UITableViewDelegate
protocols. This way UITableView
can be absolutely certain that the required methods are implemented by its delegate.
Add the following code to the
UITableViewDataSource
extension:func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int { if let albumData = currentAlbumData { return albumData.titles.count } else { return 0 } } func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { var cell:UITableViewCell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as! UITableViewCell if let albumData = currentAlbumData { cell.textLabel!.text = albumData.titles[indexPath.row] cell.detailTextLabel!.text = albumData.values[indexPath.row] } return cell } |
tableView(_:numberOfRowsInSection:)
returns the number of rows to display in the table view, which matches the number of titles in the data structure.tableView(_:cellForRowAtIndexPath:)
creates and returns a cell with the title and its value.
Note: You can actually add the methods to the main class declaration or to the extension; the compiler doesn’t care that the data source methods are actually inside the
UITableViewDataSource
extension. For humans reading the code though, this kind of organization really helps with readability.
Build and run your project. Your app should start and present you with the following screen:
This is a very nice article. thank you for publishing this. i can understand this easily.!!..iOS Swift Online Training
ReplyDelete