How to Re-Order the Realm Table Using Tableview in Swift

how can I re-order the Realm table using tableview in swift

The most recommended way to manage the order of a list of objects in Realm is to have another Realm object manage it as a List property.

class FavouritesList {
let favourites = List<FavouritesRealm>()
}

This way, you can use the features of the List property itself to manage re-ordering objects, completely within the scope of Realm.

If you don't want to go through the trouble of adding another object, another approach I've used in shipping apps before is to simply add an 'orderedIndex' property that numerically indicates the ordering of the objects, which can then sorted via adding .sorted("orderedIndex") at the end of queries. But the first approach will be much quicker and easier to manage than this way.

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/

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)

Reordering Realm objects in a UITableView

Initially, I tried to be smart about this in determining which specific Realm objects
were affected by the move, and only modifying their orderedIndex values, but this turned
out to be somewhat complex, and in certain edge cases (Usually involving objects at the top and bottom),
would lead to unpredictable behaviour.

Ultimately, I decided for ultimate simplicity (At the expense of slightly more work), I would
simply copy the contents of my self.accounts object to a mutable array, perform the
move operation in the array, and then simply rebuild the orderedIndex of each object
from scratch there.

- (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(nonnull NSIndexPath *)sourceIndexPath toIndexPath:(nonnull NSIndexPath *)destinationIndexPath
{
if (sourceIndexPath.row == destinationIndexPath.row) {
return;
}

// Make a mutable copy of the accounts list
NSMutableArray *array = [NSMutableArray array];
for (Account *account in self.accounts) {
[array addObject:account];
}

// Re-order this array as dictated by the table view
Account *accountToMove = array[sourceIndexPath.row];
[array removeObject:accountToMove];
[array insertObject:accountToMove atIndex:destinationIndexPath.row];

// Loop through all of the items and reset their index value
[self.accounts.realm transactionWithBlock:^{
NSInteger i = 0;
for (Account *account in array) {
account.orderedIndex = i++;
}
}];
}

That ended up working perfectly. :)

Realm - Update List Order

Realm object order is not guaranteed. (unless you specify a sort order)

e.g. if you load 10 songs from Realm, they could come into your app an any order and the order could change between loads. The caveat to that is a Realm List object. Lists always maintain their order.

The problem in the question is you have Song objects stored in Realm but as mentioned above there is no ordering.

So the approach needs to be modified by leveraging a List object for each user to keep track of their songs:

class UserClass: Object {
@Persisted var name: String
@Persisted var songList = List<SongClass>()
}

When adding a song to a user, call it within a write transaction

try! realm.write {
someUser.songList.append(someSong)
}

suppose the user wants to switch the place of song 2 and song 3. Again, within a write transaction:

try! realm.write {
someUser.songList.move(from: 2, to: 3)
}

So then the UI bit - tableViews are backed by a tableView dataSource - this case it would be the songList property. When that dataSource is updated, the tableView should reflect that change.

In this case you would add an observer to the someUser.songList and as the underlying data changes, the observer will react to that change and can then update the UI.

You can do something simple like tableView.reloadData() to reflect the change or if you want fine-grained changes (like row animations for example) you can do that as well. In that same guide, see the code where tableView.deleteRows, .insertRows and .reload is handled. You know what rows were changed in the underlying data there so you can then animate the rows in the tableView.

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

Store ordering in Realm

Realm already has an ordered property type: List

Using this type will preserve ordering without having to store an index order on your model.



Related Topics



Leave a reply



Submit