UITableViewCell左滑时自定义编辑视图。使用Objective-C或Swift实现。

81

如何在 iOS7 UITableView 中使用 Objective C 制作类似 Evernote 或 Apple Reminders 应用程序的自定义编辑视图,实现左滑功能。

我尝试设置自定义的 editingAccessoryView,但没有成功。

Evernote 编辑视图:

enter image description here Reminders 编辑视图:

enter image description here

我的当前代码是

- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath {
    return YES;
}

- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
    if (editingStyle == UITableViewCellEditingStyleDelete) {
        NSLog(@"delete");
    }
}

我已经尝试使用以下方式解决问题:(UITableViewController.h)

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    //make cell

    UIView *view = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)];
    [view setBackgroundColor:[UIColor greenColor]];
    //add Buttons to view

    cell.editingAccessoryView = view;

    return cell;
}

同样适用于:(UITableViewCell)

- (void)willTransitionToState:(UITableViewCellStateMask)state;
- (void)setEditing:(BOOL)editing animated:(BOOL)animated;
- (UIView*)editingAccessoryView;

我们能否固定侧滑按钮的高度?例如:我的单元格高度为150,我只想显示50.0f的按钮。这可行吗? - Suhas Arvind Patil
13个回答

100

只需复制粘贴以下代码!

-(NSArray *)tableView:(UITableView *)tableView editActionsForRowAtIndexPath:(NSIndexPath *)indexPath {
    
    UITableViewRowAction *editAction = [UITableViewRowAction rowActionWithStyle:UITableViewRowActionStyleNormal title:@"Clona" handler:^(UITableViewRowAction *action, NSIndexPath *indexPath){
       //insert your editAction here
    }];
    editAction.backgroundColor = [UIColor blueColor];
    
    UITableViewRowAction *deleteAction = [UITableViewRowAction rowActionWithStyle:UITableViewRowActionStyleNormal title:@"Delete"  handler:^(UITableViewRowAction *action, NSIndexPath *indexPath){
       //insert your deleteAction here
    }];
    deleteAction.backgroundColor = [UIColor redColor];
    return @[deleteAction,editAction];
}

8
为了让这些按钮在我的情况下显示出来,我还需要实现表格视图的dataSource方法 - (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath; 即使是这个方法的空实现也足以让按钮显示出来。 - u2Fan
我已将其转换为Swift并在下面发布了一个新答案。https://dev59.com/72Ik5IYBdhLWcg3wkezM#33748345 - MattyG
@sohil 哪张图片?你的单元格中有一个 ImageView 吗? 所以,在我编写的方法中,当您点击任何按钮时,将会获得一个处理程序。 - Kartik 123
1
这并没有试图回答问题。提交者特别询问如何为滑动操作设置图标而不是文本。 - mattsson
@mattsson,就这方面而言,问题不够明确,并且没有提到图标。它提到Evernote和Apple提醒事项作为示例。Apple提醒事项应用程序使用类似于此答案的标准实现。 - MattyG
@MattyG 你说得完全正确,我不知道当时自己在想什么。 - mattsson

45

Swift 3

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

    let editAction = UITableViewRowAction(style: .normal, title: "Edit") { (rowAction, indexPath) in
        //TODO: edit the row at indexPath here
    }
    editAction.backgroundColor = .blue

    let deleteAction = UITableViewRowAction(style: .normal, title: "Delete") { (rowAction, indexPath) in
        //TODO: Delete the row at indexPath here
    }
    deleteAction.backgroundColor = .red

    return [editAction,deleteAction]
}

Swift 2.1

func tableView(tableView: UITableView, editActionsForRowAtIndexPath indexPath: NSIndexPath) -> [UITableViewRowAction]? {
    let editAction = UITableViewRowAction(style: .Normal, title: "Edit") { (rowAction:UITableViewRowAction, indexPath:NSIndexPath) -> Void in
        //TODO: edit the row at indexPath here
    }
    editAction.backgroundColor = UIColor.blueColor()

    let deleteAction = UITableViewRowAction(style: .Normal, title: "Delete") { (rowAction:UITableViewRowAction, indexPath:NSIndexPath) -> Void in
        //TODO: Delete the row at indexPath here
    }
    deleteAction.backgroundColor = UIColor.redColor()

    return [editAction,deleteAction]
}

注意:适用于iOS 8及以上版本


