(Swift) Deleting UITableViewCell by dragging it out of the UITableView? - ios

I currently have a UITableView with items from an array in it. It's possible to move the position of the UITableViewCell with anUILongPressGestureRecognizer freely around the Y and X axis, however I want to be able to delete the dragged UITableViewCell if it's moved outside of the UITableView.
I'm mostly aware I'll be using a Switch CaseUIGestureRecognizer.Ended to perform this but I'm currently at a loss on how to deal with it.
Here's the code:
func longPressGestureRecognized(gestureRecognizer: UIGestureRecognizer) {
let longPress = gestureRecognizer as! UILongPressGestureRecognizer
let state = longPress.state
var locationInView = longPress.locationInView(tblTasks)
var indexPath = tblTasks.indexPathForRowAtPoint(locationInView)
struct My {
static var cellSnapshot: UIView? = nil
}
struct Path {
static var initialIndexPath : NSIndexPath? = nil
}
switch state {
case UIGestureRecognizerState.Began:
if indexPath != nil {
Path.initialIndexPath = indexPath
let cell = tblTasks.cellForRowAtIndexPath(indexPath!) as UITableViewCell!
My.cellSnapshot = snapshotOfCell(cell)
var center = cell.center
My.cellSnapshot!.center = center
My.cellSnapshot!.alpha = 0.0
tblTasks.addSubview(My.cellSnapshot!)
UIView.animateWithDuration(0.25, animations: { () -> Void in
center.y = locationInView.y
My.cellSnapshot!.center = center
My.cellSnapshot!.transform = CGAffineTransformMakeScale(1.05, 1.05)
My.cellSnapshot!.alpha = 0.98
cell.alpha = 0.0
}, completion: { (finished) -> Void in
if finished {
cell.hidden = true
}
}) // End of animation sequence
} // End of IF statement
case UIGestureRecognizerState.Changed:
var center = My.cellSnapshot!.center
center.x = locationInView.x
center.y = locationInView.y
My.cellSnapshot!.center = center
if (indexPath != nil) && (indexPath != Path.initialIndexPath) {
swap(&taskMgr.tasks[indexPath!.row], &taskMgr.tasks[Path.initialIndexPath!.row])
tblTasks.moveRowAtIndexPath(Path.initialIndexPath!, toIndexPath: indexPath!)
Path.initialIndexPath = indexPath
}
default:
let cell = tblTasks.cellForRowAtIndexPath(Path.initialIndexPath!) as UITableViewCell!
cell.hidden = false
cell.alpha = 0.0
UIView.animateWithDuration(0.25, animations: { () -> Void in
My.cellSnapshot!.center = cell.center
My.cellSnapshot!.transform = CGAffineTransformIdentity
My.cellSnapshot!.alpha = 0.0
cell.alpha = 1.0
}, completion: { (finished) -> Void in
if finished {
Path.initialIndexPath = nil
My.cellSnapshot!.removeFromSuperview()
My.cellSnapshot = nil
}
})
} // End of switch
} // End of longPressGestureRecognized func
Any ideas?

You're on the right track but you need to embed your table view controller in the container of another view controller, and then put the gesture recognizer code in the container view controller.
This will help a lot: http://www.freshconsulting.com/create-drag-and-drop-uitableview-swift/
It's not 100% right but pretty close and I was able to use it to get exactly this going.

Related

Drag & Drop Table view cell has weired buggy animation while dragging cell (Using long press gesture)

