Datasource and Traitcollection

Example of accessing the traitCollections within the DataSource. This example illustrates how inheritance can be useful in this situation.

I had this situation where I refactored my UIViewController so that the UITableViewDataSource was in a separate file. Below is the example code (shorten for illustration purposes).

class ScheduleTableViewController: UIViewController {

  @IBOutlet var tableView: UITableView!
  private var dataSource = ScheduleDataSource()
   ...
}
class ScheduleDataSource: NSObject, UITableViewDataSource, UITableViewDelegate {

  func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        // #warning Incomplete implementation, return the number of rows
        return 1
    }


    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

      if traitCollection.preferredContentSizeCategory > .extraExtraLarge {
        //...
        } else {
        //...
        }
        return cell
    }

}

When I had everything in one UIViewController I had access to traitcollection. As shown in the screenshot below.

Now that I have my tableView methods grouped together in a file called ScheduleDataSource which is a subclass of UITableViewDataSource I have lost access to the traitCollection.

This did not make sense initially.

I still needed access to the traitCollection because I have code that changes the tableView cell based on the traitCollection.preferredContentSizeCategory. The code below illustrates what I previously had.

Before

class ScheduleTableViewController: UIViewController {

  func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
      if traitCollection.preferredContentSizeCategory > .extraExtraLarge {
          // Use the cell with one column layout
      } else {
          // Use the cell with one column layout
      }
  }

}

Now I had to investigate on how do I get access to that traitCollection property again? Should I conform to the UITraitEnvironment or UITraitCollection protocol?

My initial thinking was to use a protocol. But it turned out the answer was much easier.

Inheritance

The xcode autocomplete screenshot shown earlier in the post tells you the answer. It states that the traitCollection is available for UIViewControllers and UIViews and its subclasses.

The key is its subclasses.

With tableView it subclasses UIScrolllView, which subclasses UIView. This means I should have access to the traitCollection through the tableView.

Knowing this you just need to write it out with tableView.traitCollection, like shown in the example below.

After

class ScheduleDataSource: NSObject, UITableViewDataSource, UITableViewDelegate {

  // ...

  func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

      if tableView.traitCollection.preferredContentSizeCategory > .extraExtraLarge {
          // Use the cell with one column layout
      } else {
          // Use the cell with two column layout
      }

  }

}

I didn’t need to conform to any protocol or access the collection through UIScreen.main.traitCollection. The use of inheritance allowed the solution to be done with minimum effort.