3

If I would like to have the same basic UITableView appearing in two different scenes, is it a good idea to use one datasource and delegate location for both tables?

I wanted to try this, but when I select the table view in IB and try to drag the line to a custom class of UITableView file, or even to another custom view controller, it will not connect. It only seems possible to make the current View Controller into the table's datasource and delegate(?).

I'm wondering if this is at least similar to this question, but even if it is, how is this done in swift (and perhaps there is a new way to do this).

Community
  • 1
  • 1
DrWhat
  • 2,360
  • 5
  • 19
  • 33

4 Answers4

7

Swift 4.1. You can create separate class and inherit it from UITableViewDataSource and UITableViewDelegate class. Here, I am implementing UITableViewDataSource() methods in DataSource class. You also need to confirm NSObject so that we don’t have to fiddle with the @objc and @class keywords because UITableViewDataSource is an Objective-C protocol.

import Foundation
import UIKit

class DataSource: NSObject, UITableViewDataSource {
  var formData: [FormData]? = nil

  func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    return self.formData?.count ?? 0
  }
  
  func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: "cell")
    let label = cell?.contentView.viewWithTag(100) as? UILabel
    let type = self.formData![indexPath.row]
    label?.text = type.placeHolder
    return cell!
  }
}

Now, We will set DataSource to UITableView. If we crate separate class then we have to pass data to DataSource class.

 class ViewController: UIViewController {
    
      @IBOutlet weak var tblView: UITableView!
      var formData: [FormData]? = nil
      var dataSource = DataSource()
     
       override func viewDidLoad() {
          super.viewDidLoad()
          formData = FormData.array
          dataSource.formData = formData // Pass data to DataSource class
          tblView.dataSource = dataSource // Setting DataSource
      }
    }

In similar way you can implement UITableViewDelegate in separate class. The other way to separate DataSource and Delegate is by creating extension of your viewController. Even you can crate separate class where you can only define extensions for your view controller. In you define extension then you don't need to pass data.

class ViewController: UIViewController {

  @IBOutlet weak var tblView: UITableView!
  var formData: [FormData]? = nil

  override func viewDidLoad() {
    super.viewDidLoad()
    formData = FormData.array
    tblView.dataSource = self
  }
}

extension ViewController:  UITableViewDataSource {
  func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    return self.formData?.count ?? 0
  }
  
  func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: "cell")
    let label = cell?.contentView.viewWithTag(100) as? UILabel
    let type = self.formData![indexPath.row]
    label?.text = type.placeHolder
    label?.backgroundColor = UIColor.gray
    return cell!
  }
}
Gurjinder Singh
  • 9,221
  • 1
  • 66
  • 58
5

Here is a code example showing different Datasource and delegates for UITableView.

Code in Swift

import UIKit

// MARK: Cell

class ItemCell: UITableViewCell{
    var label: UILabel!
    override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
        super.init(style: style, reuseIdentifier: reuseIdentifier)
        label = UILabel(frame: CGRect(x: 0, y: 0, width: 200, height: 20))
        label.textColor = .black
        label.backgroundColor = .yellow
        contentView.addSubview(label)
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}

// MARK: Main View Controller

class BlueViewController: UIViewController{

    var tableView: UITableView!
    var myDataSourse: MyTVDataSource!
    var myDelegate: MyTVDelegate!


    override func viewDidLoad() {
        super.viewDidLoad()
        self.view.backgroundColor = .blue
        tableView = UITableView()
        myDataSourse = MyTVDataSource(tableView: tableView)
        myDelegate = MyTVDelegate()
        myDelegate.presentingController = self
        tableView.dataSource = myDataSourse
        tableView.delegate = myDelegate
        tableView.register(ItemCell.self, forCellReuseIdentifier: "Cell")

        self.view.addSubview(tableView)



        self.tableView.translatesAutoresizingMaskIntoConstraints = false

        NSLayoutConstraint.activate([
            tableView.topAnchor.constraint(equalTo: view.topAnchor, constant: 0),
            tableView.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: 0),
            tableView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 0),
            tableView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: 0)
            ])
    }
}
extension BlueViewController: BluePresenting{
    func currentSelected(_ indexPath: IndexPath) {
        print(indexPath)
    }


}

// MARK: TableViewDelegate


protocol BluePresenting: class {
    func currentSelected(_ indexPath: IndexPath)
}

class MyTVDelegate: NSObject,UITableViewDelegate{

   var presentingController: BluePresenting?

    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
            presentingController?.currentSelected(indexPath)
    }
}

// MARK: TableView DataSource


class MyTVDataSource: NSObject, UITableViewDataSource{
    private var tableView: UITableView
    private var items = ["Item 1","item 2","item 3","Item 4"]

    init(tableView: UITableView) {
        self.tableView = tableView
    }
    func numberOfSections(in tableView: UITableView) -> Int {
        return 1
    }
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return items.count

    }
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = ItemCell(style: .default, reuseIdentifier: "Cell")
        cell.label.text = items[indexPath.row]

        return cell
    }

}
Rizwan Mehboob
  • 1,333
  • 17
  • 19
  • Note for anyone trying to recreate this in obj-c, it's key to have something like `@property (nonatomic, strong) id delegate;` in your header file (note that the property has to be strong) – Evan Aug 12 '21 at 16:57
3

You can implement a custom class object, and implement the UITableViewDataSource methods for this class.

@interface MyDataSource : NSObject <UITableViewDataSource>

//...

@end

And then, the UITableView has properties, delegate and dataSource. Assign right objects to those properties.

MyDataSource ds = ... ///< Initialize the dataSource object.
self.tableView.dataSource = ds; ///< Let ds be the dataSource of `self.tableView`
self.tableView.delegate = .... ///< Assign the delegate, generally it is `self`.
AechoLiu
  • 17,522
  • 9
  • 100
  • 118
  • While this ain't swift, and you don't mention creating the tableView outlet, it's clear enough. Do you actually need self.tableView.dataSource or is tableView.dataSource enough in viewDIdLoad? – DrWhat Nov 26 '15 at 14:50
  • I don't know how to write swift. But I think they are the same. – AechoLiu Nov 26 '15 at 22:50
  • Custom class: class MyDataSource: NSObject {} Outlet (linked between storyboard and code in View Controller): @IBOutlet weak var tableView: UITableView ... In same VC, instantiate a variable myDataSource of type MyDataSource and assign tableView.dataSource to myDataSource: tableView.dataSource = myDataSource – DrWhat Nov 27 '15 at 08:05
2

Each Tableview should have its own Tableview controller. This is in accordance with the Model View Controller Design Pattern.

If the data in the the two tables are the same, you could have a common class serve as the dataSource.

TheAppMentor
  • 1,091
  • 7
  • 14