How to Limit Uitableview Row Reordering to a Section

iOS UITableView limit reordering only in one section

Use the table view delegate method tableView:targetIndexPathForMoveFromRowAtIndexPath:toProposedIndexPath: to check the proposed index path and return an index path in a different section if the user choice isn't acceptable.

Reordering sections in UITableView

There is no native functionality to achieve what you want. If I understand correctly you would want to collapse a whole section of rows and then start dragging the "header" around. If you want to do this on your own I would suggest starting with a pan gesture recognizer which triggers on the header button.

The gesture should be relatively obvious. After it starts on the header you need to track position using locationIn in your table view.

To collapse rows all you need to do is modify your table view cells with appropriate animation like:

tableView.beginUpdates()
tableView.deleteSections([myIndexPath], with: .top) // Maybe experiment with animation type
// Modify whatever you need to correspond this change in the data source
tableView.endUpdates()

Since you will be removing the section you will also be removing the view (header) which has the gesture recognizer. That means it might be better adding the gesture to the table view directly or its superview even. You will need to force it to trigger only when one of those buttons on headers is pressed. You can get some idea here about it. The rest is unaffected by this change.

At this point you will probably need to create an extra view which represents your section stack and follows your finger. This should be pretty easy if you add it as a subview and manipulate it's center with pan gesture recognizer locationIn in it's superview:

movableSectionView.center = panGestureRecognizer.location(in: movableSectionView.superview!) 

So up to this point you should be able to grab a section which collapses all cells and be able to drag the "section stack" view around. Now you need to check where in table view your finger is to know where to drop the section. This is a bit painful but can be done with visibleCells and tableView.indexPath(for: ):

func indexPathForGestureRecognizer(_ recognizer: UIGestureRecognizer) -> IndexPath {
let coordinateView: UIView = tableView.superview! // This can actually be pretty much anything as long as it is in hierarchy
let y = recognizer.location(in: coordinateView).y
if let hitCell = tableView.visibleCells.first(where: { cell in
let frameInCoordinateView = cell.convert(cell.bounds, to: coordinateView)
return frameInCoordinateView.minY >= y && frameInCoordinateView.maxY <= y
}) {
// We have the cell at which the finger is. Retrieve the index path
return tableView.indexPath(for: hitCell) ?? IndexPath(row: 0, section: 0) // This should always succeed but just in case
} else {
// We may be out of bounds. That may be either too high which means above the table view otherwise too low
if recognizer.location(in: tableView).y < 0.0 {
return IndexPath(row: 0, section: 0)
} else {
guard tableView.numberOfSections > 0 else {
return IndexPath(row: 0, section: 0) // Nothing in the table view at all
}
let section = tableView.numberOfSections-1
return IndexPath(row: tableView.numberOfRows(inSection: section), section: section)
}
}
}

Once the gesture recognizer ends you can use this method to get the section you are dropping your items into. So just:

tableView.beginUpdates()
// Modify whatever you need to correspond this change in the data source
tableView.insertSections([indexPathForGestureRecognizer(panGestureRecognizer).section], with: .bottom)
tableView.endUpdates()

This should basically be enough for reordering but you might want to show in table view where the dragged section is. Like having a placeholder at the end of the section in which the stack will be dropped into. That should be relatively easy by simply adding and then moving an extra placeholder cell reusing indexPathForGestureRecognizer to get a position for it.

Have fun.

UITableView reordering cells in UIViewController - reorder control not showing

To implement row reordering in a UITableView, you need to implement tableView:canMoveRowAtIndexPath: and tableView:moveRowAtIndexPath:toIndexPath:, You might also want to implement tableView:targetIndexPathForMoveFromRowAtIndexPath:toProposedIndexPath:.

And the most important, the table view must be in edit mode for the reorder controls to appear.

Rearranging between sections in UITableView

I'm not aware of a specific bug, but have you been overriding tableView:targetIndexPathForMoveFromRowAtIndexPath:toProposedIndexPath: explicitly to check the section the row is being attempted to move into? Maybe you're not and the default behavior just happens to work one way and not the other?

Enable UITableView edit only in a specified section

You can achieve this by implementing this delegate method

-(BOOL)tableView:(UITableView *)tableView canMoveRowAtIndexPath:(NSIndexPath *)indexPath {
if (indexPath.section == 3) {
return YES;
} else {
return NO;
}
}

DiffableDataSource - Is there a way to limit reordering only be performed within the same section?

This behaviour isn't a question for your data source. It is a question for your delegate

You can use
collectionView(_:targetIndexPathForMoveFromItemAt:toProposedIndexPath:) to determine whether a move is permitted

func collectionView(_ collectionView: UICollectionView, 
targetIndexPathForMoveFromItemAt originalIndexPath: IndexPath,
toProposedIndexPath proposedIndexPath: IndexPath) -> IndexPath {

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

var destination = proposedDestinationIndexPath

if destSection < sourceSection {
destination = IndexPath(item: 0, section: sourceSection)
} else if destSection > sourceSection {
destination = IndexPath(item: self.backingStore[sourceSection].count-1, section: sourceSection)
}

return destination
}

This constrains an item's movement to its own section.



Related Topics



Leave a reply



Submit