简体   繁体   English

滑动以删除并单击“更多”按钮(例如,在iOS 7上的Mail应用程序中)

[英]Swipe to Delete and the “More” button (like in Mail app on iOS 7)

How to create a "more" button when user swipe a cell in table view (like mail app in ios 7) 用户在表格视图中滑动单元格时如何创建“更多”按钮(如ios 7中的邮件应用程序)

I have been looking for this information both here and in the Cocoa Touch forum, but I cannot seem to find the answer and I am hoping someone smarter than myself can give me a solution. 我一直在这里和Cocoa Touch论坛中都在寻找这些信息,但是我似乎找不到答案,我希望有一个比我自己聪明的人能给我解决方案。

I would like that when the user swipes a table view cell, to display more than one editing button (he default is the delete button). 我希望当用户滑动表格视图单元格时,显示多个编辑按钮(他默认是删除按钮)。 In the Mail app for iOS 7 you can swipe to delete, but there is a "MORE" button that shows up. 在iOS 7的邮件应用中,您可以滑动以删除,但是会出现一个“更多”按钮。

在此处输入图片说明

How to Implement 如何实施

It looks like iOS 8 opens up this API. 看来iOS 8开启了这个API。 Hints of such functionality are present in Beta 2. Beta 2中提供了此类功能的提示。

To get something working, implement the following two methods on your UITableView's delegate to get the desired effect (see gist for an example). 要使某些功能正常工作,请在UITableView的委托上实现以下两种方法,以获得所需的效果(有关示例,请参见要点)。

- tableView:editActionsForRowAtIndexPath:
- tableView:commitEditingStyle:forRowAtIndexPath:


Known Issues 已知的问题

The documentation says tableView:commitEditingStyle:forRowAtIndexPath is: 该文档说tableView:commitEditingStyle:forRowAtIndexPath是:

"Not called for edit actions using UITableViewRowAction - the action's handler will be invoked instead." “不使用UITableViewRowAction进行编辑操作,而是调用该操作的处理程序。”

However, the swiping doesn't work without it. 但是,没有它,刷卡是行不通的。 Even if the method stub is blank, it still needs it, for now. 即使方法存根为空,目前仍需要它。 This is most obviously a bug in beta 2. 这显然是beta 2中的错误。


Sources 资料来源

https://twitter.com/marksands/status/481642991745265664 https://gist.github.com/marksands/76558707f583dbb8f870 https://twitter.com/marksands/status/481642991745265664 https://gist.github.com/marksands/76558707f583dbb8f870

Original Answer: https://stackoverflow.com/a/24540538/870028 原始答案: https : //stackoverflow.com/a/24540538/870028


Update: 更新:

Sample code with this working (In Swift): http://dropbox.com/s/0fvxosft2mq2v5m/DeleteRowExampleSwift.zip 可以正常工作的示例代码(在Swift中): http : //dropbox.com/s/0fvxosft2mq2v5m/DeleteRowExampleSwift.zip

The sample code contains this easy-to-follow method in MasterViewController.swift, and with just this method you get the behavior shown in the OP screenshot: 示例代码在MasterViewController.swift中包含此易于遵循的方法,仅使用此方法,您就会获得OP屏幕快照中所示的行为:

override func tableView(tableView: UITableView, editActionsForRowAtIndexPath indexPath: NSIndexPath) -> [AnyObject]? {

    var moreRowAction = UITableViewRowAction(style: UITableViewRowActionStyle.Default, title: "More", handler:{action, indexpath in
        println("MORE•ACTION");
    });
    moreRowAction.backgroundColor = UIColor(red: 0.298, green: 0.851, blue: 0.3922, alpha: 1.0);

    var deleteRowAction = UITableViewRowAction(style: UITableViewRowActionStyle.Default, title: "Delete", handler:{action, indexpath in
        println("DELETE•ACTION");
    });

    return [deleteRowAction, moreRowAction];
}

I have created a new library to implement swippable buttons which supports a variety of transitions and expandable buttons like iOS 8 mail app. 我创建了一个新库来实现可滑动按钮,该按钮支持各种转换和可扩展按钮,例如iOS 8邮件应用程序。

https://github.com/MortimerGoro/MGSwipeTableCell https://github.com/MortimerGoro/MGSwipeTableCell

This library is compatible with all the different ways to create a UITableViewCell and its tested on iOS 5, iOS 6, iOS 7 and iOS 8. 该库与创建UITableViewCell的所有不同方式兼容,并且已在iOS 5,iOS 6,iOS 7和iOS 8上进行了测试。

