Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Swift - Movable rows in tableView only within a section, not between

Is there a way to prevent cells in a tableView from being moved to a different section?

The sections have data for different types of cells, so the app crashes when the user tries to drag a cell into a different section.

I would like to only allow the user to move a cell inside the section, and not in between sections.

Relevant code is below:

override func tableView(_ tableView: UITableView, canMoveRowAt indexPath: IndexPath) -> Bool {
    return true
}

override func tableView(_ tableView: UITableView, moveRowAt sourceIndexPath: IndexPath, to destinationIndexPath: IndexPath) {
    let reorderedRow = self.sections[sourceIndexPath.section].rows.remove(at: sourceIndexPath.row)
    self.sections[destinationIndexPath.section].rows.insert(reorderedRow, at: destinationIndexPath.row)

    self.sortedSections.insert(sourceIndexPath.section)
    self.sortedSections.insert(destinationIndexPath.section)
}
like image 975
user3628240 Avatar asked Nov 30 '22 14:11

user3628240


2 Answers

You will need to implement the UITableViewDelegate method targetIndexPathForMoveFromRowAt.

Your strategy will be to allow the move if the source and destination section are the same. If they aren't then you can return either row 0, if the proposed destination section is less than the source section or the last row of the section if the proposed destination section is greater than the source section.

This will constrain the move to the source section.

override func tableview(_ tableView: UITableView, targetIndexPathForMoveFromRowAt sourceIndexPath: IndexPath, toProposedIndexPath proposedDestinationIndexPath: IndexPath) -> IndexPath {

    let sourceSection = sourceIndexPath.section
    let destSection = proposedDestinationIndexPath.section

    if destSection < sourceSection {
        return IndexPath(row: 0, section: sourceSection)
    } else if destSection > sourceSection {
        return IndexPath(row: self.tableView(tableView, numberOfRowsInSection:sourceSection)-1, section: sourceSection)
    }

    return proposedDestinationIndexPath
}
like image 200
Paulw11 Avatar answered Dec 07 '22 01:12

Paulw11


You can retarget the proposed destination for restriction by implementing the tableView:targetIndexPathForMoveFromRowAtIndexPath:toProposedIndexPath: method

  func tableView(_ tableView: UITableView, targetIndexPathForMoveFromRowAt sourceIndexPath: IndexPath, toProposedIndexPath proposedDestinationIndexPath: IndexPath) -> IndexPath {

    // Finds number of items in source group
    let numberOfItems = self.tableView(tableView, numberOfRowsInSection: sourceIndexPath.section)

    // Restricts rows to relocation in their own group by checking source and destination sections
    if (sourceIndexPath.section != proposedDestinationIndexPath.section) {

      /*
       if we move the row to the not allowed upper area, it is moved to the top of the allowed group and vice versa
       if we move the row to the not allowed lower area, it is moved to the bottom of the allowed group
       also prevents moves to the last row of a group (which is reserved for the add-item placeholder).
      */
      let rowInSourceSection = (sourceIndexPath.section > proposedDestinationIndexPath.section) ? 0 : numberOfItems - 1;

      return IndexPath(row: rowInSourceSection, section: sourceIndexPath.section)
    }
    // Prevents moves to the last row of a group (which is reserved for the add-item placeholder).
    else if (proposedDestinationIndexPath.row >= numberOfItems) {

      return IndexPath(row: numberOfItems - 1, section: sourceIndexPath.section)
    }
    // Passing all restrictions
    return proposedDestinationIndexPath
  }
like image 22
ozmpai Avatar answered Dec 06 '22 23:12

ozmpai