I'm following this to perform drag and drop table view cell but I don't know why I have buggy weird animation while dragging cell to other position in the table. I don't know what actually causes this type of issue, any help or suggestion would be appreciated. You can check the code and result below:
Code:
var longGesture: UILongPressGestureRecognizer!
var scrollRate: CGFloat = 0
var dragInitialIndexPath: IndexPath?
var dragCellSnapshot: UIView?
var scrollDisplayLink: CADisplayLink?
self.longGesture = UILongPressGestureRecognizer(target: self, action: #selector(onLongPressGesture(sender:)))
self.longGesture.minimumPressDuration = 0.3
self.tblEditProject.addGestureRecognizer(self.longGesture)
//MARK: tableCell reorder / long press
#objc func onLongPressGesture(sender: UILongPressGestureRecognizer) {
self.longGesture = sender
let state = sender.state
let locationInView = self.longGesture.location(in: self.tblEditProject)
let indexPath = self.tblEditProject.indexPathForRow(at: locationInView)
switch state {
case .began:
if indexPath != nil {
guard let currentIndexPath = indexPath else { return }
dragInitialIndexPath = currentIndexPath
self.scrollDisplayLink = CADisplayLink(target: self, selector: #selector(scrollTableWithCell))
self.scrollDisplayLink?.add(to: .main, forMode: .default)
guard let cell = self.tblEditProject.cellForRow(at: currentIndexPath) else { return }
dragCellSnapshot = self.snapshotOfCell(inputView: cell)
var center = cell.center
dragCellSnapshot?.center = center
dragCellSnapshot?.alpha = 0.0
self.tblEditProject.addSubview(dragCellSnapshot!)
UIView.animate(withDuration: 0.25, animations: { () -> Void in
center.y = locationInView.y
self.dragCellSnapshot?.center = center
self.dragCellSnapshot?.transform = CGAffineTransform(scaleX: 1.0, y: 1.0)
self.dragCellSnapshot?.alpha = 0.98
cell.alpha = 0.0
}, completion: { _ in
cell.isHidden = true
})
}
case .changed:
if indexPath != nil {
self.calculateScroll(gestureRecognizer: self.longGesture)
var center = dragCellSnapshot?.center
center?.y = locationInView.y
dragCellSnapshot?.center = center!
if indexPath != nil && indexPath != dragInitialIndexPath {
// update your data model
let dataToMove = self.arrEditProject[0].section[dragInitialIndexPath!.row]
self.arrEditProject[0].section.remove(at: dragInitialIndexPath!.row)
self.arrEditProject[0].section.insert(dataToMove, at: indexPath!.row)
self.tblEditProject.moveRow(at: dragInitialIndexPath!, to: indexPath!)
dragInitialIndexPath = indexPath
}
}
case .ended:
guard let persistedIndexPath = dragInitialIndexPath else { return }
guard let cell = self.tblEditProject.cellForRow(at: persistedIndexPath) else { return }
cell.isHidden = false
cell.alpha = 0.0
UIView.animate(withDuration: 0.25, animations: { () -> Void in
self.dragCellSnapshot?.center = cell.center
self.dragCellSnapshot?.transform = CGAffineTransform.identity
self.dragCellSnapshot?.alpha = 0.0
cell.alpha = 1.0
}, completion: { _ in
self.dragInitialIndexPath = nil
self.dragCellSnapshot?.removeFromSuperview()
self.dragCellSnapshot = nil
self.isUpdateContent = true
self.tblEditProject.reloadData()
/// For scrolling while dragging
self.scrollDisplayLink?.invalidate()
self.scrollDisplayLink = nil
self.scrollRate = 0
})
default:
guard let persistedIndexPath = dragInitialIndexPath else { return }
guard let cell = self.tblEditProject.cellForRow(at: persistedIndexPath) else { return }
cell.isHidden = false
cell.alpha = 0.0
UIView.animate(withDuration: 0.25) {
self.dragCellSnapshot?.center = cell.center
self.dragCellSnapshot?.transform = CGAffineTransform.identity
self.dragCellSnapshot?.alpha = 0.0
cell.alpha = 1.0
} completion: { _ in
self.dragInitialIndexPath = nil
self.dragCellSnapshot?.removeFromSuperview()
self.dragCellSnapshot = nil
}
}
}
func snapshotOfCell(inputView: UIView) -> UIView {
UIGraphicsBeginImageContextWithOptions(inputView.bounds.size, false, 0.0)
inputView.layer.render(in: UIGraphicsGetCurrentContext()!)
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
let cellSnapshot = UIImageView(image: image)
cellSnapshot.layer.masksToBounds = false
cellSnapshot.layer.cornerRadius = 0.0
cellSnapshot.layer.shadowOffset = CGSize(width: -5.0, height: 0.0)
cellSnapshot.layer.shadowRadius = 5.0
cellSnapshot.layer.shadowOpacity = 0.4
return cellSnapshot
}
func calculateScroll(gestureRecognizer: UIGestureRecognizer) {
print("Call scroll table with cell")
let location = gestureRecognizer.location(in: self.tblEditProject)
var rect: CGRect = self.tblEditProject.bounds
/// adjust rect for content inset as we will use it below for calculating scroll zones
rect.size.height -= self.tblEditProject.contentInset.top
/// tell us if we should scroll and which direction
let scrollZoneHeight = rect.size.height / 6
let bottomScrollBeginning = self.tblEditProject.contentOffset.y + self.tblEditProject.contentInset.top + rect.size.height - scrollZoneHeight
let topScrollBeginning = self.tblEditProject.contentOffset.y + self.tblEditProject.contentInset.top + scrollZoneHeight
/// we're in the bottom zone
if (location.y >= bottomScrollBeginning)
{
self.scrollRate = (location.y - bottomScrollBeginning) / scrollZoneHeight
}
/// we're in the top zone
else if (location.y <= topScrollBeginning)
{
self.scrollRate = (location.y - topScrollBeginning) / scrollZoneHeight
}
else
{
self.scrollRate = 0
}
}
#objc func scrollTableWithCell() {
print("Call scroll table with cell")
let gestureLong = self.longGesture
let location = gestureLong?.location(in: self.tblEditProject)
let currentOffset = self.tblEditProject.contentOffset
var newOffset = CGPoint(x: currentOffset.x, y: currentOffset.y + self.scrollRate * 10)
if (newOffset.y < -self.tblEditProject.contentInset.top) {
newOffset.y = -self.tblEditProject.contentInset.top
} else if (self.tblEditProject.contentSize.height + self.tblEditProject.contentInset.bottom < self.tblEditProject.frame.size.height) {
newOffset = currentOffset
} else if (newOffset.y > (self.tblEditProject.contentSize.height + self.tblEditProject.contentInset.bottom) - self.tblEditProject.frame.size.height) {
newOffset.y = (self.tblEditProject.contentSize.height + self.tblEditProject.contentInset.bottom) - self.tblEditProject.frame.size.height;
}
self.tblEditProject.setContentOffset(newOffset, animated: false)
if let lction = location {
if (lction.y >= 0 && lction.y <= self.tblEditProject.contentSize.height + 50) {
var center = dragCellSnapshot?.center
center?.y = lction.y
dragCellSnapshot?.center = center ?? CGPoint.zero
var indexPath = self.tblEditProject.indexPathForRow(at: lction)
/// Check if the pointer is bigger than the table height to set indexPath as the last cell
if (self.tblEditProject.contentSize.height < lction.y) {
indexPath = IndexPath(row: (self.tblEditProject.numberOfRows(inSection: 0)) - 1, section: 0)
}
if let pathIndex = indexPath {
if !pathIndex.isEmpty && pathIndex != dragInitialIndexPath {
// update your data model
let dataToMove = self.arrEditProject[0].section[dragInitialIndexPath!.row]
self.arrEditProject[0].section.remove(at: dragInitialIndexPath!.row)
self.arrEditProject[0].section.insert(dataToMove, at: pathIndex.row)
self.tblEditProject.moveRow(at: dragInitialIndexPath!, to: pathIndex)
dragInitialIndexPath = pathIndex
}
}
}
}
}
Result:
Apple's already performed Drag&Drop mechanism for UITableView and UICollectionView (Apple Documentation, How to use it). You don't need to develop it from scratch.