Here a sample of some transitions: 这里是一些过渡的示例:

Border transition: 边境过渡:

边境过渡

Clip transition 剪辑过渡

剪辑过渡

3D Transition: 3D过渡:

在此处输入图片说明

Johnny's answer is the right one to upvote. 约翰尼的答案是正确的答案。 I'm just adding this below in objective-c to make it clearer to beginners (and those of us who refuse to learn Swift syntax :) 我只是在Objective-C中添加以下内容,以使初学者(以及我们中那些拒绝学习Swift语法的人)更加清楚:

Make sure you declare the uitableviewdelegate and have the following methods: 确保声明uitableviewdelegate并具有以下方法:

 -(NSArray *)tableView:(UITableView *)tableView editActionsForRowAtIndexPath:(NSIndexPath *)indexPath {
 UITableViewRowAction *button = [UITableViewRowAction rowActionWithStyle:UITableViewRowActionStyleDefault title:@"Button 1" handler:^(UITableViewRowAction *action, NSIndexPath *indexPath)
    {
        NSLog(@"Action to perform with Button 1");
    }];
    button.backgroundColor = [UIColor greenColor]; //arbitrary color
    UITableViewRowAction *button2 = [UITableViewRowAction rowActionWithStyle:UITableViewRowActionStyleDefault title:@"Button 2" handler:^(UITableViewRowAction *action, NSIndexPath *indexPath)
                                    {
                                        NSLog(@"Action to perform with Button2!");
                                    }];
    button2.backgroundColor = [UIColor blueColor]; //arbitrary color

    return @[button, button2]; //array with all the buttons you want. 1,2,3, etc...
}

- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
// you need to implement this method too or nothing will work:

}
 - (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath
    {
        return YES; //tableview must be editable or nothing will work...
    }

This is (rather ridiculously) a private API. 这是(非常荒谬)私有API。

The following two methods are private and sent to the UITableView's delegate: 以下两个方法是私有的,并发送给UITableView的委托:

-(NSString *)tableView:(UITableView *)tableView titleForSwipeAccessoryButtonForRowAtIndexPath:(NSIndexPath *)indexPath;
-(void)tableView:(UITableView *)tableView swipeAccessoryButtonPushedForRowAtIndexPath:(NSIndexPath *)indexPath;

They are pretty self explanatory. 他们很自我解释。

To improve on Johnny's answer, this can now be done using the public API as follows : 为了改善Johnny的答案,现在可以使用以下公共API来完成此操作:

func tableView(tableView: UITableView, editActionsForRowAtIndexPath indexPath: NSIndexPath) -> [UITableViewRowAction]? {

    let moreRowAction = UITableViewRowAction(style: UITableViewRowActionStyle.default, title: "More", handler:{action, indexpath in
        print("MORE•ACTION");
    });
    moreRowAction.backgroundColor = UIColor(red: 0.298, green: 0.851, blue: 0.3922, alpha: 1.0);

    let deleteRowAction = UITableViewRowAction(style: UITableViewRowActionStyle.default, title: "Delete", handler:{action, indexpath in
        print("DELETE•ACTION");
    });

    return [deleteRowAction, moreRowAction];
}

I hope you cant wait till apple gives you what ever you need right? 希望您等不及苹果给您所需的东西了吗? So here is my option. 所以这是我的选择。

Create a custom cell. 创建一个自定义单元格。 Have two uiviews in it 有两个uiviews

1. upper
2. lower

In lower view, add what ever buttons you need. 在下部视图中,添加所需的按钮。 Deal its actions just like any other IBActions. 像其他任何IBAction一样处理其动作。 you can decide the animation time, style and anything. 您可以决定动画时间,样式和其他任何内容。

now add a uiswipegesture to the upper view and reveal your lower view on swipe gesture. 现在将uiswipegesture添加到上方视图,并以滑动手势显示下方视图。 I have done this before and its the simplest option as far as I am concerned. 我之前已经这样做过,就我而言,这是最简单的选择。

Hope that help. 希望对您有所帮助。

This is not possible using the standard SDK. 使用标准SDK无法做到这一点。 However there are various 3rd party solutions that more or less imitate the behavior in Mail.app. 但是,有各种各样的第三方解决方案或多或少地模仿Mail.app中的行为。 Some of them (eg MCSwipeTableViewCell , DAContextMenuTableViewController , RMSwipeTableViewCell ) detect swipes using gesture recognizers, some of them (eg SWTableViewCell ) put a second UISScrollView below the standard UITableViewCellScrollView (private subview of UITableViewCell ) and some of them modify the behavior of UITableViewCellScrollView . 他们中的一些(例如MCSwipeTableViewCellDAContextMenuTableViewControllerRMSwipeTableViewCell )使用手势识别检测挥笔,其中一些(例如SWTableViewCell )把第二个UISScrollView低于标准UITableViewCellScrollView (私有子视图UITableViewCell ),其中一些修改的行为UITableViewCellScrollView

I like the last approach most since the touch handling feels most natural. 我最喜欢最后一种方法,因为触摸处理感觉最自然。 Specifically, MSCMoreOptionTableViewCell is good. 具体来说, MSCMoreOptionTableViewCell很好。 Your choice may vary depending on your specific needs (whether you need a left-to-right pan, too, whether you need iOS 6 compatibility, etc.). 您的选择可能会根据您的特定需求而有所不同(是否也需要从左到右的摇摄,是否需要与iOS 6兼容等等)。 Also be aware that most of these approaches come with a burden: they can easily break in a future iOS version if Apple makes changes in the UITableViewCell subview hierarchy. 还应注意,这些方法大多数都会带来负担:如果Apple在UITableViewCell子视图层次结构中进行更改,则它们很容易在将来的iOS版本中中断。

Swift 3 version code without using any library: 不使用任何库的Swift 3版本代码:

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]
    }

}

