Subclass UITableView in Swift - ios

I am trying to create a subclass of UITableView in Swift.
var tableScroll = HorizontalScrollTableView(frame: CGRectMake(15, 15, cell.contentView.frame.width - 30, cell.contentView.frame.height - 30))
cell.contentView.addSubview(tableScroll)
I add the table in the following way and then I have a HorizontalScrollTableView swift file with
import UIKit
class HorizontalScrollTableView : UITableView {
func numberOfSectionsInTableView(tableView: UITableView) -> Int
{
return 1
}
...
However the Table presents like a normal default table and the functions in HorizontalScrollTableView do not override the default ones.

You're probably looking to override numberOfSections instead of numberOfSectionsInTableView:, which is a method on the UITableViewDataSource protocol. However, I believe it's more wise to create your own subclass of UITableViewController or your class conforming to UITableViewDataSource and set it as your table view's delegate rather than of the table view itself. Subclassing UIView and its descendants is usually reserved for customizing view-specific functionality and attributes, such as adding custom drawing or custom touch handling.

I went ahead and made a simple example using a Playground. You can see table view isn't subclassed, but instead there is a view controller which serves as the table view's delegate and a stand-in data source for the table view.
The data source provides the row data for the table view and the delegate (which is also the view controller) provides the cells.
This is a skeleton for how I normally set up my table views, although I would use an XIB generally.
import UIKit
// data for table view is array of String
let rowData = [ "Chicago", "Milwaukee", "Detroit", "Lansing" ]
//
// simple subclass of UITableViewCell that has an associated ReuseIdentifier
// and a value property. Setting the value property changes what the cell displays
//
public class TableViewCell : UITableViewCell
{
public static let ReuseIdentifier = "TableViewCell"
var value:AnyObject? {
didSet {
self.textLabel!.text = value as? String
}
}
}
//
// Simple implementation of a table view data source--just contains one String per row
// You could change your rowData to be an array of Dictionary for richer content possibilities.
// You could also load your data from a JSON file... for example
//
class TableViewDataSource : NSObject, UITableViewDataSource
{
var rowData:[String]?
init( rowData:[String] )
{
self.rowData = rowData
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int
{
return rowData?.count ?? 0
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
{
assert( indexPath.section == 0 ) // we only have 1 section, so if someone section ≠ 0, it's a bug
var cell:TableViewCell! = tableView.dequeueReusableCellWithIdentifier( "Cell" ) as? TableViewCell
if cell == nil
{
cell = TableViewCell( style: .Default, reuseIdentifier: "Cell" )
}
cell.value = self.rowData![ indexPath.row ]
return cell
}
}
// This is the view controller for our table view.
// The view controller's view happens to be a table view, but your table view
// could actually be a subview of your view controller's view
class TableViewController : UIViewController, UITableViewDelegate
{
// data source for our table view, lazily created
lazy var tableViewDataSource:TableViewDataSource = TableViewDataSource( rowData: rowData )
override func loadView()
{
// our view is a UITableView
let tableView = UITableView()
tableView.delegate = self
tableView.dataSource = self.tableViewDataSource // using a self-contained data source object for this example
self.view = tableView
}
}
let window:UIWindow! = UIWindow()
window.rootViewController = TableViewController()
window.makeKeyAndVisible() // click the "preview eyeball" to see the window

Related

Linking multiple custom UITableViewCell's to single xib

Question
I'm creating a re-usable custom UITableViewCell and then subclassing it for re-use. How do I set these various subclasses to link to the same xib file?
Background
Let's call my custom UITableViewCell PickerTableViewCell. This cell includes a UIPickerView, as well as all the implementations as to how the picker view looks and behaves. When I want to use this cell, the only thing I need to give it is the data for the picker view. So I subclass PickerTableViewCell, then simply create the data source I need and assign it to the picker view. So far this has all worked well.
Here are the relevant parts of PickerTableViewCell:
class PickerTableViewCell: UITableViewCell {
var picker: UIPickerView! = UIPickerView()
var pickerDataSource: PickerViewDataSource!
override func awakeFromNib() {
super.awakeFromNib()
self.picker = UIPickerView(frame: CGRect(x: 0, y: 40, width: 0, height: 0))
self.picker.delegate = self
self.assignPickerDataSource()
}
// Must be overriden by child classes
func assignPickerDataSource() {
fatalError("Must Override")
}
Here is an example of a subclass:
class LocationPickerTableViewCell: PickerTableViewCell {
override func assignPickerDataSource() {
self.pickerDataSource = LocationPickerDataSource()
self.picker.dataSource = self.pickerDataSource
}
}
Problem
Since I am using these cells all over the place, with different data sources, I created a xib file which defines how the cell looks called PickerTableViewCell.xib, and assign it to the class PickerTableViewCell. In the view controllers I want to use it for, I register the cell with the table view inside viewDidLoad(). Then, inside func tableView(_:, cellForRowAt) I dequeue the subclass I want like this:
let cell = tableView.dequeueReusableCell(withIdentifier: "reuseIdentifier") as! LocationPickerTableViewCell
return cell
This is where the problem happens. The cell that is created is a PickerTableViewCell, not its subclass LocationPickerTableViewCell. This runs into the fatal error I placed in the parent class which is overriden by the child class.
The only way I have found to solve this is to create a separate xib file for each subclass I want to create, and assign it to the relevant subclass. While this solution does work, it feels wrong to have all of these xib files which are practically identical (except for which class they are assigned to) inside my project.
Is there a way I can overcome this problem, and have all of these cells link to the same single xib file?
Thanks! :)
Add view loaded by xib to UITableViewCell classes in which you want to use it.
Create your xib as per your require design, in your example PickerTableViewCell.xib
Create UITableViewCell sub-classes in which you want to use that view. I am using FirstTableViewCell & SecondTableViewCell for this.
in constructor of table cell load the xib and add it to table cell.
let nib = Bundle.main.loadNibNamed("PickerTableViewCell", owner: nil, options: nil)
if let view = nib?.first as? UIView{
self.addSubview(view)
}
if xib have any #IBOutlet then get them by viewWithTag function and assign to class variables
if let label = self.viewWithTag(1) as? UILabel{
self.label = label
}
override reuseIdentifier var of each tableviewCell subclass with different name
override var reuseIdentifier: String?{
return "FirstTableViewCell"
}
Now You can use these classes where you want, for using this follow below steps:
register this tableviewCell subclass with xib with tableview:
tableView.register(FirstTableViewCell.self, forCellReuseIdentifier:"PickerTableViewCell")
now in cellForRowAt indexPath method use it.
var cell = tableView.dequeueReusableCell(withIdentifier: "FirstTableViewCell", for: indexPath) as? FirstTableViewCell
if cell == nil {
cell = FirstTableViewCell()
}
cell?.label?.text = "FirstTableViewCell"
Don't use subclassing to assign different data sources.
Approach 1: Assign pickerDataSource in tableView(_:cellForRowAt:)
In the table view controller, you need to assign pickerDataSource
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "reuseIdentifier") as! PickerTableViewCell
cell.pickerDataSource = LocationPickerDataSource()
return cell
}
Handle additional work needed after the assignment of pickerDataSource with a didSet.
class PickerTableViewCell: UITableViewCell {
var picker: UIPickerView! = UIPickerView()
var pickerDataSource: PickerViewDataSource! {
didSet {
self.picker.dataSource = self.pickerDataSource
}
}
…
}
Approach 2: Extend PickerTableViewCell in all the needed ways.
Here instead of subclassing add the needed logic to a uniquely named setup method each defined in their own extension.
extension PickerTableViewCell {
func setupLocationPickerDataSource() {
self.pickerDataSource = LocationPickerDataSource()
self.picker.dataSource = self.pickerDataSource
}
}
then in tableView(_:cellForRowAt:)
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "reuseIdentifier") as! PickerTableViewCell
cell.setupLocationPickerDataSource()
return cell
}

