I have a table view and I use UITableViewRowAction to present two options to the user when a swipe is done on a row, 'delete' and 'more', and I want the more to show a popover. The popover needs to know the view it is anchored at, so how do I get a reference to the button which says 'more'?
My code:
func tableView(tableView: UITableView, editActionsForRowAtIndexPath indexPath: NSIndexPath) -> [AnyObject]? {
var moreRowAction = UITableViewRowAction(style: UITableViewRowActionStyle.Default, title: "More", handler:{action, indexpath in
let p = SomeViewController(aClient: client)
let aPopover = UIPopoverController(contentViewController: p)
let popRect = self.view.frame
// What should x be?
let x: UIView = self.tableView.cellForRowAtIndexPath(indexPath)!
aPopover.presentPopoverFromRect(popRect, inView: x, permittedArrowDirections: UIPopoverArrowDirection.Any, animated: true)
});
var deleteRowAction = UITableViewRowAction(style: UITableViewRowActionStyle.Default, title: "Delete", handler:{action, indexpath in
// Do something to delete.
});
return [deleteRowAction, moreRowAction];
}
This code shows the popover as anchored at the whole cell, not at the 'more' button.
I have searched the documentation about UITableViewRowAction, but nothing there gave me a lead. The type of the action argument is also UITableViewRowAction so I'm not sure it can be used.
Update
According to the comment, I got it working. I call a method in the custom cell view controller, there I access the subviews array. I don't like accessing subviews array and assuming the position of the button in that array.
The working code:
In the tableview callback I do:
func tableView(tableView: UITableView, editActionsForRowAtIndexPath indexPath: NSIndexPath) -> [AnyObject]? {
var moreRowAction = UITableViewRowAction(style: UITableViewRowActionStyle.Default, title: "Plus", handler:{action, indexpath in
let cell = self.tableView.cellForRowAtIndexPath(indexPath)! as MyCell
cell.showPopover()
});
Inside MyCell I do:
func showPopover() {
let p = ClientLongPressViewController(client: self.client)
let aPopover = UIPopoverController(contentViewController: p)
let x: UIView = self.subviews[0].subviews[1] as? UIView ?? self
let popRect = x.frame
aPopover.setPopoverContentSize(CGSize(width: 215, height: 241), animated: true)
aPopover.presentPopoverFromRect(popRect, inView: x, permittedArrowDirections: UIPopoverArrowDirection.Any, animated: true)
}
I tried (inside MyCell) to access self.contentView but this only contains elements from the main cell view, not the swiping elements.
Also tried using self.editingAccessoryView, but it is nil.
So I would still like a nicer way to get the "delete confirmation view", which I now access with self.subviews[0] (The second subview[1] is okay in my opinion because I decide it's position when I return [deleteRowAction, moreRowAction].
There doesn't seem to be any straight forward way access the button itself, but after you swipe the cell, I believe that what happens, is that the cell is moved to the left, and a new view called the UITableViewCellDeleteConfirmationView is added on the right. So, if you position the popover at the cell's origin plus its width, it will be at the right edge of the cell, which will be just to the left of your button (assuming that your button is the left most one if you have multiple buttons).
var moreRowAction = UITableViewRowAction(style: UITableViewRowActionStyle.Default, title: "More", handler:{action, indexpath in
let p = SomeViewController(aClient: client)
let aPopover = UIPopoverController(contentViewController: p)
let cell: UITableViewCell = self.tableView.cellForRowAtIndexPath(indexPath)!
let popRect = CGRectMake(cell.frame.origin.x + cell.frame.size.width, cell.frame.size.height/2.0, 1, 1);
aPopover.presentPopoverFromRect(popRect, inView: cell, permittedArrowDirections: .Right, animated: true)
});
Related
For my CollectionView I have this animation inside willDisplay :
func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
// Add animations here
let animation = AnimationFactory.makeMoveUpWithFade(rowHeight: cell.frame.height, duration: 0.5, delayFactor: 0.1)
let animator = Animator(animation: animation)
animator.animate(cell: cell, at: indexPath, in: collectionView)
}
This is how the animation works (I implemented it for CollectionView) if you need it for more info.
Probelm:
Inside my project the user can create and delete an item.
Right now the collectionView is not animating after deleting even though I am calling reloadData:
extension MainViewController: DismissWishlistDelegate {
func dismissWishlistVC(dataArray: [Wishlist], dropDownArray: [DropDownOption]) {
self.dataSourceArray = dataArray
self.dropOptions = dropDownArray
self.makeWishView.dropDownButton.dropView.tableView.reloadData()
// reload the collection view
theCollectionView.reloadData()
theCollectionView.performBatchUpdates(nil, completion: nil)
}
}
This is where I call the delegate inside my other ViewController:
func deleteTapped(){
let alertcontroller = UIAlertController(title: "Wishlist löschen", message: "Sicher, dass du diese Wishlist löschen möchtest?", preferredStyle: .alert)
let deleteAction = UIAlertAction(title: "Löschen", style: .default) { (alert) in
DataHandler.deleteWishlist(self.wishList.index)
self.dataSourceArray.remove(at: self.currentWishListIDX)
self.dropOptions.remove(at: self.currentWishListIDX)
// change heroID so wishlist image doesnt animate
self.wishlistImage.heroID = "delete"
self.dismiss(animated: true, completion: nil)
// update datasource array in MainVC
self.dismissWishlistDelegate?.dismissWishlistVC(dataArray: self.dataSourceArray, dropDownArray: self.dropOptions)
}
let cancelAction = UIAlertAction(title: "Abbrechen", style: .default) { (alert) in
print("abbrechen")
}
alertcontroller.addAction(cancelAction)
alertcontroller.addAction(deleteAction)
self.present(alertcontroller, animated: true)
}
When creating the animation works just fine. This is how my createDelegateFunction looks like this:
func createListTappedDelegate(listImage: UIImage, listImageIndex: Int, listName: String) {
// append created list to data source array
var textColor = UIColor.white
if Constants.Wishlist.darkTextColorIndexes.contains(listImageIndex) {
textColor = UIColor.darkGray
}
let newIndex = self.dataSourceArray.last!.index + 1
self.dataSourceArray.append(Wishlist(name: listName, image: listImage, wishData: [Wish](), color: Constants.Wishlist.customColors[listImageIndex], textColor: textColor, index: newIndex))
// append created list to drop down options
self.dropOptions.append(DropDownOption(name: listName, image: listImage))
// reload the collection view
theCollectionView.reloadData()
theCollectionView.performBatchUpdates(nil, completion: {
(result) in
// scroll to make newly added row visible (if needed)
let i = self.theCollectionView.numberOfItems(inSection: 0) - 1
let idx = IndexPath(item: i, section: 0)
self.theCollectionView.scrollToItem(at: idx, at: .bottom, animated: true)
})
}
Animation of IUCollectionView elements insertion and deletion is correctly done using finalLayoutAttributesForDisappearingItem(at:) and initialLayoutAttributesForAppearingItem(at:)
Little excerpt from Apple's documentation on finalLayoutAttributesForDisappearingItem(at:)
This method is called after the prepare(forCollectionViewUpdates:) method and before the finalizeCollectionViewUpdates() method for any items that are about to be deleted. Your implementation should return the layout information that describes the final position and state of the item. The collection view uses this information as the end point for any animations. (The starting point of the animation is the item’s current location.) If you return nil, the layout object uses the same attributes for both the start and end points of the animation.
I solved the problem by removing performBatchUpdates ... I have no idea why it works now and what exactly performBatchUpdates does but it works so if anyone wants to explain it to me, feel free :D
The final function looks like this:
func dismissWishlistVC(dataArray: [Wishlist], dropDownArray: [DropDownOption], shouldDeleteWithAnimation: Bool, indexToDelete: Int) {
if shouldDeleteWithAnimation {
self.shouldAnimateCells = true
self.dataSourceArray.remove(at: self.currentWishListIDX)
self.dropOptions.remove(at: self.currentWishListIDX)
// reload the collection view
theCollectionView.reloadData()
} else {
self.shouldAnimateCells = false
self.dataSourceArray = dataArray
self.dropOptions = dropDownArray
// reload the collection view
theCollectionView.reloadData()
theCollectionView.performBatchUpdates(nil, completion: nil)
}
self.makeWishView.dropDownButton.dropView.tableView.reloadData()
}
When a certain Table View Controller displays for the first time, how to briefly show that the red “swipe to delete” functionality exists in a table row?
The goal of programmatically playing peekaboo with this is to show the user that the functionality exists.
Environment: iOS 11+ and iPhone app.
Here's an image showing the cell slid partway with a basic "swipe to delete" red action button.
A fellow developer kindly mentioned SwipeCellKit, but there’s a lot to SwipeCellKit. All we want to do is briefly simulate a partial swipe to let the user know the "swipe to delete" exists. In other words, we want to provide a sneak peak at the delete action under the cell.
In case it helps, here's the link to the SwipeCellKit's showSwipe code Here is a link with an example of its use.
I looked at the SwipeCellKit source code. It's not clear to me how to do it without SwipeCellKit. Also, Using SwipeCellKit is not currently an option.
Googling hasn't helped. I keep running into how to add swipe actions, but not how to briefly show the Swipe Actions aka UITableViewRowAction items that are under the cell to the user.
How to briefly show this built in "swipe to delete" action to the user?
I've written this simple extension for UITableView. It searches through visible cells, finds first row that contains edit actions and then "slides" that cell for a bit to reveal action underneath. You can adjust parameters like width and duration of the hint.
It works great because it doesn't hardcode any values, so for example the hint's background color will always match the real action button.
import UIKit
extension UITableView {
/**
Shows a hint to the user indicating that cell can be swiped left.
- Parameters:
- width: Width of hint.
- duration: Duration of animation (in seconds)
*/
func presentTrailingSwipeHint(width: CGFloat = 20, duration: TimeInterval = 0.8) {
var actionPath: IndexPath?
var actionColor: UIColor?
guard let visibleIndexPaths = indexPathsForVisibleRows else {
return
}
if #available(iOS 13.0, *) {
// Use new API, UIContextualAction
for path in visibleIndexPaths {
if let config = delegate?.tableView?(self, trailingSwipeActionsConfigurationForRowAt: path), let action = config.actions.first {
actionPath = path
actionColor = action.backgroundColor
break
}
}
} else {
for path in visibleIndexPaths {
if let actions = delegate?.tableView?(self, editActionsForRowAt: path), let action = actions.first {
actionPath = path
actionColor = action.backgroundColor
break
}
}
}
guard let path = actionPath, let cell = cellForRow(at: path) else { return }
cell.presentTrailingSwipeHint(actionColor: actionColor ?? tintColor)
}
}
fileprivate extension UITableViewCell {
func presentTrailingSwipeHint(actionColor: UIColor, hintWidth: CGFloat = 20, hintDuration: TimeInterval = 0.8) {
// Create fake action view
let dummyView = UIView()
dummyView.backgroundColor = actionColor
dummyView.translatesAutoresizingMaskIntoConstraints = false
addSubview(dummyView)
// Set constraints
NSLayoutConstraint.activate([
dummyView.topAnchor.constraint(equalTo: topAnchor),
dummyView.leadingAnchor.constraint(equalTo: trailingAnchor),
dummyView.bottomAnchor.constraint(equalTo: bottomAnchor),
dummyView.widthAnchor.constraint(equalToConstant: hintWidth)
])
// This animator reverses back the transform.
let secondAnimator = UIViewPropertyAnimator(duration: hintDuration / 2, curve: .easeOut) {
self.transform = .identity
}
// Don't forget to remove the useless view.
secondAnimator.addCompletion { position in
dummyView.removeFromSuperview()
}
// We're moving the cell and since dummyView
// is pinned to cell's trailing anchor
// it will move as well.
let transform = CGAffineTransform(translationX: -hintWidth, y: 0)
let firstAnimator = UIViewPropertyAnimator(duration: hintDuration / 2, curve: .easeIn) {
self.transform = transform
}
firstAnimator.addCompletion { position in
secondAnimator.startAnimation()
}
// Do the magic.
firstAnimator.startAnimation()
}
}
Example usage:
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
tableView.presentSwipeHint()
}
I'm pretty sure this can't be done. The swipe actions are contained in a UISwipeActionPullView, which contains UISwipeStandardAction subviews, both of which are private. They are also part of the table view, not the cell, and they're not added unless a gesture is happening, so you can't just bump the cell to one side and see them there.
Outside of UI automation tests, it isn't possible to simulate user gestures without using private API, so you can't "fake" a swipe and then show the results to the user.
However, why bother doing it "properly" when you can cheat? It shouldn't be too hard to bounce the cell's content view slightly to the left, and bounce in a red box (not so far that you can see text, to avoid localisation issues), then return to normal. Do this on the first load of the table view, and stop doing it after N times or after the user has proven that they know how this particular iOS convention works.
First : enable the editing
func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
return true
}
Then customise your swipe
func tableView(_ tableView: UITableView, editActionsForRowAt indexPath: IndexPath) -> [UITableViewRowAction]? {
let conformeRowAction = UITableViewRowAction(style: UITableViewRowActionStyle.default, title: NSLocalizedString("C", comment: ""), handler:{action, indexpath in
print("C•ACTION")
})
conformeRowAction.backgroundColor = UIColor(red: 154.0/255, green: 202.0/255, blue: 136.0/255, alpha: 1.0)
let notConform = UITableViewRowAction(style: UITableViewRowActionStyle.default, title: NSLocalizedString("NC", comment: ""), handler:{action, indexpath in
print("NC•ACTION")
})
notConform.backgroundColor = UIColor(red: 252.0/255, green: 108.0/255, blue: 107.0/255, alpha: 1.0)
return [conformeRowAction,notConform]
}
I really have no idea how to call this feature, music app had it up to 8.4, it looks like on the screenshot. I want to implement it in my app so when user presses the cell the "bubble" with 2 option buttons shows up.
I am interested in how to make it happen in Obj-C but I'm sure people will apreciate the answer written in Swift. Thanks
Screenshot
I'm doing exactly the same thing as what you want.
To do so, you have to create your own view controller, not UIMenuItem. The screen capture is as follows.
What I'm doing is to create a viewController as popup (PopoverMenuController), and adding a tableView as a subView for menu.
In the same way, you can add whatever UI controls you want instead of tableView.
Here is how you use my PopoverMenuController.
var myPopupMenu: PopoverMenuController!
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
// make cell fully visible
let tableCell = tableView.cellForRow(at: indexPath)!
tableView.scrollRectToVisible(tableView.rectForRow(at: indexPath), animated: false)
// pop up menu
myPopupMenu = PopoverMenuController()
myPopupMenu.sourceView = tableCell
var rect = tableCell.bounds
// I'm adjusting popup menu position slightly (this is optional)
rect.origin.y = rect.size.height/3.0
rect.size.height = rect.size.height / 2.0
myPopupMenu.sourceRect = rect
myPopupMenu.addAction(PopMenuAction(textLabel: "MyMenu-1", accessoryType: .none, handler: myMenuHandler1))
myPopupMenu.addAction(PopMenuAction(textLabel: "MyMenu-2", accessoryType: .none, handler: myMenuHandler2))
present(myPopupMenu, animated: true, completion: nil)
}
func myMenuHandler1(_ action: PopMenuAction?) -> Bool {
// do some work...
return false
}
func myMenuHandler2(_ action: PopMenuAction?) -> Bool {
// do some work...
return false
}
To transltae to Obj-C should not be a big effort.
I put souce code of PopoverMenuController.swift here. This controller is self-contained and it has a description on how to use.
Hope this helps.
Look up how to implement UIMenuController on a UITableViewCell
How to show a custom UIMenuItem for a UITableViewCell
I am programmatically creating cells and adding a delete button to each one of them. The problem is that I'd like to toggle their .hidden state. The idea is to have an edit button that toggles all of the button's state at the same time. Maybe I am going about this the wrong way?
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier("verticalCell", forIndexPath: indexPath) as! RACollectionViewCell
let slide = panelSlides[indexPath.row]
cell.slideData = slide
cell.slideImageView.setImageWithUrl(NSURL(string: IMAGE_URL + slide.imageName + ".jpg")!)
cell.setNeedsLayout()
let image = UIImage(named: "ic_close") as UIImage?
var deleteButton = UIButton(type: UIButtonType.Custom) as UIButton
deleteButton.frame = CGRectMake(-25, -25, 100, 100)
deleteButton.setImage(image, forState: .Normal)
deleteButton.addTarget(self,action:#selector(deleteCell), forControlEvents:.TouchUpInside)
deleteButton.hidden = editOn
cell.addSubview(deleteButton)
return cell
}
#IBAction func EditButtonTap(sender: AnyObject) {
editOn = !editOn
sidePanelCollectionView.reloadData()
}
I think what you want to do is iterate over all of your data by index and then call cellForItemAtIndexPath: on your UICollectionView for each index. Then you can take that existing cell, cast it to your specific type as? RACollectionViewCell an then set the button hidden values this way.
Example (apologies i'm not in xcode to verify this precisely right now but this is the gist):
for (index, data) in myDataArray.enumerated() {
let cell = collectionView.cellForRowAtIndexPath(NSIndexPath(row: index, section: 0)) as? RACollectionViewCell
cell?.deleteButton.hidden = false
}
You probably also need some sort of isEditing Boolean variable in your view controller that keeps track of the fact that you are in an editing state so that as you scroll, newly configured cells continue to display with/without the button. You are going to need your existing code above as well to make sure it continues to work as scrolling occurs. Instead of creating a new delete button every time, you should put the button in your storyboard and set up a reference too and then you can just use something like cell.deleteButton.hidden = !isEditing
I have one tableView in my storyBoard where I added 4 static cell into it and my storyBoard look like:
I don't have any dataSource for this tableView because my cells are static.
And I use below code to drag a cell and it is working fine till I scroll a table.
import UIKit
class TableViewController: UITableViewController {
var sourceIndexPath: NSIndexPath = NSIndexPath()
var snapshot: UIView = UIView()
let longPress: UILongPressGestureRecognizer = {
let recognizer = UILongPressGestureRecognizer()
return recognizer
}()
override func viewDidLoad() {
super.viewDidLoad()
longPress.addTarget(self, action: "longPressGestureRecognized:")
self.tableView.addGestureRecognizer(longPress)
self.tableView.allowsSelection = false
}
override func viewWillAppear(animated: Bool) {
self.tableView.reloadData()
}
// MARK: UIGestureRecognizer
func longPressGestureRecognized(gesture: UILongPressGestureRecognizer){
let state: UIGestureRecognizerState = gesture.state
let location:CGPoint = gesture.locationInView(self.tableView)
if let indexPath: NSIndexPath = self.tableView.indexPathForRowAtPoint(location){
switch(state){
case UIGestureRecognizerState.Began:
sourceIndexPath = indexPath
let cell: UITableViewCell = self.tableView .cellForRowAtIndexPath(indexPath)!
//take a snapshot of the selected row using helper method
snapshot = customSnapshotFromView(cell)
//add snapshot as subview, centered at cell's center
var center: CGPoint = cell.center
snapshot.center = center
snapshot.alpha = 0.0
self.tableView.addSubview(snapshot)
UIView.animateWithDuration(0.25, animations: { () -> Void in
center.y = location.y
self.snapshot.center = center
self.snapshot.transform = CGAffineTransformMakeScale(1.05, 1.05)
self.snapshot.alpha = 0.98
cell.alpha = 0.0
}, completion: { (finished) in
cell.hidden = true
})
case UIGestureRecognizerState.Changed:
let cell: UITableViewCell = self.tableView.cellForRowAtIndexPath(indexPath)!
var center: CGPoint = snapshot.center
center.y = location.y
snapshot.center = center
print("location \(location.y)")
//is destination valid and is it different form source?
if indexPath != sourceIndexPath{
//update data source
//I have commented this part because I am not using any dataSource.
// self.customArray.exchangeObjectAtIndex(indexPath.row, withObjectAtIndex: sourceIndexPath.row)
//move the row
self.tableView.moveRowAtIndexPath(sourceIndexPath, toIndexPath: indexPath)
//and update source so it is in sync with UI changes
sourceIndexPath = indexPath
}
if (location.y < 68) || (location.y > 450) {
print("cancelled")
self.snapshot.alpha = 0.0
cell.hidden = false
UIView.animateWithDuration(0.10, animations: { () -> Void in
self.snapshot.center = cell.center
self.snapshot.transform = CGAffineTransformIdentity
self.snapshot.alpha = 0.0
//undo fade out
cell.alpha = 1.0
}, completion: { (finished) in
self.snapshot.removeFromSuperview()
})
}
case UIGestureRecognizerState.Ended:
//clean up
print("ended")
let cell: UITableViewCell = tableView.cellForRowAtIndexPath(indexPath)!
cell.hidden = false
UIView.animateWithDuration(0.25, animations: { () -> Void in
self.snapshot.center = cell.center
self.snapshot.transform = CGAffineTransformIdentity
self.snapshot.alpha = 0.0
//undo fade out
cell.alpha = 1.0
}, completion: { (finished) in
self.snapshot.removeFromSuperview()
})
break
default:
break
}
}else{
gesture.cancelsTouchesInView = true
}
}
func customSnapshotFromView(inputView: UIView) -> UIView {
// Make an image from the input view.
UIGraphicsBeginImageContextWithOptions(inputView.bounds.size, false, 0)
inputView.layer.renderInContext(UIGraphicsGetCurrentContext()!)
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext();
// Create an image view.
let snapshot = UIImageView(image: image)
snapshot.layer.masksToBounds = false
snapshot.layer.cornerRadius = 0.0
snapshot.layer.shadowOffset = CGSize(width: -5.0, height: 0.0)
snapshot.layer.shadowRadius = 5.0
snapshot.layer.shadowOpacity = 0.4
return snapshot
}
}
When I scroll after dragging it looks like:
As you can see cell is not appearing again. I want to drag and drop static cell and I want to save it's position so I will not rearrange again when I scroll.
Sample project for more Info.
This is just a demo project But I have added many elements into my cell and every cell have different UI.
There is a library that does exactly what you are looking to do with a very similar approach. It's called FMMoveTableView but it's for cells with a datasource.
I think that what is causing your problem is that when you move the cells around and then you scroll the datasource from the storyboard is no longer in sync with the table and therefore your cell object can't be redrawn.
I think you should implement your table this way:
Make your 4 cells custom cells.
Subclass each one.
Create an Array with numbers 1 to 4
Reorder the array on long drag
Override cellForRowAtIndexPath to show the right cell for the right number
You can drag uitableview cell from uitableview delegates .......
1) set the table view editing style to none in its delegate.
2) implement table view delegate to enable dragging of cell i.e canMoveRowAtIndexPath methods...
You can create multiple dynamic cells.
You'll just have to dequeue cells with correct identifier.
Are you doing this for layout purposes only, maybe a UICollectionView or a custom made UIScrollView could do the job?
Never the less, I have a solution:
Create a IBOutlet collection holding all your static UITableViewCells
Create a index list to simulate a "data source"
Override the cellForRowAtIndexPath to draw using your own index list
When updating the list order, update the indexList so that the view "remembers" this change
This Table view controller explains it all:
class TableViewController: UITableViewController {
#IBOutlet var outletCells: [UITableViewCell]!
var indexList = [Int]()
override func viewDidLoad() {
super.viewDidLoad()
// Prepare a index list.
// We will move positions in this list instead
// of trying to move the view's postions.
for (index, _) in outletCells.enumerate() {
indexList.append(index)
}
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// Use dynamic count, not needed I guess but
// feels better this way.
return outletCells.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
// Use the index path to get the true index and return
// the view on that index in our IBOutlet collection
let realIndexForPos = indexList[indexPath.row]
return outletCells[realIndexForPos]
}
#IBAction func onTap(sender: AnyObject) {
// Simulating your drag n drop stuff here... :)
let swapThis = 1
let swapThat = 2
tableView.moveRowAtIndexPath(NSIndexPath(forItem: swapThis, inSection: 0), toIndexPath: NSIndexPath(forItem: swapThat, inSection: 0))
// Update the indexList as this is the "data source"
// Do no use moveRowAtIndexPath as this is never triggred
// This one line works: swap(&indexList[swapThis], &indexList[swapThat])
// But bellow is easier to understand
let tmpVal = indexList[swapThis]
indexList[swapThis] = indexList[swapThat]
indexList[swapThat] = tmpVal
}
}
To create the IBOutlet use the Interface Builder.
Use the Referencing Outlet Collection on each Table View Cell and drag, for each, to the same #IBOutlet in your controller code.