You need to subclass UITableViewCell and subclass method willTransitionToState:(UITableViewCellStateMask)state which is called whenever user swipes the cell. 您需要子类化UITableViewCell和子类方法willTransitionToState:(UITableViewCellStateMask)state ,每当用户滑动单元格时都会调用该willTransitionToState:(UITableViewCellStateMask)state The state flags will let you know if the Delete button is showing, and show/hide your More button there. state标志将让您知道“删除”按钮是否正在显示,并在那里显示/隐藏“更多”按钮。

Unfortunately this method gives you neither the width of the Delete button nor the animation time. 不幸的是,此方法既无法提供“删除”按钮的宽度,也无法提供动画时间。 So you need to observer & hard-code your More button's frame and animation time into your code (I personally think Apple needs to do something about this). 因此,您需要观察者并将“更多”按钮的帧和动画时间硬编码到代码中(我个人认为Apple需要为此做些事情)。

For swift programming 快速编程

func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
  if editingStyle == UITableViewCellEditingStyle.Delete {
    deleteModelAt(indexPath.row)
    self.tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: .Automatic)
  }
  else if editingStyle == UITableViewCellEditingStyle.Insert {
    println("insert editing action")
  }
}

func tableView(tableView: UITableView, editActionsForRowAtIndexPath indexPath: NSIndexPath) -> [AnyObject]? {
  var archiveAction = UITableViewRowAction(style: .Default, title: "Archive",handler: { (action: UITableViewRowAction!, indexPath: NSIndexPath!) in
        // maybe show an action sheet with more options
        self.tableView.setEditing(false, animated: false)
      }
  )
  archiveAction.backgroundColor = UIColor.lightGrayColor()

  var deleteAction = UITableViewRowAction(style: .Normal, title: "Delete",
      handler: { (action: UITableViewRowAction!, indexPath: NSIndexPath!) in
        self.deleteModelAt(indexPath.row)
        self.tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: .Automatic);
      }
  );
  deleteAction.backgroundColor = UIColor.redColor()

  return [deleteAction, archiveAction]
}

func deleteModelAt(index: Int) {
  //... delete logic for model
}

THIS COULD HELP YOU OUT. 这可以帮助您。

-(NSArray *)tableView:(UITableView *)tableView editActionsForRowAtIndexPath:(NSIndexPath *)indexPath {
 UITableViewRowAction *button = [UITableViewRowAction rowActionWithStyle:UITableViewRowActionStyleDefault title:@"Button 1" handler:^(UITableViewRowAction *action, NSIndexPath *indexPath)
    {
        NSLog(@"Action to perform with Button 1");
    }];
    button.backgroundColor = [UIColor greenColor]; //arbitrary color
    UITableViewRowAction *button2 = [UITableViewRowAction rowActionWithStyle:UITableViewRowActionStyleDefault title:@"Button 2" handler:^(UITableViewRowAction *action, NSIndexPath *indexPath)
                                    {
                                        NSLog(@"Action to perform with Button2!");
                                    }];
    button2.backgroundColor = [UIColor blueColor]; //arbitrary color

    return @[button, button2]; //array with all the buttons you want. 1,2,3, etc...
}

- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
// you need to implement this method too or nothing will work:

}
 - (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath
    {
        return YES; //tableview must be editable or nothing will work...
    }

I was looking to add the same functionality to my app, and after going through so many different tutorials ( raywenderlich being the best DIY solution), I found out that Apple has its own UITableViewRowAction class, which is very handy. 我一直在尝试向我的应用程序添加相同的功能,并且在经历了许多不同的教程( raywenderlich是最好的DIY解决方案)之后,我发现Apple有自己的UITableViewRowAction类,非常方便。

You have to change the Tableview's boilerpoint method to this: 您必须将Tableview的锅炉方法更改为此:

override func tableView(tableView: UITableView, editActionsForRowAtIndexPath indexPath: NSIndexPath) -> [AnyObject]?  {
    // 1   
    var shareAction = UITableViewRowAction(style: UITableViewRowActionStyle.Default, title: "Share" , handler: { (action:UITableViewRowAction!, indexPath:NSIndexPath!) -> Void in
    // 2
    let shareMenu = UIAlertController(title: nil, message: "Share using", preferredStyle: .ActionSheet)

    let twitterAction = UIAlertAction(title: "Twitter", style: UIAlertActionStyle.Default, handler: nil)
    let cancelAction = UIAlertAction(title: "Cancel", style: UIAlertActionStyle.Cancel, handler: nil)

    shareMenu.addAction(twitterAction)
    shareMenu.addAction(cancelAction)


    self.presentViewController(shareMenu, animated: true, completion: nil)
    })
    // 3
    var rateAction = UITableViewRowAction(style: UITableViewRowActionStyle.Default, title: "Rate" , handler: { (action:UITableViewRowAction!, indexPath:NSIndexPath!) -> Void in
    // 4
    let rateMenu = UIAlertController(title: nil, message: "Rate this App", preferredStyle: .ActionSheet)

    let appRateAction = UIAlertAction(title: "Rate", style: UIAlertActionStyle.Default, handler: nil)
    let cancelAction = UIAlertAction(title: "Cancel", style: UIAlertActionStyle.Cancel, handler: nil)

    rateMenu.addAction(appRateAction)
    rateMenu.addAction(cancelAction)


    self.presentViewController(rateMenu, animated: true, completion: nil)
    })
    // 5
    return [shareAction,rateAction]
  }

You can find out more about this on This Site . 您可以在本网站上找到更多有关此的信息。 Apple's own documentation is really useful for changing the background colour: Apple 自己的文档对于更改背景颜色非常有用:

The background color of the action button. 操作按钮的背景色。

Declaration OBJECTIVE-C @property(nonatomic, copy) UIColor *backgroundColor Discussion Use this property to specify the background color for your button. 声明OBJECTIVE-C @property(非原子,复制)UIColor * backgroundColor讨论使用此属性指定按钮的背景色。 If you do not specify a value for this property, UIKit assigns a default color based on the value in the style property. 如果未为此属性指定值,则UIKit将基于style属性中的值分配默认颜色。

Availability Available in iOS 8.0 and later. 可用性在iOS 8.0和更高版本中可用。

If you want to change the font of the button, it's a bit more tricky. 如果您想更改按钮的字体,则比较棘手。 I've seen another post on SO. 我看过另一篇关于SO的文章。 For the sake of providing the code as well as the link, here's the code they used there. 为了提供代码和链接,下面是他们在此处使用的代码。 You'd have to change the appearance of the button. 您必须更改按钮的外观。 You'd have to make a specific reference to tableviewcell, otherwise you'd change the button's appearance throughout your app (I didn't want that, but you might, I don't know :) ) 您必须对tableviewcell进行特定的引用,否则您将在整个应用程序中更改按钮的外观(我不希望那样,但是您可能不知道:))

Objective C: 目标C:

+ (void)setupDeleteRowActionStyleForUserCell {

    UIFont *font = [UIFont fontWithName:@"AvenirNext-Regular" size:19];

    NSDictionary *attributes = @{NSFontAttributeName: font,
                      NSForegroundColorAttributeName: [UIColor whiteColor]};

    NSAttributedString *attributedTitle = [[NSAttributedString alloc] initWithString: @"DELETE"
                                                                          attributes: attributes];

    /*
     * We include UIView in the containment hierarchy because there is another button in UserCell that is a direct descendant of UserCell that we don't want this to affect.
     */
    [[UIButton appearanceWhenContainedIn:[UIView class], [UserCell class], nil] setAttributedTitle: attributedTitle
                                                                                          forState: UIControlStateNormal];
}

Swift: 迅速:

    //create your attributes however you want to
    let attributes = [NSFontAttributeName: UIFont.systemFontOfSize(UIFont.systemFontSize())] as Dictionary!            

   //Add more view controller types in the []
    UIButton.appearanceWhenContainedInInstancesOfClasses([ViewController.self])

This is the easiest, and most stream-lined version IMHO. 这是最简单,最简化的版本恕我直言。 Hope it helps. 希望能帮助到你。

Update: Here's the Swift 3.0 version: 更新:这是Swift 3.0版本:

func tableView(_ tableView: UITableView, editActionsForRowAt indexPath: IndexPath) -> [UITableViewRowAction]? {
    var shareAction:UITableViewRowAction = UITableViewRowAction(style: .default, title: "Share", handler: {(action, cellIndexpath) -> Void in
        let shareMenu = UIAlertController(title: nil, message: "Share using", preferredStyle: .actionSheet)

        let twitterAction = UIAlertAction(title: "Twitter", style: .default, handler: nil)
        let cancelAction = UIAlertAction(title: "Cancel", style: .cancel, handler: nil)

        shareMenu.addAction(twitterAction)
        shareMenu.addAction(cancelAction)


        self.present(shareMenu,animated: true, completion: nil)
    })

    var rateAction:UITableViewRowAction = UITableViewRowAction(style: .default, title: "Rate" , handler: {(action, cellIndexpath) -> Void in
        // 4
        let rateMenu = UIAlertController(title: nil, message: "Rate this App", preferredStyle: .actionSheet)

        let appRateAction = UIAlertAction(title: "Rate", style: .default, handler: nil)
        let cancelAction = UIAlertAction(title: "Cancel", style: .cancel, handler: nil)

        rateMenu.addAction(appRateAction)
        rateMenu.addAction(cancelAction)


        self.present(rateMenu, animated: true, completion: nil)
    })
    // 5
    return [shareAction,rateAction]
}

Actual Swift 3 Answer 实际的Swift 3答案

This is the ONLY function you need. 这是您唯一需要的功能。 You do not need CanEdit or CommitEditingStyle functions for custom actions. 您不需要CanEdit或CommitEditingStyle函数来执行自定义操作。

func tableView(_ tableView: UITableView, editActionsForRowAt indexPath: IndexPath) -> [UITableViewRowAction]? {
    let action1 = UITableViewRowAction(style: .default, title: "Action1", handler: {
        (action, indexPath) in
        print("Action1")
    })
    action1.backgroundColor = UIColor.lightGray
    let action2 = UITableViewRowAction(style: .default, title: "Action2", handler: {
        (action, indexPath) in
        print("Action2")
    })
    return [action1, action2]
}

As of iOS 11 this is publicly available in UITableViewDelegate . 从iOS 11开始,它可以在UITableViewDelegate公开获得。 Here's some sample code: 这是一些示例代码:

- (UISwipeActionsConfiguration *)tableView:(UITableView *)tableView trailingSwipeActionsConfigurationForRowAtIndexPath:(NSIndexPath *)indexPath {
    UIContextualAction *delete = [UIContextualAction contextualActionWithStyle:UIContextualActionStyleDestructive
                                                                         title:@"DELETE"
                                                                       handler:^(UIContextualAction * _Nonnull action, __kindof UIView * _Nonnull sourceView, void (^ _Nonnull completionHandler)(BOOL)) {
                                                                           NSLog(@"index path of delete: %@", indexPath);
                                                                           completionHandler(YES);
                                                                       }];

    UIContextualAction *rename = [UIContextualAction contextualActionWithStyle:UIContextualActionStyleNormal
                                                                         title:@"RENAME"
                                                                       handler:^(UIContextualAction * _Nonnull action, __kindof UIView * _Nonnull sourceView, void (^ _Nonnull completionHandler)(BOOL)) {
                                                                           NSLog(@"index path of rename: %@", indexPath);
                                                                           completionHandler(YES);
                                                                       }];

    UISwipeActionsConfiguration *swipeActionConfig = [UISwipeActionsConfiguration configurationWithActions:@[rename, delete]];
    swipeActionConfig.performsFirstActionWithFullSwipe = NO;

    return swipeActionConfig;
}

Also available: 也提供:

- (UISwipeActionsConfiguration *)tableView:(UITableView *)tableView leadingSwipeActionsConfigurationForRowAtIndexPath:(NSIndexPath *)indexPath;

Docs: https://developer.apple.com/documentation/uikit/uitableviewdelegate/2902367-tableview?language=objc 文件: https : //developer.apple.com/documentation/uikit/uitableviewdelegate/2902367-tableview? language =objc

Swift 4 & iOs 11+ Swift 4和iOs 11+

@available(iOS 11.0, *)
override func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? {

    let delete = UIContextualAction(style: .destructive, title: "Delete") { _, _, handler in

        handler(true)
        // handle deletion here
    }

    let more = UIContextualAction(style: .normal, title: "More") { _, _, handler in

        handler(true)
        // handle more here
    }

    return UISwipeActionsConfiguration(actions: [delete, more])
}

I used tableViewCell to show multiple data, after swipe () right to left on a cell it will show two buttons Approve And reject, there are two methods, the first one is ApproveFunc which takes one argument, and the another one is RejectFunc which also takes one argument. 我使用tableViewCell来显示多个数据,在一个单元格上从右向左滑动()之后,它将显示两个按钮Approve and reject,有两种方法,第一种是ApproveFunc,它接受一个参数,第二种是RejectFunc,它也有一个论点。

在此处输入图片说明

func tableView(_ tableView: UITableView, editActionsForRowAt indexPath: IndexPath) -> [UITableViewRowAction]? {
        let Approve = UITableViewRowAction(style: .normal, title: "Approve") { action, index in

            self.ApproveFunc(indexPath: indexPath)
        }
        Approve.backgroundColor = .green

        let Reject = UITableViewRowAction(style: .normal, title: "Reject") { action, index in

            self.rejectFunc(indexPath: indexPath)
        }
        Reject.backgroundColor = .red



        return [Reject, Approve]
    }

    func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
        return true
    }

    func ApproveFunc(indexPath: IndexPath) {
        print(indexPath.row)
    }
    func rejectFunc(indexPath: IndexPath) {
        print(indexPath.row)
    }