Dynamic Tableview inside a Static tableview Cell

I wanna populate a dynamic tableview inside a static tableview cell, by the same class for both of these.
As you can see in the picture under the cell 'GRE Test Information'.
I'm using the code inside the the class named as MenuController, which is a tableview controller.
class MenuController: UITableViewController,MFMailComposeViewControllerDelegate {
#IBOutlet weak var tablle: UITableView!
var items = [String]()
override func viewDidLoad() {
super.viewDidLoad()
// Uncomment the following line to preserve selection between presentations
// self.clearsSelectionOnViewWillAppear = false
// Uncomment the following line to display an Edit button in the navigation bar for this view controller.
// self.navigationItem.rightBarButtonItem = self.editButtonItem()
items = ["A "," BB "]
tablle.delegate = self
tablle.dataSource = self
self.tablle.registerClass(MainTableViewCell.self, forCellReuseIdentifier: "cellNew")
}
// Table Data Source
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 2
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath)
-> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("cellNew", forIndexPath: indexPath) as! MainTableViewCell
print("Aasim Khaan")
cell.customCell01.text = items[indexPath.row]
return cell
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
But it's not populating that at runtime, and says
Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'unable to dequeue a cell with identifier cellNew - must register a nib or a class for the identifier or connect a prototype cell in a storyboard'
However I'm using the same identifier named as cellNew both in the code and storyboard.
Well after astonishing efforts regarding this one, I've found the solution.
Concerning the following:
Swift: TableView within Static UITableViewCell
Where the problem solver says : As far as I can determine by experimenting with this, you can't use the same UITableViewController as the data source and delegate of both table views. With a static table view, you're not supposed to implement the data source methods at all. The strange thing is, even if I disconnect the data source and delegate connections between my static table view and the table view controller, that table view still calls numberOfRowsInSection in my table view controller class. If I explicitly set the data source to nil in code, that stops it from calling the data source methods, but the embedded dynamic table view also fails to call them, so this structure doesn't work.
However, you can get around this by using a different object to be the data source and delegate of your embedded dynamic table view. Make an IBOutlet for your embedded table view, and set its data source and delegate to this new object (The class is DataSource in this example, and it's a subclass of NSObject).
I've modified my code in this way now :
import Foundation
import UIKit
import MessageUI
class DataSource: NSObject, UITableViewDataSource, UITableViewDelegate {
var items : [String] = ["GRE Test Structure ","GRE Score "]
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1;
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 2;
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("cellNew", forIndexPath: indexPath) as! MainTableViewCell
cell.customCell01.text = items[indexPath.row]
return cell
}
}
class MenuController: UITableViewController,MFMailComposeViewControllerDelegate {
#IBOutlet var tablle0: UITableView!
#IBOutlet weak var tablle: UITableView!
var dataSource = DataSource()
override func viewDidLoad() {
super.viewDidLoad()
// Uncomment the following line to preserve selection between presentations
// self.clearsSelectionOnViewWillAppear = false
// Uncomment the following line to display an Edit button in the navigation bar for this view controller.
// self.navigationItem.rightBarButtonItem =
self.editButtonItem()
tablle.delegate = dataSource
tablle.dataSource = dataSource
}
}
Now it works exactly fine.
in viewDidLoad
// First Register the UITableViewcell class from nib
let cellNib = UINib(nibName: "MainTableViewCell", bundle: bundle)
self.tableView.registerNib(cellNib, forCellReuseIdentifier:"cellNew")
Then Check with below screeshots
STEP 1: Select MainTableViewCell from Identity Inspector-Custom Class-Click Class Drop Down arrow.It shows you list.From that you can click the MainTableViewCell
STEP 2:Once you click that it shows the name with selected table view cell.
While the existing answers explain how you can do this, they don't address whether you should do this. From the example you provided, it seems that all you need is a single UITableView with multiple dynamic cell types. Each cell type can specify its contentInsets to indent the content as needed.
Terminating app due to uncaught exception
'NSInternalInconsistencyException', reason: 'unable to dequeue a cell
with identifier cellNew - must register a nib or a class for the
identifier or connect a prototype cell in a storyboard'
However I'm using the same identifier named as cellNew both in the
code and storyboard.
You're getting this error because you are dequeing/retrieving the prototype cell from the wrong table!
The line in your cellForRowAtIndexPath should be:
let cell = tablle.dequeueReusableCellWithIdentifier("cellNew", forIndexPath: indexPath) as! MainTableViewCell
Having said that, even once that is working, asking a tableViewController to act as data source and delegate for both a static and a dynamic table causes problems later.

