Swipe-able Table View Cell in iOS 9
Try this, updated for Swift 3 (Developer Docs)
override func tableView(_ tableView: UITableView, editActionsForRowAt: IndexPath) -> [UITableViewRowAction]? {
let more = UITableViewRowAction(style: .normal, title: "More") { action, index in
print("more button tapped")
}
more.backgroundColor = .lightGray
let favorite = UITableViewRowAction(style: .normal, title: "Favorite") { action, index in
print("favorite button tapped")
}
favorite.backgroundColor = .orange
let share = UITableViewRowAction(style: .normal, title: "Share") { action, index in
print("share button tapped")
}
share.backgroundColor = .blue
return [share, favorite, more]
}
Also implement this: (You can make it conditional, but here everything is editable)
override func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
return true
}
Swiping right on a UITableViewCell
No, there Is no such native API as of now.
How to add a Swipe Left gesture to a TableView and return the cell location when swiped in Swift 3
I've done some research and created a bare-bones example of how to this - create a table cell that can be swiped AND tapped. I'm using it in a music player - tap a cell and play the song, swipe the same cell and segue to a different view.
I've built my solution based on these two existing samples:
https://www.raywenderlich.com/77974/making-a-gesture-driven-to-do-list-app-like-clear-in-swift-part-1
https://gabrielghe.github.io/swift/2016/03/20/swipable-uitableviewcell
I don't have a repository to share so all the code is here.
I'm using Xcode 8.2.1 and Swift 3
STEP 1:
- Create a New, Single-View Swift project, and open the Storyboard.
- Drag a TableView onto the existing ViewController and drag it to fit the view.
STEP 2:
- Add new Swift File to the Project and name it "TableViewCellSliding.swift".
Copy/paste the code below into the new file.
//
// TableViewCellSliding.swift
//
import UIKit
protocol SlidingCellDelegate {
// tell the TableView that a swipe happened
func hasPerformedSwipe(touch: CGPoint)
func hasPerformedTap(touch: CGPoint)
}
class SlidingTableViewCell: UITableViewCell {
var delegate: SlidingCellDelegate?
var originalCenter = CGPoint()
var isSwipeSuccessful = false
var touch = CGPoint()
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
// add a PAN gesture
let pRecognizer = UIPanGestureRecognizer(target: self, action: #selector(SlidingTableViewCell.handlePan(_:)))
pRecognizer.delegate = self
addGestureRecognizer(pRecognizer)
// add a TAP gesture
// note that adding the PAN gesture to a cell disables the built-in tap responder (didSelectRowAtIndexPath)
// so we can add in our own here if we want both swipe and tap actions
let tRecognizer = UITapGestureRecognizer(target: self, action: #selector(SlidingTableViewCell.handleTap(_:)))
tRecognizer.delegate = self
addGestureRecognizer(tRecognizer)
}
override func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
if let panGestureRecognizer = gestureRecognizer as? UIPanGestureRecognizer {
let translation = panGestureRecognizer.translation(in: superview!)
//look for right-swipe
if (fabs(translation.x) > fabs(translation.y)) && (translation.x > 0){
// look for left-swipe
//if (fabs(translation.x) > fabs(translation.y)) && (translation.x < 0){
//print("gesture 1")
touch = panGestureRecognizer.location(in: superview)
return true
}
//not left or right - must be up or down
return false
}else if gestureRecognizer is UITapGestureRecognizer {
touch = gestureRecognizer.location(in: superview)
return true
}
return false
}
func handleTap(_ recognizer: UITapGestureRecognizer){
// call function to get indexPath since didSelectRowAtIndexPath will be disabled
delegate?.hasPerformedTap(touch: touch)
}
func handlePan(_ recognizer: UIPanGestureRecognizer) {
if recognizer.state == .began {
originalCenter = center
}
if recognizer.state == .changed {
checkIfSwiped(recongizer: recognizer)
}
if recognizer.state == .ended {
let originalFrame = CGRect(x: 0, y: frame.origin.y, width: bounds.size.width, height: bounds.size.height)
if isSwipeSuccessful{
delegate?.hasPerformedSwipe(touch: touch)
//after 'short' swipe animate back to origin quickly
moveViewBackIntoPlaceSlowly(originalFrame: originalFrame)
} else {
//after successful swipe animate back to origin slowly
moveViewBackIntoPlace(originalFrame: originalFrame)
}
}
}
func checkIfSwiped(recongizer: UIPanGestureRecognizer) {
let translation = recongizer.translation(in: self)
center = CGPoint(x: originalCenter.x + translation.x, y: originalCenter.y)
//this allows only swipe-right
isSwipeSuccessful = frame.origin.x > frame.size.width / 2.0 //pan is 1/2 width of the cell
//this allows only swipe-left
//isSwipeSuccessful = frame.origin.x < -frame.size.width / 3.0 //pan is 1/3 width of the cell
}
func moveViewBackIntoPlace(originalFrame: CGRect) {
UIView.animate(withDuration: 0.2, animations: {self.frame = originalFrame})
}
func moveViewBackIntoPlaceSlowly(originalFrame: CGRect) {
UIView.animate(withDuration: 1.5, animations: {self.frame = originalFrame})
}
}
STEP 3:
Copy/paste the code below into the existing file "ViewController.swift"
//
// ViewController.swift
//
import UIKit
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource, SlidingCellDelegate {
@IBOutlet weak var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
tableView.dataSource = self
tableView.delegate = self
tableView.register(SlidingTableViewCell.self, forCellReuseIdentifier: "cell")
tableView.rowHeight = 50;
}
func hasPerformedSwipe(touch: CGPoint) {
if let indexPath = tableView.indexPathForRow(at: touch) {
// Access the image or the cell at this index path
print("got a swipe row:\(indexPath.row)")
}
}
func hasPerformedTap(touch: CGPoint){
if let indexPath = tableView.indexPathForRow(at: touch) {
// Access the image or the cell at this index path
print("got a tap row:\(indexPath.row)")
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView,numberOfRowsInSection section: Int)-> Int {
return 100
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell=tableView.dequeueReusableCell(withIdentifier: "cell",for: indexPath) as! SlidingTableViewCell
// Configure cell
cell.selectionStyle = .none
cell.textLabel?.text = "hello \(indexPath.row)"
cell.delegate = self
return cell
}
func tableView(sender: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
// do stuff with indexPath.row and indexPath.section
//never make it here because we added a tap gesture but this will
print("selected cell")
}
}
STEP 4:
Connect it all in the Storyboard.
Open the Storyboard view and select the TableView. Go to the Connections Inspector (upper-right corner arrow in a circle) and drag from New Referencing Outlet to the TableView and select "tableView" from the popup menu.
With the TableView still selected, drag from Outlets > dataSource to the TableView in the Storyboard. Repeat starting with Outlets > delegate.
STEP 5:
- Run it!
I'm not going into details about any of the code as the two links at the top do that very well. This is just about having complete, simple, clean code that you can build on. Enjoy.
Swipe Table Cells overlapping cell content on swipe motion
I actually resolved this issue - i needed to add the labels to the contentView, not the actual view.
so the code should have been
self.contentView.addSubview(name)
for example, on the custom tableviewcell
How to enable swipe to delete cell in a TableView?
You don't have to set editing:YES
if you need to show Delete button on cell swipe. You have to implement tableView:canEditRowAtIndexPath:
and return YES from there for rows you need to edit/delete. This is not necessary when your tableView's dataSource is a subclass of UITableViewContoller - this method, if not overridden, returns YES by default. In all other cases you have to implement it.
EDIT: Together we have found the problem - tableView:editingStyleForRowAtIndexPath:
returned UITableViewCellEditingStyleNone
if table wasn't in editing mode.
How to edit tableview cell on TAP instead of swipe - Swift
I don't think you're going to be able to do it, sorry. This feature relies on the UITableView's pan gesture recognizer. When the user pans sideways, the table view checks the editActions...
delegate method and, if we have edit actions, inserts a private confirmation view behind the cell and allows the cell to be moved to reveal it. You can't trigger that behavior programmatically as far as I can tell.
This would be a reason — one of many very good reasons — for sticking with a third-party scrollable cell class. In this way, the cell is a horizontal scroll view and you just scroll it in code.
UITableViewCell, show delete button on swipe
During startup in (-viewDidLoad or in storyboard)
do:
self.tableView.allowsMultipleSelectionDuringEditing = false
Override to support conditional editing of the table view. This only needs to be implemented if you are going to be returning NO
for some items. By default, all items are editable.
- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath {
// Return YES if you want the specified item to be editable.
return YES;
}
// Override to support editing the table view.
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
if (editingStyle == UITableViewCellEditingStyleDelete) {
//add code here for when you hit delete
}
}
Related Topics
How to Use Instance Method as Callback For Function Which Takes Only Func or Literal Closure
Wait Until Swift For Loop With Asynchronous Network Requests Finishes Executing
Trying to Understand Asynchronous Operation Subclass
How to Resolve: 'Keywindow' Was Deprecated in iOS 13.0
Alternative to Performselector in Swift
Swiftui: How to Make Textfield Become First Responder
Waiting Until the Task Finishes
Passing an Array to a Function With Variable Number of Args in Swift
How to Enumerate an Enum With String Type
How to Dispatch_Sync, Dispatch_Async, Dispatch_After, etc in Swift 3, Swift 4, and Beyond
Instantiated Optional Variable Shows as Nil in Xcode Debugger
How to Create Usdz File Using Xcode Converter
Index of a Substring in a String With Swift
Is Swift Pass by Value or Pass by Reference