Here's a somewhat fragile way of doing it that does not involve private APIs or constructing your own system. 这是一种有些脆弱的方法,它不涉及私有API或构造您自己的系统。 You're hedging your bets that Apple doesn't break this and that hopefully they will release an API that you can replace these few lines of code with. 您在押注苹果不会破坏这一点,并希望他们会发布一个API,您可以用这些API替换这几行代码。

  1. KVO self.contentView.superview.layer.sublayer. KVO self.contentView.superview.layer.sublayer。 Do this in init. 在init中执行此操作。 This is the UIScrollView's layer. 这是UIScrollView的层。 You can't KVO 'subviews'. 您不能KVO“子视图”。
  2. When subviews changes, find the delete confirmation view within scrollview.subviews. 当子视图改变时,在scrollview.subviews中找到删除确认视图。 This is done in the observe callback. 这是在观察回调中完成的。
  3. Double the size of that view and add a UIButton to the left of its only subview. 将该视图的大小加倍,并在其唯一子视图的左侧添加一个UIButton。 This is also done in the observe callback. 这也在观察回调中完成。 The only subview of the delete confirmation view is the delete button. 删除确认视图的唯一子视图是删除按钮。
  4. (optional) The UIButton event should look up self.superview until it finds a UITableView and then call a datasource or delegate method you create, such as tableView:commitCustomEditingStyle:forRowAtIndexPath:. (可选)UIButton事件应查找self.superview,直到找到UITableView,然后调用您创建的数据源或委托方法,例如tableView:commitCustomEditingStyle:forRowAtIndexPath:。 You may find the indexPath of the cell by using [tableView indexPathForCell:self]. 您可以使用[tableView indexPathForCell:self]找到单元格的indexPath。