Init custom UITableViewCell from nib without dequeueReusableCellWithIdentifier

SWIFT
I need to make an array of cells. I have few custom cell classes (inheritated from UITableViewCell) with nib files.
How to init cell without registering nib in tableview and doing dequeueReusableCellWithIdentifier?
I did it like this, but don't think, that it will work:
var labelCell = CustomCellClass.initialize()
I'm inferring from the discussion in comments elsewhere that the reason you want to not allow cells to be dequeued and reused is that you're having trouble keeping track of user input captured in the cells.
The bottom line is that you really should allow the cells to be dequeued and reused and just handle that appropriately. If you're having problems with cells being reused, this can be resolved by separating the “model” (i.e. your data) from the “view” (i.e., the UIKit controls). This is the spirit of the model-view-controller pattern, but is true in any of those patterns that have separation of concerns (e.g., MVVP, MVP, etc.).
The key is that as values change in the cell, your cell should immediately tell the view controller so that the view controller can update the model immediately. Then, when the view controller needs to later do something with the value associated with a particular row, it doesn't retrieve it from the cell, but rather from its own model.
So, I might define a protocol for the cell to inform the table view that its text field changed:
protocol CustomCellDelegate: class {
func cell(_ cell: CustomCell, didUpdateTextField textField: UITextField)
}
And I'd then define a cell class that called that delegate:
class CustomCell: UITableViewCell {
weak var delegate: CustomCellDelegate?
#IBOutlet weak var customTextField: UITextField! // hook up outlet to this property in IB
#IBAction func didChangeValue(_ sender: UITextField) { // hook up "editing changed" action for the text field to this method in IB
delegate?.cell(self, didUpdateTextField: sender)
}
}
Now, the view controller will:
register the reuse identifier with the NIB in question;
in cellForRowAt, populate the text field and specify itself as the delegate for that cell; and
handle the didUpdateTextField method to update model if user changes anything.
Thus, something like:
class ViewController: UITableViewController {
var values = ["One", "Two", "Three"] // some initial values
private let cellIdentifier = "CustomCell"
override func viewDidLoad() {
super.viewDidLoad()
// if you’re using NIBs, you register them.
// obviously if using prototype cells in your storyboard, this isn’t necessary.
tableView.register(UINib(nibName: "CustomCell", bundle: nil), forCellReuseIdentifier: cellIdentifier) // or use cell prototype with storyboard identifer specified
}
}
// MARK: - UITableViewDataSource
extension ViewController {
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return values.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier, for: indexPath) as! CustomCell
// populate cell and specify delegate
cell.delegate = self
cell.customTextField.text = values[indexPath.row]
return cell
}
}
// MARK: - CustomCellDelegate
extension ViewController: CustomCellDelegate {
func cell(_ cell: CustomCell, didUpdateTextField textField: UITextField) {
// when the cell tells us that its text field's value changed, update our own model
if let indexPath = tableView.indexPath(for: cell), let string = textField.text {
values[indexPath.row] = string
}
}
}
Many people might be inclined to simplify this further, by hooking the IBAction for the text field directly to a view controller method. That works, and eliminates the need for this protocol, but the problem is that you need to figure out with which row this particular UIKit control is associated. The common trick is to navigate up the view hierarchy to identify the appropriate cell (e.g. often the text field will be in a content view within the cell, so you grab textField.superview.superview as! UITableViewCell), but that feels a little fragile to me.
But regardless of this little detail, hopefully this illustrates the broader pattern. Rather than trying to have cells keep track of user input, you should have the cell (the “view”) update the controller of any data changes immediately, and the view controller then updates the model immediately, and you no longer need to worry about the cell reuse optimizations that iOS employs.
For Swift 2 renditions, see previous revision of this answer.
The very idea of a static table is that it is fully defined in IB. One solution is to copy-paste those cells from their isolated NIBs to the one containing the table.
A better solution is to make the table dynamic, and have the dynamic code return a static number of sections and rows. Write the rest of the logic as if it's dynamic, (e.g. register all of the nibs, and initialize an array of cell identifiers how you want them organized in the table, use that array to dequeue).
Another way to solve it:
1) UITextField Delegate in View Controller. Store values in array
var textFieldValues = ["", "", "", "", "", "", "", "", "", ""] // with count of text fields in table. Init in top of ViewController.
//MARK:TextField Delegate
func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
if string == " "
{
return false
}
let indexPathForCellWhereTextFieldIs = self.tableView.indexPathForCell(textField.superview?.superview as! UITableViewCell)
textFieldValues[(indexPathForCellWhereTextFieldIs?.section)!] = (textField.text! as NSString).stringByReplacingCharactersInRange(range, withString: string) as NSString as String
return true
}
2) At indexPathForRow
cell.registrationTextField.text = textFieldValues[indexPath.section]
cell.registrationTextField.delegate = self
3) Get data from array
func tapOnRegisterButton(sender:UIButton)
{
debugPrint(textFieldValues)
}

