87

I want my table list to have a swipe-able menu like in iOS 8 (first introduced in iOS 7).

Screenshot of table view cell action buttons

I've found a Ray Wenderlich guide that is clear on how to do it, but it was written a year and 4 months ago and the code is in Objective-C.

Did iOS 8 or the upcoming iOS 9 finally include this function in Apple's SDK? I know they made the "swipe to reveal delete function" built-in years ago. I don't want to waste my time implementing patched-together code to mimic the iOS 8 mail function, if Apple's new iOS is going to hand it to me in a neatly wrapped package.

jscs
  • 63,694
  • 13
  • 151
  • 195
Dave G
  • 12,042
  • 7
  • 57
  • 83

7 Answers7

161

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
}
pkamb
  • 33,281
  • 23
  • 160
  • 191
jose920405
  • 7,982
  • 6
  • 45
  • 71
  • 11
    This doesn't answer the question. We are trying to figure a way out to the left swipe using swift This does not do that – Somu Oct 19 '15 at 21:30
  • Thanks! This obviously didn't handle the left-right swipe, but I've decided to abandon that function anyhow. The only thing that isn't clear is how to get the table to automatically refresh after pushing a button that might move/delete the cell from the table? – Dave G Jan 07 '16 at 18:57
  • 1
    Do not know if you mean `tableview.reloadRowsAtIndexPaths ([indexpath] withRowAnimation: UITableViewRowAnimation.Automatic)` and for delete `tableview.deleteRowsAtIndexPaths([indexpath], withRowAnimation: UITableViewRowAnimation.Automatic)` – jose920405 Jan 07 '16 at 19:02
  • 2
    Is it possible to open Edit Action by tapping the cell instead of swiping it? – Heysem Katibi Feb 24 '16 at 15:49
  • @MoHaKa With this logic I think not. I see only two options: 1) With the tap you can simulate the swipe for this to work (not recommended). 2) Display a customized pop up with the options you need when you click on the cell. – jose920405 Feb 24 '16 at 16:47
  • @jose920405 how to add image here.. i have tried to set image as backgroundcolor. no luck – Muruganandham K Sep 20 '16 at 07:02
  • @MuruganandhamK You have already tried this? ==> http://stackoverflow.com/questions/29335104/how-add-custom-image-to-uitableview-cell-swipe-to-delete#29335209 – jose920405 Sep 20 '16 at 13:10
  • Thank you so much. On iOS 9 it's not import to implement the commitEditingStyle but on iOS 8 it doesn't work without it. You saved me a lot of time. – user1007522 Oct 03 '16 at 07:02
  • Just wondering, is it possible to create a custom view? Cause what if I would like to have two different background colors for my custom action? – Happiehappie Oct 31 '16 at 09:36
  • Something like http://stackoverflow.com/questions/29421894/uitableviewrowaction-image-for-title#38655903 ? – jose920405 Oct 31 '16 at 13:56
  • 1
    Swift 3 function definition `override func tableView(_ tableView: UITableView, editActionsForRowAt indexPath: IndexPath) -> [UITableViewRowAction]?` – David Corbin Dec 24 '16 at 16:58
  • I have image but size is bigger than cell size. Then how to auto adjust image in cell action ? – Mayur Shinde Jul 19 '19 at 06:49
29

This code is work for me in the swift4.

enter image description here

Answer of the above screen is:-

 func tableView(_ tableView: UITableView,
                   trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration?
    {
        // Write action code for the trash
        let TrashAction = UIContextualAction(style: .normal, title:  "Trash", handler: { (ac:UIContextualAction, view:UIView, success:(Bool) -> Void) in
            print("Update action ...")
            success(true)
        })
        TrashAction.backgroundColor = .red

        // Write action code for the Flag
        let FlagAction = UIContextualAction(style: .normal, title:  "Flag", handler: { (ac:UIContextualAction, view:UIView, success:(Bool) -> Void) in
            print("Update action ...")
            success(true)
        })
        FlagAction.backgroundColor = .orange

        // Write action code for the More
        let MoreAction = UIContextualAction(style: .normal, title:  "More", handler: { (ac:UIContextualAction, view:UIView, success:(Bool) -> Void) in
            print("Update action ...")
            success(true)
        })
        MoreAction.backgroundColor = .gray


        return UISwipeActionsConfiguration(actions: [TrashAction,FlagAction,MoreAction])
    }

enter image description here

Answer of the above screen:-

 func tableView(_ tableView: UITableView,
                   leadingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration?
    {

        let closeAction = UIContextualAction(style: .normal, title:  "Mark as Read", handler: { (ac:UIContextualAction, view:UIView, success:(Bool) -> Void) in
            print("CloseAction ...")
            success(true)
        })
        closeAction.backgroundColor = .blue
        return UISwipeActionsConfiguration(actions: [closeAction])

    }

Write tableview Delegate method likewise:-

func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return arrPerson.count
    }

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

        let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
        let personName = arrPerson[indexPath.row]
        cell.textLabel?.text = personName.personName
        return cell

    }

And in the viewDidLoad

override func viewDidLoad() {
    super.viewDidLoad()

    tblView.delegate = self
    tblView.dataSource = self

    let person1 = personData(personName: "Jonny", personAge: 30)
    let person2 = personData(personName: "Chandan", personAge: 20)
    let person3 = personData(personName: "Gopal", personAge: 28)

   arrPerson.append(person1)
   arrPerson.append(person2)
   arrPerson.append(person3)

}
Niraj Paul
  • 1,498
  • 14
  • 22
21

You can use a UITableView delegate method to ask for those actions. Implement this method as follows:

- (NSArray *)tableView:(UITableView *)tableView editActionsForRowAtIndexPath:(NSIndexPath *)indexPath {
     UITableViewRowAction *modifyAction = [UITableViewRowAction rowActionWithStyle:UITableViewRowActionStyleDefault title:@"Modify" handler:^(UITableViewRowAction *action, NSIndexPath *indexPath) {
         // Respond to the action.
     }];
     modifyAction.backgroundColor = [UIColor blueColor];
     return @[modifyAction];
}

You can of course return multiple actions and customize the text and background color.

Implementing this method is also required to make the row editable:

- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
}
bitwit
  • 2,589
  • 2
  • 28
  • 33
BalestraPatrick
  • 9,944
  • 4
  • 30
  • 43
  • I can obtain all that functionality with just a dozen lines of code? Or you're just saying to insert whatever code I do end up using into that function None of the given code even looks like it modifies the cell. Also, trying to resolve this in Swift. – Dave G Aug 14 '15 at 08:54
  • Yes, you can obtain all the functionalities with only that code. It's a built-in feature. Wow, this is even the correct answered and someone down voted it. I'm surprised. – BalestraPatrick Aug 14 '15 at 08:56
  • Be aware that this is available since iOS8+ and it ONLY allows you to swipe left, you have to make custom implementation in order to do swipe right. Other than that, quick and easy answer as well – Jiri Trecak Aug 14 '15 at 09:00
  • Thank you for sharing it. If I am too incompetent to implement the full menu then I might use this simpler solution. I gave it an up-vote since it is relevant but I can't pick it as an answer as it doesn't answer the question of how to mimic the full iOS8 Mail menu, plus it's written in objective-C. – Dave G Aug 14 '15 at 09:25
15

I found this library MGSwipeTableCell After searching a lot to implement a slide cell in table view using swift I found this one and its just one line of code to do the implementation and found it extremely useful.

     func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
  {
    let reuseIdentifier = "programmaticCell"
    var cell = self.table.dequeueReusableCellWithIdentifier(reuseIdentifier) as! MGSwipeTableCell!
    if cell == nil
    {
      cell = MGSwipeTableCell(style: UITableViewCellStyle.Subtitle, reuseIdentifier: reuseIdentifier)
    }

    cell.textLabel!.text = "Title"
    cell.detailTextLabel!.text = "Detail text"
    cell.delegate = self //optional

    //configure left buttons
    cell.leftButtons = [MGSwipeButton(title: "", icon: UIImage(named:"check.png"), backgroundColor: UIColor.greenColor())
      ,MGSwipeButton(title: "", icon: UIImage(named:"fav.png"), backgroundColor: UIColor.blueColor())]
    cell.leftSwipeSettings.transition = MGSwipeTransition.Rotate3D

    //configure right buttons
    cell.rightButtons = [MGSwipeButton(title: "Delete", backgroundColor: UIColor.redColor())
      ,MGSwipeButton(title: "More",backgroundColor: UIColor.lightGrayColor())]
    cell.rightSwipeSettings.transition = MGSwipeTransition.Rotate3D

    return cell
  }

Thats the only function you'll have to implement and update your pod file

patrikbelis
  • 1,350
  • 2
  • 16
  • 35
Somu
  • 601
  • 11
  • 22
11

Swift 3 complete solution:

import UIKit

class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {

    @IBOutlet weak var tableView: UITableView!

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.

        tableView.tableFooterView = UIView(frame: CGRect.zero) //Hiding blank cells.
        tableView.separatorInset = UIEdgeInsets.zero
        tableView.dataSource = self
        tableView.delegate = self
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {

        return 4
    }

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

        let cell: UITableViewCell = tableView.dequeueReusableCell(withIdentifier: "tableCell", for: indexPath)

        return cell
    }

    //Enable cell editing methods.
    func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {

        return true
    }

    func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {

    }

    func tableView(_ tableView: UITableView, editActionsForRowAt indexPath: IndexPath) -> [UITableViewRowAction]? {

        let more = UITableViewRowAction(style: .normal, title: "More") { action, index in
            //self.isEditing = false
            print("more button tapped")
        }
        more.backgroundColor = UIColor.lightGray

        let favorite = UITableViewRowAction(style: .normal, title: "Favorite") { action, index in
            //self.isEditing = false
            print("favorite button tapped")
        }
        favorite.backgroundColor = UIColor.orange

        let share = UITableViewRowAction(style: .normal, title: "Share") { action, index in
            //self.isEditing = false
            print("share button tapped")
        }
        share.backgroundColor = UIColor.blue

        return [share, favorite, more]
    }

}
mriaz0011
  • 1,887
  • 23
  • 11
2

AFAIK there is no in-built ready to go solution, and even if there was in iOS9, you probably cannot use it since you can't only support iOS9 in your app for foreseeable future.

Instead, I recommend you look into this library:

https://github.com/CEWendel/SWTableViewCell

It is very easily configurable, quite polished, and worked well in any swift project that I worked on.

Hope it helps!

Jiri Trecak
  • 5,092
  • 26
  • 37
  • Thanks. New to developing and never used GitHub before. I just downloaded the zip file and opened the project in X-Code, and then ran the project but got "Build Failed". Do I have to merge the code into my project before I can see how it works? – Dave G Aug 14 '15 at 08:52
  • You are better of installing Cocoapods as dependency manager ; It is industry standard and will save you A LOT of headache. More aboout cocoapods and how to use it here https://cocoapods.org/ – Jiri Trecak Aug 14 '15 at 08:53
  • Thanks Jiri, after briefly reading about CocoaPods it sounds like I'll have to do further reading tonight to understand them. I got eager and instead of running the github project I just started looking at the code. It's in objective-C! My app is in Swift and that's the language I am familiar with. Would I have to translate the github solution into Swift or, since they can be run side by side, would I be able to copy the Objective-C ViewController code into my BasicCellViewController? – Dave G Aug 14 '15 at 09:21
  • With cocoapods, you run libraries side by side, objective C and swift if you are using iOS8+. Then you can seamlessly use Obj-C code in your swift project (but it will be hidden under "pods" project), the only thing you have to do is import objective-c library in your "Bridging Header" https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/BuildingCocoaApps/MixandMatch.html – Jiri Trecak Aug 14 '15 at 09:24
  • Just read about CocoaPods (http://www.raywenderlich.com/97014/use-cocoapods-with-swift), I think that'll be too much for my brain to chew on. I get the concept but implementing it in terminal, uses workspaces, having my app run on code not merged in with my other code... plus tweaking the actual menu function to look/act how I want it too... my brain would explode. Going to look into how I'd just paste that obj-c in and tell my app I'm using both languages. Haven't done that before but seems simpler – Dave G Aug 14 '15 at 17:59
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/87055/discussion-between-dave-g-and-jiri-trecak). – Dave G Aug 15 '15 at 08:35
1

It is easier than you think. Here is an example of a Swift class with an implemented UITableView and the ability to swipe UITableViewCell.

import UIKit

class ViewController: UIViewController {

    // MARK: Properties

    let strings = ["firstString", "secondString", "thirdString"]

    // MARK: Outlets

    @IBOutlet weak var tableView: UITableView!

    // MARK: Lifecycle

    override func viewDidLoad() {
        super.viewDidLoad()

        tableView.delegate = self
        tableView.dataSource = self
    }
}

extension ViewController: UITableViewDataSource, UITableViewDelegate {

    // MARK: UITableViewDataSource

    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return objects.count
    }

    func numberOfSections(in tableView: UITableView) -> Int {
        return 1
    }

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "cellIdentifier", for: indexPath)
        let currentString = strings[indexPath.row]
        cell.textLabel?.text = currentString
        return cell
    }

    // MARK: UITableViewDelegate

    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        tableView.deselectRow(at: indexPath, animated: true)
    }

    func tableView(_ tableView: UITableView, leadingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? {
        let leftAction = UIContextualAction(style: .normal, title:  "Red", handler: { (ac:UIContextualAction, view:UIView, success:(Bool) -> Void) in
            print("leftAction tapped")
            success(true)
        })

        leftAction.image = UIImage(named: "")
        leftAction.backgroundColor = UIColor.red

        return UISwipeActionsConfiguration(actions: [leftAction])
    }

    func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? {
        let rightAction = UIContextualAction(style: .normal, title:  "Green", handler: { (ac:UIContextualAction, view:UIView, success:(Bool) -> Void) in
            print("rightAction tapped")
            success(true)
        })

        rightAction.image = UIImage(named: "")
        rightAction.backgroundColor = UIColor.green

        return UISwipeActionsConfiguration(actions: [rightAction])
    }

}
iAleksandr
  • 595
  • 10
  • 11