How to swap two cells of two different tables view?

I'm stuck with this problem:
I'm using this code to swap two cells of the same table view. I want to ask you how I should edit this code to swap two cells of two different table view in the same controller? I think that there isn't much to edit but I can't do on my own... thank you all.
#objc func onLongPressGestureDone(sender: UILongPressGestureRecognizer) {
let locationInView = sender.location(in: tableViewDone)
let indexPath = tableViewDone.indexPathForRow(at: locationInView)
if sender.state == .began {
if indexPath != nil {
initialIndexPathDone = indexPath
let cell = tableViewDone.cellForRow(at: indexPath!)
cellSnapshotDone = snapshotOfCellDone(inputView: cell!)
var center = cell?.center
cellSnapshotDone?.center = center!
cellSnapshotDone?.alpha = 0.0
tableViewDone.addSubview(cellSnapshotDone!)
UIView.animate(withDuration: 0.25, animations: { () -> Void in
center?.y = locationInView.y
self.cellSnapshotDone?.center = center!
self.cellSnapshotDone?.transform = (self.cellSnapshotDone?.transform.scaledBy(x: 1.05, y: 1.05))!
self.cellSnapshotDone?.alpha = 0.99
cell?.alpha = 0.0
}, completion: { (finished) -> Void in
if finished {
cell?.isHidden = true
}
})
}
} else if sender.state == .changed {
var center = cellSnapshotDone?.center
center?.y = locationInView.y
cellSnapshotDone?.center = center!
if ((indexPath != nil) && (indexPath != initialIndexPathDone)) {
progetto.done.swapAt(indexPath!.row, initialIndexPathDone!.row)
tableViewDone.moveRow(at: initialIndexPathDone!, to: indexPath!)
initialIndexPathDone = indexPath
}
} else if sender.state == .ended {
let cell = tableViewDone.cellForRow(at: initialIndexPathDone!)
cell?.isHidden = false
cell?.alpha = 0.0
UIView.animate(withDuration: 0.25, animations: { () -> Void in
self.cellSnapshotDone?.center = (cell?.center)!
self.cellSnapshotDone?.transform = CGAffineTransform.identity
self.cellSnapshotDone?.alpha = 0.0
cell?.alpha = 1.0
}, completion: { (finished) -> Void in
if finished {
self.initialIndexPathDone = nil
self.cellSnapshotDone?.removeFromSuperview()
self.cellSnapshotDone = nil
}
})
}
}
func snapshotOfCellDone(inputView: UIView) -> UIView {
UIGraphicsBeginImageContextWithOptions(inputView.bounds.size, false, 0.0)
inputView.layer.render(in: UIGraphicsGetCurrentContext()!)
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
let cellSnapshotDone = UIImageView(image: image)
cellSnapshotDone.layer.masksToBounds = false
cellSnapshotDone.layer.cornerRadius = 0.0
cellSnapshotDone.layer.shadowOffset = CGSize(width: -5.0, height: 0.0)
cellSnapshotDone.layer.shadowRadius = 5.0
cellSnapshotDone.layer.shadowOpacity = 0.4
return cellSnapshotDone
}
You can do this easier with the moveRow(at:to:) function:
tableView.beginUpdates()
tableView.moveRow(at: firstIndexPath, to: secondIndexPath)
tableView.moveRow(at: secondIndexPath, to: firstIndexPath)
tableView.endUpdates()