Swift dynamic tableview in UIViewController

I have been trying to add a tableview in my ViewController unfortunately no data is shown in my table view. numberOfSectionsInTableView , numberOfRowsInSection , cellForRowAtIndexPath are not even getting called.I tried tableview inside ViewController as there is another controls like label , text field's and TableView underneath .Because of these label's and text fields I am unable to use a TableViewController. what I have done ...
Created a UIViewController and added controls like label , text fields etc.
From Object Library I dragged a TableView into my UIViewController.
To manage my data from server I created a Model
class Model {
var name : String
var email : String
init?(name : String , email : String)
{
self.name = name
self.email = email
} }
as I have dynamic data, for the cell I created a swift file under the Subclass UITableViewCell named userinfoCell and connected my labels(two label's inside TableViewCell one for username and another for email) to this class(userinfoCell).
Finally in my UIViewController I have added the following code to populate my Table View.
Inside my class definition I have initialised a global variable like this
var model = [Model]()
I have created a function that adds data to my model
func loadMembers()
{
let member1 = Model(name: "Caprese Salad", email: "caprreswe#gmail.com")!
let member2 = Model(name: "Capresed", email: "pepperoni#gmail.com")!
model += [member1, member2]
}
on my ViewDidLoad I calls this function
override func viewDidLoad() {
super.viewDidLoad()
loadMembers()
}
override func numberOfSectionsInTableView(tableView: UITableView) -> Int
{
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return model.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Members", forIndexPath: indexPath) as! userinfoCell
let member = model[indexPath.row]
cell.MemberName.text = member.name
cell.MemberEmail.text = member.email
return cell
}
But I am getting an Empty table view. What went wrong in my approach
how can I implement a dynamic UITableView inside UIViewController ???
Modify your loadMembers method like this,
func loadMembers()
{
let member1 = Model(name: "Caprese Salad", email: "caprreswe#gmail.com")!
let member2 = Model(name: "Capresed", email: "pepperoni#gmail.com")!
model += [member1, member2]
// reload the table view after modifying the data source.
self.tableView.reloadData()
}
Ctrl drag from table view to View controller and set datasource and delegate
then call self.tableView.reloadData() in loadMember()
Hold control and drag from tableview to view controller and set data source and delegate like this...

What is the best design solution for this situation in iOS?

I have UITableView with two static cells. Each cell has custom class and independently validate account name, when I fill text field in the cell. (This part of code I got as is and I am not allowed to rewrite it). The cell delegates about changes if validation is correct to delegate (SocialFeedSelectCellDelegate). Originally, this tableView appeared in SignUpViewController: UITableViewController, UITableViewDataSource, UITableViewDelegate, SocialFeedSelectCellDelegate only.
Problem : The same UITableView should appear in two different places (SignUpViewController and SettingsViewController). Also SignUpViewController and SettingsViewController should know about success or fail of account validation.
What I tried : I created SocialFeedTableViewController: UITableViewController, SocialFeedSelectCellDelegate for the tableView with two cells. Set view in SocialFeedTableViewController as container view for SignUpViewController and SettingsViewController. I used second delegation (from SocialFeedTVC to SignUp and Settings) to notify SignUp and Settings about validation changes. I think it is bad idea, because of double delegation. Teammate said me that it is hard to understand.
Question: What is the best and simple design solution for the problem?
Why is the double delegation a problem? As far as I see it you have 2 table views, 1 for each controller. Then each controller sets the delegate to each of the table view as self. Even if not it is quite common to change the delegate of the object in runtime. It is also normal to have 2 delegate properties with the same protocol simply to be able to forward the message to 2 objects or more.
There are many alternatives as well. You may use the default notification center and be able to forward the messages this way. The only bad thing about it is you need to explicitly resign the notification listener from the notification center.
Another more interesting procedure in your case is creating a model (a class) that holds the data from the table view and also implements the protocol from the cells. The model should then be forwarded to the new view controller as a property. If the view controller still needs to refresh beyond the table view then the model should include another protocol for the view controller itself.
Take something like this for example:
protocol ModelProtocol: NSObjectProtocol {
func cellDidUpdateText(cell: DelegateSystem.Model.MyCell, text: String?)
}
class DelegateSystem {
class Model: NSObject, UITableViewDelegate, UITableViewDataSource, ModelProtocol {
// My custom cell class
class MyCell: UITableViewCell {
weak var modelDelegate: ModelProtocol?
var indexPath: NSIndexPath?
func onTextChanged(field: UITextField) { // just an example
modelDelegate?.cellDidUpdateText(self, text: field.text) // call the cell delegate
}
}
// some model values
var firstTextInput: String?
var secondTextInput: String?
// a delegate method from a custom protocol
func cellDidUpdateText(cell: DelegateSystem.Model.MyCell, text: String?) {
// update the appropriate text
if cell.indexPath?.row == 0 {
self.firstTextInput = text
} else {
self.secondTextInput = text
}
}
// table view data source
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 2
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = MyCell() // create custom cell
cell.indexPath = indexPath // We want to keep track of the cell index path
// assign from appropriate text
if cell.indexPath?.row == 0 {
cell.textLabel?.text = self.firstTextInput
} else {
cell.textLabel?.text = self.secondTextInput
}
cell.modelDelegate = self // set the delegate
return cell
}
}
// The first view controller class
class FirstViewController: UIViewController {
var tableView: UITableView? // most likely from storyboard
let model = Model() // generate the new model
override func viewDidLoad() {
super.viewDidLoad()
refresh() // refresh when first loaded
}
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
refresh() // Refresh each time the view appears. This will include when second view controller is popped
}
func refresh() {
if let tableView = self.tableView {
tableView.delegate = model // use the model as a delegate
tableView.dataSource = model // use the model as a data source
tableView.reloadData() // refresh the view
}
}
// probably from some button or keyboard done pressed
func presentSecondController() {
let controller = SecondViewController() // create the controller
controller.model = model // assign the same model
self.navigationController?.pushViewController(controller, animated: true) // push it
}
}
// The second view controller class
class SecondViewController: UIViewController {
var tableView: UITableView? // most likely from storyboard
var model: Model? // the model assigned from the previous view controller
override func viewDidLoad() {
super.viewDidLoad()
refresh() // refresh when first loaded
}
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
refresh() // Refresh each time the view appears. This will include when third view controller is popped
}
func refresh() {
if let tableView = self.tableView {
tableView.delegate = model // use the model as a delegate
tableView.dataSource = model // use the model as a data source
tableView.reloadData() // refresh the view
}
}
// from back button for instance
func goBack() {
self.navigationController?.popViewControllerAnimated(true)
}
}
}
Here the 2 view controllers will communicate with the same object which also implements the table view protocols. I do not suggest you to put all of this into a single file but as you can see both of the view controllers are extremely clean and the model takes over all the heavy work. The model may have another delegate which is then used by the view controllers themselves to forward additional info. The controllers should then "steal" the delegate slot from the model when view did appear.
I hope this helps you understand the delegates are not so one-dimensional and a lot can be done with them.

Resources