@Maven,你是否已经实现了canEditRowAtIndexPath并返回true? - MattyG
1
我注意到你至少需要编写以下代码才能调用editActionsForRowAtIndexPath函数: func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) { } - reojased
1
如何添加图片而不是文本? - Jan
当创建一个引用self的闭包时,是否会出现内存泄漏问题? - Legonaftik
同时,设置背景颜色并不是必要的。对于破坏性操作,请使用“.destructive”样式,对于其他操作,可以使用“.normal”样式,在这种情况下,将使用一些系统默认值。实际上,“destructive”并不完全是纯粹的“.red”,它是另外一种颜色。 - mojuba
显示剩余3条评论

30

您可以使用UITableViewRowActionbackgroundColor属性来设置自定义图像或视图。关键是使用UIColor(patternImage:)方法。

基本上,UITableViewRowAction区域的宽度由其标题决定,因此您可以找到标题(或空格)的确切长度,并使用patternImage方法设置图像的确切大小。

为了实现这一点,我创建了一个UIView的扩展方法。

func image() -> UIImage {
    UIGraphicsBeginImageContextWithOptions(bounds.size, isOpaque, 0)
    guard let context = UIGraphicsGetCurrentContext() else {
        return UIImage()
    }
    layer.render(in: context)
    let image = UIGraphicsGetImageFromCurrentImageContext()
    UIGraphicsEndImageContext()
    return image!
}

制作一个带有空格并且长度精确的字符串,

fileprivate func whitespaceString(font: UIFont = UIFont.systemFont(ofSize: 15), width: CGFloat) -> String {
    let kPadding: CGFloat = 20
    let mutable = NSMutableString(string: "")
    let attribute = [NSFontAttributeName: font]
    while mutable.size(attributes: attribute).width < width - (2 * kPadding) {
        mutable.append(" ")
    }
    return mutable as String
}

现在,您可以创建UITableViewRowAction

func tableView(_ tableView: UITableView, editActionsForRowAt indexPath: IndexPath) -> [UITableViewRowAction]? {
    let whitespace = whitespaceString(width: kCellActionWidth)
    let deleteAction = UITableViewRowAction(style: .`default`, title: whitespace) { (action, indexPath) in
        // do whatever you want
    }

    // create a color from patter image and set the color as a background color of action
    let kActionImageSize: CGFloat = 34
    let view = UIView(frame: CGRect(x: 0, y: 0, width: kCellActionWidth, height: kCellHeight))
    view.backgroundColor = UIColor.white
    let imageView = UIImageView(frame: CGRect(x: (kCellActionWidth - kActionImageSize) / 2,
                                              y: (kCellHeight - kActionImageSize) / 2,
                                              width: 34,
                                              height: 34))
    imageView.image = UIImage(named: "x")
    view.addSubview(imageView)
    let image = view.image()

    deleteAction.backgroundColor = UIColor(patternImage: image)

    return [deleteAction]
}

结果将会看起来像这样。

输入图像描述

另一种方法是导入拥有所需图片的自定义字体,并使用UIButton.appearance。然而,这将影响其他按钮,除非您手动设置其他按钮的字体。

从iOS 11开始,它将显示此消息[TableView] Setting a pattern color as backgroundColor of UITableViewRowAction is no longer supported.。目前仍在工作,但在未来更新中将不再工作。

==========================================

对于iOS 11及以上版本,您可以使用:

func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? {
  let deleteAction = UIContextualAction(style: .normal, title: "Delete") { (action, view, completion) in
    // Perform your action here
      completion(true)
  }

  let muteAction = UIContextualAction(style: .normal, title: "Mute") { (action, view, completion) in
    // Perform your action here
    completion(true)
  }

  deleteAction.image = UIImage(named: "icon.png")
  deleteAction.backgroundColor = UIColor.red
  return UISwipeActionsConfiguration(actions: [deleteAction, muteAction])
} 