Removing a UILongPressGesture default motions

I have a setting that allows the user to turn on and off the UILongPressGesture on a UITableView cell. I have a simple bool that I am checking if I should add or remove the gesture.
// Gesture Recognizer
let longPress: UILongPressGestureRecognizer = UILongPressGestureRecognizer(target: self, action: #selector(longPressDetected(_:)))
if(options?.alphaOrder == false){
self.tableView.addGestureRecognizer(longPress)
}
else{
self.tableView.removeGestureRecognizer(longPress)
}
Long press method
func longPressDetected(_ sender: Any){
let longPress:UILongPressGestureRecognizer = sender as! UILongPressGestureRecognizer
let state:UIGestureRecognizerState = longPress.state
let location:CGPoint = longPress.location(in: self.tableView) as CGPoint
let indexPath = self.tableView.indexPathForRow(at: location)
var snapshot:UIView!
var sourceIndexPath:NSIndexPath?
if (holderView) != nil{
snapshot = holderView
}
if (beginningIndexPath) != nil{
sourceIndexPath = beginningIndexPath
}
switch(state){
case UIGestureRecognizerState.began:
if let test = indexPath{
sourceIndexPath = indexPath! as NSIndexPath
beginningIndexPath = sourceIndexPath
let cell:UITableViewCell = self.tableView.cellForRow(at: test)!
snapshot = self.customSnapShotFrom(view: cell)
holderView = snapshot
var center:CGPoint = cell.center
snapshot.center = center
snapshot.alpha = 0.0
self.tableView.addSubview(snapshot)
UIView.animate(withDuration: 0.25, animations:{
center.y = location.y
snapshot.center = center
snapshot.transform = CGAffineTransform(scaleX: 1.05, y: 1.05)
snapshot.alpha = 0.98
cell.alpha = 0.0
},completion:{ _ in
cell.isHidden = true})
}
break
case UIGestureRecognizerState.changed:
// print("changed")
var center:CGPoint = snapshot.center
center.y = location.y
snapshot.center = center
if let test = indexPath {
let bool = indexPath!.elementsEqual(beginningIndexPath as IndexPath)
if !bool {
self.updatePriorities(draggedOverIndexPath: test as NSIndexPath)
}
}
default:
// print("finished")
let cell:UITableViewCell = self.tableView.cellForRow(at: sourceIndexPath as! IndexPath)!
cell.isHidden = false
cell.alpha = 0.0
UIView.animate(withDuration: 0.25, animations: {
snapshot.center = cell.center;
snapshot.transform = .identity
snapshot.alpha = 0.0;
cell.alpha = 1.0;
}, completion: { _ in
sourceIndexPath = nil
snapshot.removeFromSuperview()
snapshot = nil
})
}
}
func customSnapShotFrom(view:UIView) -> UIView {
let snapshot:UIView = view.snapshotView(afterScreenUpdates: true)!
snapshot.layer.masksToBounds = false;
snapshot.layer.cornerRadius = 0.0;
snapshot.layer.shadowOffset = CGSize(width: 2.0, height: 2.0);
snapshot.layer.shadowRadius = 4.0;
snapshot.layer.shadowOpacity = 1.0;
return snapshot;
}
func updatePriorities(draggedOverIndexPath:NSIndexPath) {
let firstItem: Person = fetchedResultsController.object(at: beginningIndexPath as IndexPath)
let secondItem: Person = fetchedResultsController.object(at: draggedOverIndexPath as IndexPath)
firstItem.priority = Int32(draggedOverIndexPath.row)
secondItem.priority = Int32(beginningIndexPath.row)
beginningIndexPath = draggedOverIndexPath
coreDataStack.saveContext()
}
This works,but I noticed that even when the gesture is removed, I'm still able to long press on the table cell and move it. It doesn't rearrange the table cell, but I would like to stop this from happening.
Here's some semi-psuedo code that shows what I would recommend. This is better, because because every time the viewWillAppear the GestureRecognizer is updated.
class CustomViewController: UIViewController {
var longPress: UILongPressGestureRecognizer
required init() {
super.init(nibName: nil, bundle: nil)
longPress = UILongPressGestureRecognizer(target: self, action: #selector(longPressDetected(_:)))
self.tableView.addGestureRecognizer(longPress)
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
longPress.isEnabled = (options?.alphaOrder == false)
}
func longPressDetected(_ sender: Any) {
...existing method...
}
}

Drag & Drop Works - Autoscroll Doesn't

I have a drag and drop function (code below) for my table that works great. Except, I can't drag a cell to a location not showing on screen / in view.
In other words, I haven't figured out how to make it autoscroll.
Is anyone able to look at my code and know where to add code that enables autoscrolling when the user drags the cell towards the top or bottom of the view?
func animateCellDrag (gestureRecognizer: UIGestureRecognizer, tableView: UITableView, whichList: String) {
let longPress = gestureRecognizer as! UILongPressGestureRecognizer
let state = longPress.state
var locationInView = longPress.locationInView(tableView)
var indexPath = tableView.indexPathForRowAtPoint(locationInView)
var count = 0
struct My {
static var cellSnapshot : UIView? = nil
}
struct Path {
static var initialIndexPath : NSIndexPath? = nil
}
if indexPath != nil {
//Steps to take a cell snapshot. Function to be called in switch statement
func snapshotOfCell(inputView: UIView) -> UIView {
UIGraphicsBeginImageContextWithOptions(inputView.bounds.size, false, 0.0)
inputView.layer.renderInContext(UIGraphicsGetCurrentContext()!)
let image = UIGraphicsGetImageFromCurrentImageContext() as UIImage
UIGraphicsEndImageContext()
let cellSnapshot : UIView = UIImageView(image: image)
cellSnapshot.layer.masksToBounds = false
cellSnapshot.layer.cornerRadius = 0.0
cellSnapshot.layer.shadowOffset = CGSizeMake(-5.0, 0.0)
cellSnapshot.layer.shadowRadius = 5.0
cellSnapshot.layer.shadowOpacity = 0.4
return cellSnapshot
}
switch state {
case UIGestureRecognizerState.Began:
//Calls above function to take snapshot of held cell, animate pop out
//Run when a long-press gesture begins on a cell
if indexPath != nil {
Path.initialIndexPath = indexPath
let cell = tableView.cellForRowAtIndexPath(indexPath!) as UITableViewCell!
My.cellSnapshot = snapshotOfCell(cell)
var center = cell.center
My.cellSnapshot!.center = center
My.cellSnapshot!.alpha = 0.0
tableView.addSubview(My.cellSnapshot!)
UIView.animateWithDuration(0.25, animations: { () -> Void in
center.y = locationInView.y
My.cellSnapshot!.center = center
My.cellSnapshot!.transform = CGAffineTransformMakeScale(1.05, 1.05)
My.cellSnapshot!.alpha = 0.98
cell.alpha = 0.0
}, completion: { (finished) -> Void in
if finished {
cell.hidden = true
}
})
}
case UIGestureRecognizerState.Changed:
if My.cellSnapshot != nil || indexPath != nil {
//Runs when the user "lets go" of the cell
//Sets CG Y-Coordinate of snapshot cell to center of current location in table (snaps into place)
var center = My.cellSnapshot!.center
center.y = locationInView.y
My.cellSnapshot!.center = center
let appDel: AppDelegate = (UIApplication.sharedApplication().delegate as! AppDelegate)
let context: NSManagedObjectContext = appDel.managedObjectContext!
let fetchRequest = NSFetchRequest(entityName: whichList)
let sortDescriptor = NSSortDescriptor(key: "displayOrder", ascending: true )
fetchRequest.sortDescriptors = [ sortDescriptor ]
//If the indexPath is not 0 AND is not the same as it began (didn't move): Update array and table row order
if ((indexPath != nil) && (indexPath != Path.initialIndexPath) && (indexPath?.row < taskList_Cntxt.count)) {
switch whichList {
case "TodayTask", "TomTask", "TBDTask", "FinTask":
swap(&taskList_Cntxt[indexPath!.row], &taskList_Cntxt[Path.initialIndexPath!.row])
tableView.moveRowAtIndexPath(Path.initialIndexPath!, toIndexPath: indexPath!)
toolBox.updateDisplayOrder()
default:
swap(&curLifeList_Context[indexPath!.row], &curLifeList_Context[Path.initialIndexPath!.row])
tableView.moveRowAtIndexPath(Path.initialIndexPath!, toIndexPath: indexPath!)
toolBox.llvc_updateDisplayOrder()
}
do {
try context.save()
} catch _ { print("Drag and drop error. State: Changed")}
Path.initialIndexPath = indexPath
}
}
default:
if My.cellSnapshot != nil || indexPath != nil {
//Runs continuously while a long press is recognized / Animates cell movement
let cell = tableView.cellForRowAtIndexPath(Path.initialIndexPath!) as UITableViewCell!
cell.hidden = false
cell.alpha = 0.0
UIView.animateWithDuration(0.25, animations: { () -> Void in
My.cellSnapshot!.center = cell.center
My.cellSnapshot!.transform = CGAffineTransformIdentity
My.cellSnapshot!.alpha = 0.0
cell.alpha = 1.0
}, completion: { (finished) -> Void in
if finished {
//Completion block removes snapshot of cell, cleans everything up
Path.initialIndexPath = nil
My.cellSnapshot!.removeFromSuperview()
My.cellSnapshot = nil
}
})//End of competion block & end of animation
}//End of 'if nil'
}//End of switch
}//End of longPress if-nil
}//End of longPressGestureRecognized
I was able to resolve it using this code:
if locationOnScreen.y < 200 && indexPath!.row > 1 {
let indexPath = NSIndexPath(forRow: indexPath!.row-1, inSection: 0)
tableView.scrollToRowAtIndexPath(indexPath, atScrollPosition: UITableViewScrollPosition.Top, animated: false)
} else if locationOnScreen.y > 550 && indexPath!.row < listCount {
let indexPath = NSIndexPath(forRow: indexPath!.row+1, inSection: 0)
tableView.scrollToRowAtIndexPath(indexPath, atScrollPosition: UITableViewScrollPosition.Bottom, animated: false)
}
Where:
var locationOnScreen = longPress.locationInView(tableView.superview)
listCount = listArrayOrContext.count
Inserted in the switch case:
case UIGestureRecognizerState.Changed:
I set animate to false because otherwise I got very jerky, messy scrolling. If anyone figures out how to get a slow, gradual scroll, please post it.

Drag and Drop cells with UILongPressGestureRecognizer in UITableView

I am working on an app with a tableView in a ViewController. In this tableView I have sections and rows and I want my user to be able to long press, drag and drop cells in between sections with UILongPressGestureRecognizer.
With the following code my gesture recognizer works but at the moment that I try to drag the cell and drop it anywhere in the tableView, my app crashes. The crashes occur in two different lines. When the cell is long pressed and drag inside a section it crashes on this line: tableView.moveRowAtIndexPath(Path.initialIndexPath!, toIndexPath: indexPath!)
if I try to drag out of a section it crashes on this line: swap(&Categories.categories[indexPath!.row], &Categories.categories[Path.initialIndexPath!.row])
(the arrays with the data for each section and rows are in a separate class, named Categories. The array with sections is called "sections", and the one with rows is named "categories.")
I would appreciate any help with finding out why it fails and how can I fix it.
func setupLongPress() {
let longpress = UILongPressGestureRecognizer(target: self, action: "longPressGestureRecognized:")
tableView.addGestureRecognizer(longpress)
}
func longPressGestureRecognized(gestureRecognizer: UIGestureRecognizer) {
let longPress = gestureRecognizer as! UILongPressGestureRecognizer
let state = longPress.state
let locationInView = longPress.locationInView(tableView)
let indexPath = tableView.indexPathForRowAtPoint(locationInView)
struct My {
static var cellSnapshot : UIView? = nil
}
struct Path {
static var initialIndexPath : NSIndexPath? = nil
}
switch state {
case UIGestureRecognizerState.Began:
if indexPath != nil {
Path.initialIndexPath = indexPath
let cell = tableView.cellForRowAtIndexPath(indexPath!) as UITableViewCell!
My.cellSnapshot = snapshopOfCell(cell)
var center = cell.center
My.cellSnapshot!.center = center
My.cellSnapshot!.alpha = 0.0
tableView.addSubview(My.cellSnapshot!)
UIView.animateWithDuration(0.25, animations: { () -> Void in
center.y = locationInView.y
My.cellSnapshot!.center = center
My.cellSnapshot!.transform = CGAffineTransformMakeScale(1.05, 1.05)
My.cellSnapshot!.alpha = 0.98
cell.alpha = 0.0
}, completion: { (finished) -> Void in
if finished {
cell.hidden = true
}
})
}
case UIGestureRecognizerState.Changed:
var center = My.cellSnapshot!.center
center.y = locationInView.y
My.cellSnapshot!.center = center
if ((indexPath != nil) && (indexPath != Path.initialIndexPath)) {
swap(&Categories.categories[indexPath!.row], &Categories.categories[Path.initialIndexPath!.row])
tableView.moveRowAtIndexPath(Path.initialIndexPath!, toIndexPath: indexPath!)
Path.initialIndexPath = indexPath
}
default:
let cell = tableView.cellForRowAtIndexPath(Path.initialIndexPath!) as UITableViewCell!
cell.hidden = false
cell.alpha = 0.0
UIView.animateWithDuration(0.5, animations: { () -> Void in
My.cellSnapshot!.center = cell.center
My.cellSnapshot!.transform = CGAffineTransformIdentity
My.cellSnapshot!.alpha = 0.0
cell.alpha = 1.0
}, completion: { (finished) -> Void in
if finished {
Path.initialIndexPath = nil
My.cellSnapshot!.removeFromSuperview()
My.cellSnapshot = nil
}
})
}
}
func snapshopOfCell(inputView: UIView) -> UIView {
UIGraphicsBeginImageContextWithOptions(inputView.bounds.size, false, 0.0)
inputView.layer.renderInContext(UIGraphicsGetCurrentContext()!)
let image = UIGraphicsGetImageFromCurrentImageContext() as UIImage
UIGraphicsEndImageContext()
let cellSnapshot : UIView = UIImageView(image: image)
cellSnapshot.layer.masksToBounds = false
cellSnapshot.layer.cornerRadius = 0.0
cellSnapshot.layer.shadowOffset = CGSizeMake(-5.0, 0.0)
cellSnapshot.layer.shadowRadius = 1.0
cellSnapshot.layer.shadowOpacity = 0.2
return cellSnapshot
}
Thank you!

Resources