Before I've just used tableView.setEditing(true, animated: true) in order to make rows in tableView move. I used the code:
override func tableView(_ tableView: UITableView, canMoveRowAt indexPath: IndexPath) -> Bool {
return true
}
override func tableView(_ tableView: UITableView, moveRowAt sourceIndexPath: IndexPath, to destinationIndexPath: IndexPath) {
array.insert(array.remove(at: sourceIndexPath.row), at: destinationIndexPath.row)
}
I've found that there is an interesting protocols: UITableViewDragDelegate and UITableViewDropDelegate. I have to implement 2 funcs and add tableView.dragDelegate = self; tableView.dropDelegate = self; tableView.dragInteractionEnabled = true
func tableView(_ tableView: UITableView, itemsForBeginning session: UIDragSession, at indexPath: IndexPath) -> [UIDragItem] {
return []
}
func tableView(_ tableView: UITableView, performDropWith coordinator: UITableViewDropCoordinator) {
}
What is surprising to me is that this code is enough to make rows move. And I have a simple question - is it that simple? May be I miss something, may be I have to add something?
Related
I'm new to iOS development and now I'm trying to achieve this effect:
As you can see, when dragging, the cell's background is transparent and items above or below it will move away with a fluent animation.
Either UIKit or SwiftUI will do. Any advice will be appreciated. :)
This is achievable with the help of table delegates.Just implement below methods.
func tableView(_ tableView: UITableView, canMoveRowAt indexPath: IndexPath) -> Bool {
return true
}
func tableView(_ tableView: UITableView, editingStyleForRowAt indexPath: IndexPath) -> UITableViewCell.EditingStyle {
return .none
}
func tableView(_ tableView: UITableView, moveRowAt sourceIndexPath: IndexPath, to destinationIndexPath: IndexPath) {
let itemToReorder = yourList[sourceIndexPath.row]
yourList.remove(at: sourceIndexPath.row)
yourList.insert(itemToReorder, at: destinationIndexPath.row)
}
Don't forget to enable disable editing mode when needed or not.
Use this for enabling and disabling
tableView.setEditing(true, animated: true)
According to the Apple documentation to support drag and drop for UITableView I have to implement UITableViewDragDelegate and UITableViewDropDelegate. I implemented only UITableViewDragDelegate with fake implementation for tableView(_:itemsForBeginning:at:) (it return empty list). But this solution works.
Why does it work?
import UIKit
class TableViewController: UITableViewController, UITableViewDragDelegate {
var data = [1, 2, 3, 4, 5]
// MARK: - Table view drag delegate
func tableView(_ tableView: UITableView, itemsForBeginning session: UIDragSession, at indexPath: IndexPath) -> [UIDragItem] {
// fake implementation with empty list
return []
}
// MARK: - Table view data source
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return data.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
cell.textLabel?.text = "Cell \(data[indexPath.row])"
return cell
}
override func tableView(_ tableView: UITableView, moveRowAt sourceIndexPath: IndexPath, to destinationIndexPath: IndexPath) {
let tmp = data[sourceIndexPath.row]
data.remove(at: sourceIndexPath.row)
data.insert(tmp, at: destinationIndexPath.row)
}
// MARK: - View controller
override func viewDidLoad() {
super.viewDidLoad()
self.tableView.dragInteractionEnabled = true
self.tableView.dragDelegate = self
}
}
It doesn't work. What you are seeing is not drag and drop.
You are seeing row rearrangment, using the "reorder control". This mechanism is very old; it existed for many years before drag and drop was created. That is the effect of your implementation of
override func tableView(_ tableView: UITableView, moveRowAt sourceIndexPath: IndexPath, to destinationIndexPath: IndexPath) {
It is a UITableViewDataSource method; it has nothing to do with drag and drop. See:
https://developer.apple.com/documentation/uikit/uitableviewdatasource/1614867-tableview
If you delete that method, the old row rearrangement mechanism will cease to operate, and now you'll be using drag and drop and your implementation of
func tableView(_ tableView: UITableView, itemsForBeginning session: UIDragSession, at indexPath: IndexPath) -> [UIDragItem] {
will be meaningful.
I have a UITableView which I need to both reorder and allow for cells to be deleted by swiping left on a cell to reveal the Delete action. Is it possible to have both at the same time?
For the table I have:
tableView.allowsSelectionDuringEditing = true
tableView.isEditing = true
For the datasource:
func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
return true
}
func tableView(_ tableView: UITableView, canMoveRowAt indexPath: IndexPath) -> Bool {
return true
}
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
// I do nothing here
}
func tableView(_ tableView: UITableView, moveRowAt sourceIndexPath: IndexPath, to destinationIndexPath: IndexPath) {
// This is where I rearrange my data source array
}
And for the delegate:
func tableView(_ tableView: UITableView, editingStyleForRowAt indexPath: IndexPath) -> UITableViewCellEditingStyle {
return .none
}
func tableView(_ tableView: UITableView, shouldIndentWhileEditingRowAt indexPath: IndexPath) -> Bool {
return false
}
func tableView(_ tableView: UITableView, editActionsForRowAt indexPath: IndexPath) -> [UITableViewRowAction]? {
let delete = UITableViewRowAction(style: .destructive, title: "Remove") { action, index in
// delete row
}
delete.backgroundColor = UIColor.appRed
return [delete]
}
So what I want to have is:
Reorder control allowing me to move a cell
Swipe left to reveal cell actions
And I need these to be visible and work at the same time. Can this be done?
Swipe to delete is not possible when your UITableView is in editing mode.
If you want to hide the delete button on the left when in editing mode you have to implement the following methods:
override func tableView(_ tableView: UITableView, shouldIndentWhileEditingRowAt indexPath: IndexPath) -> Bool {
return false
}
override func tableView(_ tableView: UITableView, editingStyleForRowAt indexPath: IndexPath) -> UITableViewCellEditingStyle {
return tableView.isEditing ? .none : .delete
}
I think this is the closest you can get.
My structure is the following:
UIViewController -> UIVIew -> UITableView
tableView(_ tableView: UITableView, numberOfRowsInSection section: Int)
and
tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath)
is called but
tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath)
is not called.
class ViewcontrollerA : UIViewController , UITableViewDelegate , UITableViewDataSource {
override func viewDidLoad() {
super.viewDidLoad()
self.doctortableView.delegate = self
self.doctortableView.dataSource = self
self.doctortableView.allowsSelection = true
}
func callTable() {
doctortableView.frame = CGRect(......)
view1.addSubview(doctortableView)
doctortableView.isUserInteractionEnabled = true
self.view.addSubview(view1)
}
// Tableview Functions
// Number of Sections in Table
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
var count = Int()
return count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
var cell = UITableViewCell()
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
//This function is not called
}
}
The problem seems to be in this datasource method at first glance.
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
var count = Int()
return count
}
Doesn't Int() return 0? That means your tableview doesn't have any cells.
As per discussion on Chat
You are creating TableView programatically and add to self.view
func callTable() {
doctortableView.frame = CGRect(......)
view1.addSubview(doctortableView)
doctortableView.isUserInteractionEnabled = true
self.view.addSubview(view1)
}
but your tableView is not on top, so you are not able to scroll it and that's why delegate are not working
following line is missing
self.view.bringSubview(toFront: doctortableView)
Here is Full code
func callTable() {
doctortableView.frame = CGRect(......)
view1.addSubview(doctortableView)
doctortableView.isUserInteractionEnabled = true
self.view.addSubview(view1)
self.view.bringSubview(toFront: doctortableView)
}
I have uiviewcontroller with uitableview
and swipe to delete work fine but
when I set uitableview semantic to force right-to-left the swipe to delete not even shown in the tableview
classViewController:UIViewController,UITableViewDelegate,UITableViewDataSource {
var yourArray : [Type] = []
override func viewDidLoad() {
super.viewDidLoad()
tablView.delegate = self
tablView.dataSource = self
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return yourArray.count
}
func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
return true
}
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == .delete{
tableView.beginUpdates()
yourArray.remove(at: indexPath.row)
tableView.endUpdates()
tableView.reloadData()
}
}
}