1
能否澄清一下 func image() -> UIImage {} 这个函数?我有错误,因为第一行没有变量bounds.size。同样地,在第五行也没有layer变量……由于该函数不接受任何变量。谢谢。 - Jerland2
1
这是一个'UIView'的扩展。因此,bounds将是实例的边界。 - Ryan
这太棒了,正是我在寻找的。非常有创意的实现方式,我喜欢!谢谢。 - Jerland2
1
顺便提一下,你不应该直接使用 init:deleteAction.backgroundColor = UIColor.init(patternImage: image) 应该是 deleteAction.backgroundColor = UIColor(patternImage: image)。不过回答很棒。 - TheCodingArt
非常好的回答。非常感谢! - krlbsk
这个解决方案正是我所需要的,但不幸的是,它只适用于非常小的图像,因为“空白”填充不再增加大小超过最小宽度。我尝试了一个不间断空格\u{00a0},但它仍然始终是最小尺寸。我想知道是否有另一个空白的Unicode字符实际上占据空间? - Travis M.

13

你可以尝试这个:

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

    let backView = UIView(frame: CGRect(x: 0, y: 0, width: 80, height: 80))
    backView.backgroundColor = #colorLiteral(red: 0.933103919, green: 0.08461549133, blue: 0.0839477703, alpha: 1)

    let myImage = UIImageView(frame: CGRect(x: 30, y: backView.frame.size.height/2-14, width: 16, height: 16))
    myImage.image = #imageLiteral(resourceName: "rubbish-bin")
    backView.addSubview(myImage)

    let label = UILabel(frame: CGRect(x: 0, y: myImage.frame.origin.y+14, width: 80, height: 25))
    label.text = "Remove"
    label.textAlignment = .center
    label.textColor = UIColor.white
    label.font = UIFont(name: label.font.fontName, size: 14)
    backView.addSubview(label)

    let imgSize: CGSize = tableView.frame.size
    UIGraphicsBeginImageContextWithOptions(imgSize, false, UIScreen.main.scale)
    let context = UIGraphicsGetCurrentContext()
    backView.layer.render(in: context!)
    let newImage: UIImage = UIGraphicsGetImageFromCurrentImageContext()!
    UIGraphicsEndImageContext()

    let delete = UITableViewRowAction(style: .destructive, title: "           ") { (action, indexPath) in
        print("Delete")
    }

    delete.backgroundColor = UIColor(patternImage: newImage)

    return [delete, share]
}

12

请参考此链接:https://github.com/TeehanLax/UITableViewCell-Swipe-for-Options

使用多个按钮自定义您的uitableviewcell。

 UIScrollView *scrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 0, CGRectGetWidth(self.bounds), CGRectGetHeight(self.bounds))];
scrollView.contentSize = CGSizeMake(CGRectGetWidth(self.bounds) + kCatchWidth, CGRectGetHeight(self.bounds));
scrollView.delegate = self;
scrollView.showsHorizontalScrollIndicator = NO;

[self.contentView addSubview:scrollView];
self.scrollView = scrollView;

UIView *scrollViewButtonView = [[UIView alloc] initWithFrame:CGRectMake(CGRectGetWidth(self.bounds) - kCatchWidth, 0, kCatchWidth, CGRectGetHeight(self.bounds))];
self.scrollViewButtonView = scrollViewButtonView;
[self.scrollView addSubview:scrollViewButtonView];