This also requires that you implement the standard table view editing delegate callbacks. 这还要求您实现标准的表视图编辑委托回调。

static char kObserveContext = 0;

@implementation KZTableViewCell {
    UIScrollView *_contentScrollView;
    UIView *_confirmationView;
    UIButton *_editButton;
    UIButton *_deleteButton;
}

- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
    self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
    if (self) {
        _contentScrollView = (id)self.contentView.superview;

        [_contentScrollView.layer addObserver:self
             forKeyPath:@"sublayers"
                options:0
                context:&kObserveContext];

        _editButton = [UIButton new];
        _editButton.backgroundColor = [UIColor lightGrayColor];
        [_editButton setTitle:@"Edit" forState:UIControlStateNormal];
        [_editButton addTarget:self
                        action:@selector(_editTap)
              forControlEvents:UIControlEventTouchUpInside];

    }
    return self;
}

-(void)dealloc {
    [_contentScrollView.layer removeObserver:self forKeyPath:@"sublayers" context:&kObserveContext];
}

-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
    if(context != &kObserveContext) {
        [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
        return;
    }
    if(object == _contentScrollView.layer) {
        for(UIView * view in _contentScrollView.subviews) {
            if([NSStringFromClass(view.class) hasSuffix:@"ConfirmationView"]) {
                _confirmationView = view;
                _deleteButton = [view.subviews objectAtIndex:0];
                CGRect frame = _confirmationView.frame;
                CGRect frame2 = frame;
                frame.origin.x -= frame.size.width;
                frame.size.width *= 2;
                _confirmationView.frame = frame;

                frame2.origin = CGPointZero;
                _editButton.frame = frame2;
                frame2.origin.x += frame2.size.width;
                _deleteButton.frame = frame2;
                [_confirmationView addSubview:_editButton];
                break;
            }
        }
        return;
    }
}

