Swift - Reorder Uitableview Cells

Swift - Reorder UITableView cells

I have tried this...here is the code

In my example code there is button that starts the editing ---
Action Method of the button -->

@IBAction func editTableView (sender:UIBarButtonItem)
{
if listTableView.editing{
//listTableView.editing = false;
listTableView.setEditing(false, animated: false);
barButton.style = UIBarButtonItemStyle.Plain;
barButton.title = "Edit";
//listTableView.reloadData();
}
else{
//listTableView.editing = true;
listTableView.setEditing(true, animated: true);
barButton.title = "Done";
barButton.style = UIBarButtonItemStyle.Done;
//listTableView.reloadData();
}
}

And the related UITableView delegate methods -->

// The editing style for a row is the kind of button displayed to the left of the cell when in editing mode.

func tableView(tableView: UITableView!, editingStyleForRowAtIndexPath indexPath: NSIndexPath!) -> UITableViewCellEditingStyle
{
if (false == self.editing && !indexPath){
return UITableViewCellEditingStyle.None;
}

if (self.editing && indexPath.row == countryList.count){
return UITableViewCellEditingStyle.Insert;
}
else{
return UITableViewCellEditingStyle.Delete;
}
//return UITableViewCellEditingStyle.Delete;
}

// Update the data model according to edit actions delete or insert.
func tableView(tableView: UITableView!, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath!)
{
if editingStyle == UITableViewCellEditingStyle.Delete{
countryList.removeAtIndex(indexPath.row);
self.editTableView(barButton);
listTableView.reloadData();
}
else if editingStyle == UITableViewCellEditingStyle.Insert{
countryList.append("New Country");
}
}


// Determine whether a given row is eligible for reordering or not.
func tableView(tableView: UITableView!, canMoveRowAtIndexPath indexPath: NSIndexPath!) -> Bool
{
return true;
}

// Process the row move. This means updating the data model to correct the item indices.
func tableView(tableView: UITableView!, moveRowAtIndexPath sourceIndexPath: NSIndexPath!, toIndexPath destinationIndexPath: NSIndexPath!)
{
let item : String = countryList[sourceIndexPath.row];
countryList.removeAtIndex(sourceIndexPath.row);
countryList.insert(item, atIndex: destinationIndexPath.row)
}

You can also download full code Here

How can I use Drag and Drop to reorder a UITableView?

If you are performing a drag on a single item locally, you can use tableView(_:moveRowAt:to:). In order to do this, you need to implement UITableViewDragDelegate.

Setup

Start by setting your delegates. Setting dragInteractionEnabled is required for iPhones.

func viewDidLoad() {
super.viewDidLoad()
tableView.dataSource = self
tableView.delegate = self
tableView.dragDelegate = self
tableView.dragInteractionEnabled = true
}

UITableViewDragDelegate

Notice that the array is returning a single item. If you return more than one item, then the UITableViewDropDelegate methods will be used instead of tableView(_:moveRowAt:to:). You must set a local object.

func tableView(_ tableView: UITableView, itemsForBeginning session: UIDragSession, at indexPath: IndexPath) -> [UIDragItem] {
let dragItem = UIDragItem(itemProvider: NSItemProvider())
dragItem.localObject = data[indexPath.row]
return [ dragItem ]
}

Moving

This is where the moving happens and is actually a part of UITableViewDelegate.

func tableView(_ tableView: UITableView, moveRowAt sourceIndexPath: IndexPath, to destinationIndexPath: IndexPath) {
// Update the model
let mover = data.remove(at: sourceIndexPath.row)
data.insert(mover, at: destinationIndexPath.row)
}

You can also use tableView(_:canMoveRow:at:) and tableView(_:targetIndexPathForMoveFromRowAt: toProposedIndexPath:) if needed.

You can read more here...

  • Drag and Drop
  • Adopting Drag and Drop in a Table View

How to save reorder of rows in TableView with Swift

Your code in tableView(_:moveRowAt:to:) has some errors. You should change it to:

override func tableView(_ tableView: UITableView, moveRowAt indexPath: IndexPath, to: IndexPath) {
let itemToMove = list[indexPath.row]
list.remove(at: indexPath.row)
list.insert(itemToMove, at: to.row)

UserDefaults.standard.set(self.list, forKey:"soundboard")
}

Notice that I removed UserDefaults.standard.synchronize(). You should remove it from your other methods as well, since it is kind of a non-sense in this case. I have also changed list.insert(itemToMove, at: indexPath.row) to list.insert(itemToMove, at: to.row), because you need to place it to the "destination" index, and not put it back from where you removed it.

The rest of your code seems fine. It should be working once you perform these changes.

How do I reorder UITableView cells in Realm Swift?

I'd encourage you to store ordered items in a List rather than sorting based on an orderPosition property.

Storing the index manually will be much less performant when moving an item, because all objects between the "old index" and "new index" will need to be mutated to account for the change.

You can then use List.move(from:to:) to move an object from one index to another, which should correspond directly to the indices in the table view you're reordering.

Here's a tutorial you can follow guides you through building a task management app, including support for reordering tasks: https://realm.io/docs/realm-mobile-platform/example-app/cocoa/

swift uitableview reorder cells except the last cell

indexPath.row starts at zero. if myTableviewArray is your data source.

indexPath.row <myTableviewArray.count, this will be always true.

try using :

func tableView (_ tableView: UITableView, canMoveRowAt indexPath: IndexPath) -> Bool {
if indexPath.row < myTableviewArray.count - 1 {
return true
}
return false
}

Swift: Reorder Table Rows programmatically

You can programmatically remove a row from UITableView and insert a row programmatically. Before doing the operations on the UITableView, make sure to remove/add a specific item to the data source array. Otherwise, it'll just crash.

If you want to simply move the rows, you can use the code below. You need to do it in the place where the array which holds the data source is updated.

tableView.moveRow(at: oldIndexPath, to: newIndexPath)

If you want to do delete and insert new objects into the array, you may try the method as shown below.

let element = tasks.remove(at: indexPath.row)
tableView.deleteRows(at: indexPath, with: .automatic)

tasks.insert(element, at: tasks.count)
tableView.insertRows(at: [IndexPath(row: tasks.count, section: 0)], with: .automatic)

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.

moveRowAt Reorder TableView with Realm

One of the cool things about Realm List objects is they maintain their order.

In this case you would not need the sortingIndex property since those items are stored in a List.

class ShoppingListItem: Object {
@objc dynamic var department: String = ""
var item = List<ShoppingItem>() <- order is maintained
}

When you re-order a row in your tableView, reflect that change in the list by by inserting it at a new position and removing it from the old (which is done first depends on which direct the object is moved). You can either do it manually using .insert and .remove

itemList.remove(at: 5) //remove the shoppingItem object at index 5
itemList.insert(shoppingItem, at: 1) //insert the object at index 1

or use the super easy .move to move the object from one index to another.

itemList.move(from: 5, to: 1)


Related Topics



Leave a reply



Submit