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


246

用户在表格视图中滑动单元格时如何创建“更多”按钮(例如ios 7中的邮件应用程序)

我一直在这里和Cocoa Touch论坛中都在寻找这些信息,但是我似乎找不到答案,我希望比我自己更聪明的人可以给我解决方案。

我希望当用户滑动一个表格视图单元时,显示多个编辑按钮(他默认是删除按钮)。在iOS 7的邮件应用中,您可以滑动以删除,但是会出现一个“更多”按钮。

在此处输入图片说明



要添加“删除”按钮,我实现以下两个功能。-(BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath; -(void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath; 我想在它旁边添加按钮“更多”。
Guy Kahlon 2013年

3
@MonishBansal Bansal这个线程中的某个人(Apple开发者论坛中的devforums.apple.com/message/860459#860459)继续前进并构建了自己的实现。您可以在GitHub上找到一个可以满足
Guy Kahlon

8
@GuyKahlonMatrix感谢您的解决方案,它像一个魅力。这个问题是许多Google搜索的第一结果,人们被迫使用评论来交换他们的知识,因为有人认为关闭该问题并宣扬民主会更有帮助。这个地方显然需要更好的mod。
Şafak基色

2
如果您可以将iOS 8作为目标平台,那么以下是您想要的答案。
约翰尼

Answers:


126

如何实施

看来iOS 8开启了这个API。Beta 2中提供了此类功能的提示。

要使某些功能正常工作,请在UITableView的委托上实现以下两种方法以获得所需的效果(有关示例,请参见要点)。

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


已知的问题

该文档说tableView:commitEditingStyle:forRowAtIndexPath是:

“不使用UITableViewRowAction进行编辑操作,而是调用该操作的处理程序。”

但是,没有它,刷卡是行不通的。即使该方法存根为空,目前仍需要它。这显然是beta 2中的错误。


资料来源

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

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


更新:

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

示例代码在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];
}

1
这似乎是正确的,但是在Xcode 6 GM中,滑动手势似乎不起作用。仍然可以通过将表视图置于编辑模式来访问editActions。其他人发现刷卡无效吗?
齐格福特2014年

@Siegfoult您是否尝试实现(即使留空)tableView:commitEditingStyle:forRowAtIndexPath :?
约翰尼(Johnny)

我没有在目标c中工作。我编写了相同的代码。请提出一些提示。
Solid Soft

@SolidSoft您有一个我可以看的示例项目吗?我也许能够以这种方式提供更好的帮助。
约翰尼

3
回答我自己的评论。您调用tableView.editing = falseNO在objc中),单元格将“关闭”。
Ben Lachman 2014年

121

我创建了一个新库来实现可滑动按钮,该按钮支持各种转换和可扩展按钮,例如iOS 8邮件应用程序。

https://github.com/MortimerGoro/MGSwipeTableCell

该库与创建UITableViewCell的所有不同方式兼容,并且已在iOS 5,iOS 6,iOS 7和iOS 8上进行了测试。

这里是一些过渡的示例:

边境过渡:

边境过渡

剪辑过渡

剪辑过渡

3D过渡:

在此处输入图片说明


1
做得好!拥有回调以自定义动画真是太棒了。
Pacu 2014年

1
@MortimerGoro不错的人。看上去不错。我正在尝试在我的Android项目之一中实现类似的效果。请告诉我如何在Android中实现此目标?
Nitesh Kumar

在iOS 8 + iPad上,我只是没有发生滑动。
ScorpionKing2k5 2015年

这是一个了不起的库,很好的地方是它仍然有支持。
凌晨

@MortimerGoro,我尝试使用“ MGSwipeTableCel” l框架,但问题是当我重新加载表格时却隐藏了滑动按钮。解决此问题的任何方法。
Ganesh Guturi 2015年

71

约翰尼的答案是正确的答案。我只是在Objective-C中添加以下内容,以使初学者(以及我们中那些拒绝学习Swift语法的人)更加清楚:

确保声明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...
    }

1
值得一提的是canEditRowAtIndexPath
Heckscheibe

如果我在滑动单元格后重新加载表格,那么那些滑动按钮是可见的还是隐藏的?
Ganesh Guturi 2015年