// Set up our two buttons
UIButton *moreButton = [UIButton buttonWithType:UIButtonTypeCustom];
moreButton.backgroundColor = [UIColor colorWithRed:0.78f green:0.78f blue:0.8f alpha:1.0f];
moreButton.frame = CGRectMake(0, 0, kCatchWidth / 3.0f, CGRectGetHeight(self.bounds));
[moreButton setTitle:@"More" forState:UIControlStateNormal];
[moreButton setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
[moreButton addTarget:self action:@selector(userPressedMoreButton:) forControlEvents:UIControlEventTouchUpInside];

[self.scrollViewButtonView addSubview:moreButton];

UIButton *shareButton = [UIButton buttonWithType:UIButtonTypeCustom];
shareButton.backgroundColor = [UIColor colorWithRed:0.0f green:0.0f blue:1.0f alpha:1.0f];
shareButton.frame = CGRectMake(kCatchWidth / 3.0f, 0, kCatchWidth / 3.0f, CGRectGetHeight(self.bounds));
[shareButton setTitle:@"Share" forState:UIControlStateNormal];
[shareButton setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
[shareButton addTarget:self action:@selector(userPressedMoreButton:) forControlEvents:UIControlEventTouchUpInside];
[self.scrollViewButtonView addSubview:shareButton];

UIButton *deleteButton = [UIButton buttonWithType:UIButtonTypeCustom];
deleteButton.backgroundColor = [UIColor colorWithRed:1.0f green:0.231f blue:0.188f alpha:1.0f];
deleteButton.frame = CGRectMake(kCatchWidth / 3.0f+kCatchWidth / 3.0f, 0, kCatchWidth / 3.0f, CGRectGetHeight(self.bounds));
[deleteButton setTitle:@"Delete" forState:UIControlStateNormal];
[deleteButton setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
[deleteButton addTarget:self action:@selector(userPressedDeleteButton:) forControlEvents:UIControlEventTouchUpInside];
[self.scrollViewButtonView addSubview:deleteButton];

UIView *scrollViewContentView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, CGRectGetWidth(self.bounds), CGRectGetHeight(self.bounds))];
scrollViewContentView.backgroundColor = [UIColor whiteColor];
[self.scrollView addSubview:scrollViewContentView];
self.scrollViewContentView = scrollViewContentView;

UILabel *scrollViewLabel = [[UILabel alloc] initWithFrame:CGRectInset(self.scrollViewContentView.bounds, 10, 0)];
self.scrollViewLabel = scrollViewLabel;
[self.scrollViewContentView addSubview:scrollViewLabel];
  • 我已经在我的应用程序中使用了这段代码,并得到了这样的结果。您可以在滑动单元格中添加多个按钮。

    以下是实现


8

这支持标题和图片。

iOS 11及以上版本:

func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? {
    let action = UIContextualAction(
        style: .normal,
        title: "My Title",
        handler: { (action, view, completion) in
            //do what you want here
            completion(true)
    })

    action.image = UIImage(named: "My Image")
    action.backgroundColor = .red
    let configuration = UISwipeActionsConfiguration(actions: [action])
    configuration.performsFirstActionWithFullSwipe = false
    return configuration
}

同时,leadingSwipeActions也有类似的方法可用。
来源: https://developer.apple.com/videos/play/wwdc2017/201/(大约在16分钟处讲解此内容) https://developer.apple.com/videos/play/wwdc2017/204/(大约在23分钟处讲解此内容)

从iOS 9及以上版本呢? - Majid Bashir
@MajidBashir 我的应用程序不支持旧版iOS。很抱歉,无法提供帮助。 - Vinay Kharb

4

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

        // action one
        let editAction = UITableViewRowAction(style: .default, title: "Edit", handler: { (action, indexPath) in
            print("Edit tapped")

            self.myArray.add(indexPath.row)
        })
        editAction.backgroundColor = UIColor.blue

        // action two
        let deleteAction = UITableViewRowAction(style: .default, title: "Delete", handler: { (action, indexPath) in
            print("Delete tapped")

            self.myArray.removeObject(at: indexPath.row)
            self.myTableView.deleteRows(at: [indexPath], with: UITableViewRowAnimation.automatic)

        })
        deleteAction.backgroundColor = UIColor.red
        // action three
        let shareAction = UITableViewRowAction(style: .default, title: "Share", handler: { (action , indexPath)in
             print("Share Tapped")
        })
        shareAction.backgroundColor = UIColor .green
        return [editAction, deleteAction, shareAction]
    }

如果你在答案中加入一些解释的话,那就太好了,伙计。 - Harshith Rai
这样做是可以的,但我认为在这种情况下代码本身已经很清晰明了(考虑到注释已经包含在内,函数名也相当自解释)。 - James Wolfe

2
override func tableView(tableView: UITableView, editActionsForRowAtIndexPath indexPath: NSIndexPath) -> [UITableViewRowAction]? {
    let delete = UITableViewRowAction(style: .Destructive, title: "Delete") { (action, indexPath) in
        // delete item at indexPath
    }

    let share = UITableViewRowAction(style: .Normal, title: "Disable") { (action, indexPath) in
    // share item at indexPath
    }

    share.backgroundColor = UIColor.blueColor()

    return [delete, share]
}

上述代码展示了如何在滑动行时创建自定义按钮。

但是在Swift3中,滑动没有任何效果!!是否需要其他方法? - mythicalcoder
tableView(_, commit:, forRowAt:) { } - nyxee

0

我认为,使用基于UIGestureRecognizer的单元格并不是最好的方法。

首先,您将无法使用CoreGraphics。

完美的解决方案是为整个表视图使用UIResponder或一个UIGestureRecognizer,而不是为每个UITableViewCell使用。这会使您的应用程序卡住。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接