13

Sometimes when I tried to scroll a tableview to a row, I can unexpectedly supply a nonexistent section/row. And then the app will crash.

self.tableView.scrollToRow(at: IndexPath(row: targetRow, section: targetSection), at: UITableViewScrollPosition.bottom, animated: true);

How can I make this scrolling process crash-safe? I mean, if I do supply a nonexistent section/row, I want UITableView just ignore it. Or how can I check whether a section/row is exist or not within a UITableView before I do the scroll? Thanks.

Chen Li Yong
  • 5,459
  • 8
  • 58
  • 124

3 Answers3

23

Use this UITableView extension to check whether a Indexpath is valid.

extension UITableView
{
    func indexPathExists(indexPath:IndexPath) -> Bool {
        if indexPath.section >= self.numberOfSections {
            return false
        }
        if indexPath.row >= self.numberOfRows(inSection: indexPath.section) {
            return false
        }
        return true
    }
}

Use like this

var targetRowIndexPath = IndexPath(row: 0, section: 0)
if table.indexPathExists(indexPath: targetRowIndexPath)
{
  table.scrollToRow(at: targetRowIndexPath, at: .bottom, animated: true)
}
RajeshKumar R
  • 15,445
  • 2
  • 38
  • 70
  • This is another neat idea to make it an extension. Thanks! – Chen Li Yong Oct 02 '17 at 02:53
  • After a revisit to this question, I decided that this answer actually better, therefore I reselect my accepted answer to this one. – Chen Li Yong Mar 30 '20 at 02:08
  • Don't we also need to check that `numberOfRows(inSection: indexPath.section) > 0` to avoid `Attempted to scroll the table view to an out-of-bounds row (0) when there are only 0 rows in section 0` ? – Peter Warbo Aug 17 '23 at 10:36
13

Try this --

let indexPath = IndexPath(row: targetRow, section: targetSection)
if let _ = self.tableView.cellForRow(at: indexPath) {
 self.tableView.scrollToRow(at: indexPath, at: UITableViewScrollPosition.bottom, animated: true)
}
Aditya Srivastava
  • 2,630
  • 2
  • 13
  • 23
  • Oooouw clever! I forgot about that `cellForRow` function. Thanks! – Chen Li Yong Sep 29 '17 at 07:20
  • I think using "guard" would be even better alternative right since its especially used to avoid crashes ? like guard let _ = self.tableView.cellForRow(at: indexPath) else { return } – Bharath Sep 29 '17 at 07:57
  • @Bharath It depends where we are using it. There is no such like guard is better alternative for crash. It is just like returning from there only if the nil is found in the condition.. But if let just checks the condition if it's nil then also the remaining code of line will execute. You can check this [link also](https://stackoverflow.com/questions/32256834/swift-guard-vs-if-let) – Aditya Srivastava Sep 29 '17 at 08:22
  • @AdityaSrivastava : Well explained, thanks for the info. – Bharath Sep 29 '17 at 08:59
  • Usually I use guard if I want to avoid indentation hell in swift. https://thatthinginswift.com/guard-statement-swift/ BUT guard is unfit if we still need to run other lines of code unrelated to the condition, just like what Aditya has explained. – Chen Li Yong Oct 02 '17 at 02:51
  • 7
    Warning: cellForRow gets nil if the cell is not visible. – orafaelreis Jun 21 '19 at 13:10
  • @orafaelreis Yes, that's why I have used `if let` while calling `cellForRow` – Aditya Srivastava Jun 22 '19 at 06:04
  • 1
    @AdityaSrivastava if you need to scroll to some cell out of bound of visibility this won't help you. – orafaelreis Jun 22 '19 at 17:14
  • @orafaelreis Correct. But the question is how to scroll safely not scrolling to indexpath which is not visible. – Aditya Srivastava Jun 24 '19 at 06:32
  • This creates a new cell. Better is to check against the view or data model. – Karsten Jul 29 '22 at 08:40
0

Here's an another version with extension for UITableView that includes the scrollToRowSafely method

extension UITableView {
    func scrollToRowSafely(at indexPath: IndexPath, animated: Bool) {
        // Step 1: Check Data Availability
        guard indexPath.section < numberOfSections,
              indexPath.row < numberOfRows(inSection: indexPath.section) else {
            // Invalid index path, do not proceed with scrolling
            return
        }

        // Step 2: Perform on Main Thread
        DispatchQueue.main.async {

            // Step 3: Scroll Animation
            let scrollPosition: UITableView.ScrollPosition = .top

            // Step 4: Scroll to Row
            self.scrollToRow(at: indexPath, at: scrollPosition, animated: animated)
        }
    }
}

How to use it?

// Assuming you have a reference to your table view

tableView.scrollToRowSafely(at: IndexPath(row: 0, section: 0), animated: true)
chirag05k
  • 39
  • 5