25

这是(非常荒谬)私有API。

以下两个方法是私有的,并发送给UITableView的委托:

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

他们很自我解释。


4
苹果已经在iOS 8中启用了此功能。请参阅下面的约翰尼答案。
齐格福特2014年

24

为了改善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];
}

17

我希望您不能等到苹果给您您所需要的东西了吗?所以这是我的选择。

创建一个自定义单元格。有两个uiviews

1. upper
2. lower

在下部视图中,添加所需的按钮。像其他任何IBAction一样处理其动作。您可以决定动画时间,样式和其他任何内容。

现在将uiswipegesture添加到上方视图,并以滑动手势显示下方视图。我之前已经这样做过,就我而言,这是最简单的选择。

希望对您有所帮助。


7

使用标准SDK无法做到这一点。但是,有各种第三方解决方案或多或少地模仿Mail.app中的行为。其中一些(例如MCSwipeTableViewCellDAContextMenuTableViewControllerRMSwipeTableViewCell)使用手势识别器检测滑动,其中一些(例如SWTableViewCell)将第二个UISScrollView放置在标准下方UITableViewCellScrollView(的私有子视图UITableViewCell),其中一些修改了的行为UITableViewCellScrollView

我最喜欢最后一种方法,因为触摸处理感觉最自然。具体来说,MSCMoreOptionTableViewCell很好。您的选择可能会根据您的特定需求而有所不同(是否也需要从左到右的摇摄,是否需要iOS 6兼容性等)。还应注意,这些方法大多数都会带来负担:如果Apple在UITableViewCell子视图层次结构中进行更改,它们很容易在将来的iOS版本中中断。


7

不使用任何库的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]
    }

}

6

您需要子类UITableViewCell和子类方法willTransitionToState:(UITableViewCellStateMask)state,每当用户滑动单元格时就会调用该方法。这些state标志将让您知道“删除”按钮是否正在显示,并在那里显示/隐藏“更多”按钮。

不幸的是,此方法既无法提供“删除”按钮的宽度,也无法提供动画时间。因此,您需要观察者并将“更多”按钮的帧和动画时间硬编码到代码中(我个人认为Apple需要为此做些事情)。


7
“我个人认为苹果需要为此做些事情”。我同意。您是否已经为他们编写了错误报告/功能请求?
Tafkadasoh

4

快速编程

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
}

@bibscy,欢迎您提出修改建议。很长一段时间没有使用
Swift

3

这可以帮助您。

-(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...
    }

3

我一直在尝试向我的应用程序添加相同的功能,在经历了许多不同的教程(raywenderlich是最好的DIY解决方案)之后,我发现Apple拥有自己的UITableViewRowAction类,非常方便。

您必须将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]
  }

您可以在本网站上找到更多有关此的信息。Apple 自己的文档对于更改背景颜色非常有用:

操作按钮的背景色。

声明OBJECTIVE-C @property(非原子,复制)UIColor * backgroundColor讨论使用此属性可以指定按钮的背景色。如果未为此属性指定值,则UIKit将基于style属性中的值分配默认颜色。

可用性在iOS 8.0和更高版本中可用。

如果要更改按钮的字体,则比较棘手。我看过另一篇关于SO的文章。为了提供代码和链接,下面是他们在此处使用的代码。您必须更改按钮的外观。您必须对tableviewcell进行特定的引用,否则您将在整个应用程序中更改按钮的外观(我不希望那样,但是您可能不知道:))

目标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];
}

迅速:

    //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])

这是最简单,最简化的版本恕我直言。希望能帮助到你。

更新:这是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]
}

1
感谢您的回答,我相信它将对许多开发人员有所帮助。是的,您是对的,实际上Apple从iOS 8提供了此解决方案。但是不幸的是,此本机解决方案未提供完整的功能。例如,在Apple的Mail应用程序中,您具有两侧的按钮(左侧为一个按钮,右侧为三个按钮),而Apple的当前API则无法在两侧都添加按钮,并且当前的API不支持用户向两侧长滑动时的默认操作。目前,恕我直言,最好的解决方案是开源MGSwipeTableCell。
Guy Kahlon

