I had a UITableViewController that had some cells registered in the Storyboard. These cells did not cause any problems. But it also contained other cells that I designed with a separate UINib, so I had to register them specifically in viewDidLoad
, like so:
override func viewDidLoad() {
super.viewDidLoad()
tableView.register(UINib(nibName: "SCSocialCell", bundle: nil), forCellReuseIdentifier: "SCSocialCell")
...
}
The problem appears to be that these manually registered cells are not really registered before the table starts to load data (which happens automatically in a UITableViewController). In other words numberOfSections
and numberOfRowsInSection
are called even before viewDidLoad is called (or finished, not sure).
I solved it by introducing a bool variable allTableViewCellsRegistered
, like so:
private var allTableViewCellsRegistered = false
override func viewDidLoad() {
super.viewDidLoad()
tableView.register(UINib(nibName: "SCSocialCell", bundle: nil), forCellReuseIdentifier: "SCSocialCell")
...
allTableViewCellsRegistered = true
tableView.reloadData()
}
And adding guard statements in numberOfSections
and numberOfRowsInSection
like so:
override func numberOfSections(in tableView: UITableView) -> Int {
guard allTableViewCellsRegistered else {
return 0
}
// rest of code
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
guard allTableViewCellsRegistered else {
return 0
}
// rest of code
}
Perhaps only adding this in numberOfSections
would do, but I added it in both and it's working now.
I believe this problem only surfaced on the iPad, because the cells in question were at the bottom of my tableView. On iPhone, these last/bottom cells dropped off the screen, so cellForRowAt
was not yet called for them on initial load. But by the time the user started scrolling to the bottom, viewDidLoad
was finished and the cells were registered. So that's why iPhone didn't give me any problems.
Hope this helps someone.