-(void)_editTap {
    UITableView *tv = (id)self.superview;
    while(tv && ![tv isKindOfClass:[UITableView class]]) {
        tv = (id)tv.superview;
    }
    id<UITableViewDelegate> delegate = tv.delegate;
    if([delegate respondsToSelector:@selector(tableView:editTappedForRowWithIndexPath:)]) {
        NSIndexPath *ip = [tv indexPathForCell:self];
        // define this in your own protocol
        [delegate tableView:tv editTappedForRowWithIndexPath:ip];
    }
}
@end

There is an amazing library called SwipeCellKit , it should gain more acknowledgement. 有一个很棒的库SwipeCellKit ,它应该获得更多认可。 In my opinion it is cooler than MGSwipeTableCell . 在我看来,它比MGSwipeTableCell The latter doesn't completely replicate the behavior of the Mail app's cells whereas SwipeCellKit does. 后者不能完全复制Mail应用程序单元的行为,而SwipeCellKit可以。 Have a look 看一看

Swift 4 斯威夫特4

func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? {
    let delete = UIContextualAction(style: .destructive, title: "Delete") { (action, sourceView, completionHandler) in
        print("index path of delete: \(indexPath)")
        completionHandler(true)
    }
    let rename = UIContextualAction(style: .normal, title: "Edit") { (action, sourceView, completionHandler) in
        print("index path of edit: \(indexPath)")
        completionHandler(true)
    }
    let swipeActionConfig = UISwipeActionsConfiguration(actions: [rename, delete])
    swipeActionConfig.performsFirstActionWithFullSwipe = false
    return swipeActionConfig
}

Here is one simple solution. 这是一个简单的解决方案。 It is capable to display and hide custom UIView inside UITableViewCell. 它能够显示和隐藏UITableViewCell中的自定义UIView。 Displaying logic is contained inside class extended from UITableViewCell, BaseTableViewCell. 显示逻辑包含在从UITableViewCell,BaseTableViewCell扩展的类内部。

BaseTableViewCell.h BaseTableViewCell.h

#import <UIKit/UIKit.h>

@interface BaseTableViewCell : UITableViewCell

@property(nonatomic,strong)UIView* customView;

-(void)showCustomView;

-(void)hideCustomView;

@end

BaseTableViewCell.M BaseTableViewCell.M

#import "BaseTableViewCell.h"

@interface BaseTableViewCell()
{
    BOOL _isCustomViewVisible;
}

@end

@implementation BaseTableViewCell

- (void)awakeFromNib {
    // Initialization code
}

-(void)prepareForReuse
{
    self.customView = nil;
    _isCustomViewVisible = NO;
}

- (void)setSelected:(BOOL)selected animated:(BOOL)animated {
    [super setSelected:selected animated:animated];

    // Configure the view for the selected state
}