@GuyKahlon是的,关于左右滑动问题,您是绝对正确的,我同意对于更多的自定义设置,MGSwipeTableCell是最好的。苹果公司自己的解决方案不是最复杂的选择,但我发现对于简单任务而言,它最直接。
Septronic's

@Septronic您能否将代码更新为Swift 3?shareMenu.没有采取一种addAction方法。谢谢
bibscy

@bibscy我添加了快速版本。您还需要该属性位吗?sharemenu只是一个UIAlertController,因此应该采取行动。试试看,让我知道是否有运气:)
Septronic

3

实际的Swift 3答案

这是您唯一需要的功能。您不需要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]
}

3

从iOS 11开始,该版本已在中公开提供UITableViewDelegate。这是一些示例代码:

- (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;
}

也提供:

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

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


3

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

2

我使用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)
    }

您可以在答案中添加一些解释,以便读者可以从中学习吗?
Nico Haase

感谢您提供此代码段,它可能会提供一些有限的即时帮助。一个适当的解释,将大大提高其长期的价值通过展示为什么这是一个很好的解决了这个问题,并会使其与其他类似的问题,更有助于未来的读者。请编辑您的答案以添加一些解释,包括您所做的假设。
蒂姆·迪克曼

1

这是一种有些脆弱的方法,它不涉及私有API或构建您自己的系统。您在押注苹果不会破坏这一点,并希望他们会发布一个API,您可以用其替换这几行代码。

  1. KVO self.contentView.superview.layer.sublayer。 在init中执行此操作。这是UIScrollView的层。您不能KVO“子视图”。
  2. 当子视图改变时,在scrollview.subviews中找到删除确认视图。 这是在观察回调中完成的。
  3. 将该视图的大小加倍,并在其唯一子视图的左侧添加一个UIButton。 这也在观察回调中完成。删除确认视图的唯一子视图是删除按钮。
  4. (可选)UIButton事件应查找self.superview,直到找到UITableView,然后调用您创建的数据源或委托方法,例如tableView:commitCustomEditingStyle:forRowAtIndexPath:。您可以使用[tableView indexPathForCell:self]找到单元格的indexPath。

这还要求您实现标准的表视图编辑委托回调。

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

如果您能提供示例代码,我将非常高兴,谢谢
Guy Kahlon 2014年

做完了 它可能有一个或两个错误,但您有要点。
xtravar 2014年

1

有一个了不起的库SwipeCellKit,它应该得到更多的认可。我认为它比酷MGSwipeTableCell。后者不能完全复制Mail应用程序单元的行为,而SwipeCellKit可以。看一看


我尝试了一下SwipeCellKit,并留下了深刻的印象……直到得到其中一个例外,因为表视图更新之前的行数与更新之后的行数不相同+/-行的变化。问题是,我从未更改过数据集。因此,如果不担心,我不知道这是什么。因此,我决定不使用它,而只是使用了新的UITableViewDelegate方法。如果您需要更多自定义设置,则可以随时覆盖willBeginEditingRowAt: ....
horseshoe7

@ horseshoe7很奇怪。使用SwipeCellKit时,我从未遇到任何异常。毕竟,由于数据源的更改,单元格与这种异常会发生什么样的关系?
安德烈·切尔努卡

1

斯威夫特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
}

您的代码中的源代码视图是什么?是图标还是图像?
Saeed Rahmatolahi

1
@SaeedRahmatolahi sourceView是“显示操作的视图”。有关更多信息,请搜索“ UIContextualAction.Handler”。
马克·莫肯斯

0

这是一个简单的解决方案。它能够显示和隐藏UITableViewCell中的自定义UIView。显示逻辑包含在从UITableViewCell,BaseTableViewCell扩展的类内部。

BaseTableViewCell.h

#import <UIKit/UIKit.h>

@interface BaseTableViewCell : UITableViewCell

@property(nonatomic,strong)UIView* customView;

-(void)showCustomView;

-(void)hideCustomView;

@end

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

要获得此功能,只需从BaseTableViewCell扩展表视图单元格即可。

接下来,在实现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];
}

比添加两个滑动处理程序

- (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)];
    }
}

现在,在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;
}

当然,这种自定义UIView的加载方式仅用于此示例。根据需要进行管理。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.