-(void)showCustomView
{
    if(nil != self.customView)
    {
        if(!_isCustomViewVisible)
        {
            _isCustomViewVisible = YES;

            if(!self.customView.superview)
            {
                CGRect frame = self.customView.frame;
                frame.origin.x = self.contentView.frame.size.width;
                self.customView.frame = frame;
                [self.customView willMoveToSuperview:self.contentView];
                [self.contentView addSubview:self.customView];
                [self.customView didMoveToSuperview];
            }

            __weak BaseTableViewCell* blockSelf = self;
            [UIView animateWithDuration:.5 animations:^(){

                for(UIView* view in blockSelf.contentView.subviews)
                {
                    CGRect frame = view.frame;
                    frame.origin.x = frame.origin.x - blockSelf.customView.frame.size.width;
                    view.frame = frame;
                }
            }];
        }
    }
}

-(void)hideCustomView
{
    if(nil != self.customView)
    {
        if(_isCustomViewVisible)
        {
            __weak BaseTableViewCell* blockSelf = self;
            _isCustomViewVisible = NO;
            [UIView animateWithDuration:.5 animations:^(){
                for(UIView* view in blockSelf.contentView.subviews)
                {
                    CGRect frame = view.frame;
                    frame.origin.x = frame.origin.x + blockSelf.customView.frame.size.width;
                    view.frame = frame;
                }
            }];
        }
    }
}

@end

To get this functionality, simple extend your table view cell from BaseTableViewCell. 要获得此功能,只需从BaseTableViewCell扩展表视图单元即可。

Next, Inside UIViewController, which implement UITableViewDelegate, create two gesture recognizers to handle left and right swipes. 接下来,在实现UITableViewDelegate的UIViewController内部,创建两个手势识别器来处理左右滑动。

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.

    [self.tableView registerNib:[UINib nibWithNibName:CUSTOM_CELL_NIB_NAME bundle:nil] forCellReuseIdentifier:CUSTOM_CELL_ID];

    UISwipeGestureRecognizer* leftSwipeRecognizer = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(handleLeftSwipe:)];
    leftSwipeRecognizer.direction = UISwipeGestureRecognizerDirectionLeft;
    [self.tableView addGestureRecognizer:leftSwipeRecognizer];

    UISwipeGestureRecognizer* rightSwipeRecognizer = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(handleRightSwipe:)];
    rightSwipeRecognizer.direction = UISwipeGestureRecognizerDirectionRight;
    [self.tableView addGestureRecognizer:rightSwipeRecognizer];
}

Than add two swipe handlers 比添加两个滑动处理程序

- (void)handleLeftSwipe:(UISwipeGestureRecognizer*)recognizer
{
    CGPoint point = [recognizer locationInView:self.tableView];
    NSIndexPath* index = [self.tableView indexPathForRowAtPoint:point];

    UITableViewCell* cell = [self.tableView cellForRowAtIndexPath:index];

    if([cell respondsToSelector:@selector(showCustomView)])
    {
        [cell performSelector:@selector(showCustomView)];
    }
}

- (void)handleRightSwipe:(UISwipeGestureRecognizer*)recognizer
{
    CGPoint point = [recognizer locationInView:self.tableView];
    NSIndexPath* index = [self.tableView indexPathForRowAtPoint:point];

    UITableViewCell* cell = [self.tableView cellForRowAtIndexPath:index];

    if([cell respondsToSelector:@selector(hideCustomView)])
    {
        [cell performSelector:@selector(hideCustomView)];
    }
}

Now, inside cellForRowAtIndexPath, of UITableViewDelegate, you can create custom UIView and attach it to the dequeued cell. 现在,在UITableViewDelegate的cellForRowAtIndexPath内部,您可以创建自定义UIView并将其附加到出队单元格上。

-(UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    CustomCellTableViewCell* cell = (CustomCellTableViewCell*)[tableView dequeueReusableCellWithIdentifier:@"CustomCellTableViewCell" forIndexPath:indexPath];

    NSArray* nibViews = [[NSBundle mainBundle] loadNibNamed:@"CellCustomView"
                                                      owner:nil
                                                    options:nil];

    CellCustomView* customView = (CellCustomView*)[ nibViews objectAtIndex: 0];

    cell.customView = customView;

    return cell;
}

Of course, this way of loading of custom UIView is just for this example. 当然,这种自定义UIView的加载方式仅用于此示例。 Manage it as you want. 根据需要进行管理。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM