【问题标题】:Custom edit view in UITableViewCell while swipe left. Objective-C or Swift向左滑动时在 UITableViewCell 中自定义编辑视图。 Objective-C 或 Swift
【发布时间】:2013-10-10 10:42:47
【问题描述】:

如何在 iOS7 UITableView 中使用 Objective C 制作自定义编辑视图,例如 Evernote 或 Apple Reminders 应用程序,同时向左滑动。 我曾尝试设置自定义editingAccessoryView,但这不起作用。

印象笔记编辑视图:

提醒编辑视图:

我当前的代码是

- (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 可能吗?

标签: ios objective-c swift uitableview swipe


【解决方案1】:

只需复制粘贴下面的代码!

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

【讨论】:

  • 为了让这些显示在我的案例中,我还需要实现表格视图的 dataSource 方法 - (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath * )索引路径;即使是这个方法的一个空实现也足以让按钮显示出来。
  • 我已将其转换为 Swift 并将其发布为下面的新答案。 stackoverflow.com/a/33748345/470879
  • @sohil 哪个图片?您的单元格中有图像视图吗?因此,您将在我编写的该方法中获得一个处理程序,当您点击任何按钮时将调用该处理程序
  • 这并不试图回答这个问题。提交者特别询问如何为滑动操作设置图标而不是文本。
  • @mattsson,在这方面问题很模糊,根本没有提到图标。它以 Evernote 和 Apple 提醒为例。 Apple Reminders 应用程序使用类似这个答案的标准实现。
【解决方案2】:

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

斯威夫特 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 以上版本

【讨论】:

  • swift 3 - 滑动似乎不起作用!没有显示任何选项!
  • @Maven,你实现了 canEditRowAtIndexPath 以返回 true 吗?
  • 我注意到你至少还需要写func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) { }editActionsForRowAtIndexPath 不会被调用
  • 如何添加图片而不是文字?
  • 当你创建一个引用self的闭包时会不会有内存泄漏?
【解决方案3】:

您可以使用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])
} 

【讨论】:

  • 你能澄清一下 func image() -> UIImage {} 我有错误,因为第一行没有可变的 bounds.size。同样在第 5 行中没有层变量......因为该函数不接受任何变量。谢谢
  • 这是一个 'UIView' 的扩展。所以 bounds 将是实例的边界。
  • 这太棒了,正是我想要的。很有创意的实现,喜欢!谢谢
  • 仅供参考,您不应该直接使用 init:deleteAction.backgroundColor = UIColor.init(patternImage: image) 应该是 deleteAction.backgroundColor = UIColor(patternImage: image)。很棒的答案虽然
  • 很好的答案。非常感谢!
【解决方案4】:

参考此链接: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];
  • 我已经用我的应用程序实现了这段代码,得到了这样的结果。您可以在滑动单元格中添加按钮数量。

    这里是实现的屏幕截图

    滑动单元格3个按钮后出现“更多”、“分享”、“删除”。

【讨论】:

    【解决方案5】:

    你可以试试这个,

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

    【讨论】:

      【解决方案6】:

      这同时支持标题和图片。

      对于 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 及以上版本怎么样?
      • @MajidBashir 我的应用程序不支持较旧的 iOS 版本。抱歉,帮不上忙。
      【解决方案7】:

      如果您想在进行滑动操作时仅使用文本,那么您可以使用 iOS 默认的滑动操作,但如果您想要图像和文本,则必须对其进行自定义。我找到了一个很好的教程和示例,可以解决这个问题。

      试用此存储库以获取自定义滑动单元格。您可以在此处添加多个选项。

      http://iosbucket.blogspot.in/2016/04/custom-swipe-table-view-cell_16.html

      https://github.com/pradeep7may/PKSwipeTableViewCell

      【讨论】:

        【解决方案8】:
          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]
            }
        

        【讨论】:

        • 如果您在答案本身中添加一些解释会很好,伙计。
        • 它会,但我相信在这种情况下代码不言自明(考虑到包含 cmets 并且函数名称非常自我解释)
        【解决方案9】:
        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 中还需要其他方法吗?
        • tableView(_, commit:, forRowAt:) { } 如上所述
        【解决方案10】:

        我认为,这不是使用基于 UIGestureRecognizer 的单元格的最佳方式。

        首先,您将没有任何使用 CoreGraphics 的选项。

        完美的解决方案,将UIResponder 或一个UIGestureRecognizer 用于整个表格视图。不是每个UITableViewCell。它会让你的应用卡住。

        【讨论】:

          【解决方案11】:

          在表格视图中的自定义单元格上创建一个视图,并将 PanGestureRecognizer 应用于单元格上的视图。将按钮添加到自定义单元格,当您在自定义单元格上滑动视图时,自定义单元格上的按钮将变为可见。

           UIGestureRecognizer* recognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handlePan:)];
              recognizer.delegate = self;
              [YourView addGestureRecognizer:recognizer];
          

          并在方法中处理视图上的平移

           if (recognizer.state == UIGestureRecognizerStateBegan) {
              // if the gesture has just started, record the current centre location
              _originalCenter = vwCell.center;
          }
          
          // 2
          if (recognizer.state == UIGestureRecognizerStateChanged) {
              // translate the center
              CGPoint translation = [recognizer translationInView:self];
              vwCell.center = CGPointMake(_originalCenter.x + translation.x, _originalCenter.y);
              // determine whether the item has been dragged far enough to initiate  / complete
              _OnDragRelease = vwCell.frame.origin.x < -vwCell.frame.size.width / 2;
          
          }
          
          // 3
          if (recognizer.state == UIGestureRecognizerStateEnded) {
              // the frame this cell would have had before being dragged
              CGPoint translation = [recognizer translationInView:self];
          
              if (_originalCenter.x+translation.x<22) {
                    vwCell.center = CGPointMake(22, _originalCenter.y);
                  IsvwRelease=YES;
          
              }
              CGRect originalFrame = CGRectMake(0, vwCell.frame.origin.y,
                                                vwCell.bounds.size.width, vwCell.bounds.size.height);
              if (!_deleteOnDragRelease) {
                  // if the item is not being dragged far enough , snap back to the original location
                  [UIView animateWithDuration:0.2
                                   animations:^{
                                       vwCell.frame = originalFrame;
                                   }
                   ];
              }
          }
          

          【讨论】:

          • 你有例子吗
          【解决方案12】:
          - (UISwipeActionsConfiguration *)tableView:(UITableView *)tableView trailingSwipeActionsConfigurationForRowAtIndexPath:(NSIndexPath *)indexPath
          {
              UIContextualAction *delete = [UIContextualAction contextualActionWithStyle:UIContextualActionStyleNormal title:nil handler:^(UIContextualAction * _Nonnull action, __kindof UIView * _Nonnull sourceView, void (^ _Nonnull completionHandler)(BOOL)) {
                  
                  // your code...
                  
              }];
              delete.image  = [UIImage systemImageNamed:@"trash"];
              
              UISwipeActionsConfiguration *actions = [UISwipeActionsConfiguration configurationWithActions:[[NSArray alloc] initWithObjects:delete, nil]];
              
              return actions;
          }
          

          【讨论】:

          • 向右滑动如何自动触发delete???
          • 你应该使用 - (UISwipeActionsConfiguration *)tableView:(UITableView *)tableView trailingSwipeActionsConfigurationForRowAtIndexPath:(NSIndexPath *)indexPath。对于 UIContextualAction 动作,请查看:stackoverflow.com/questions/5560602/…
          • 这就是您的示例。而且您的参考与我的问题无关:如何在滑动过程结束时触发delete 操作(无需单击按钮)
          • 我明白了。请看这里:developer.apple.com/documentation/uikit/…
          • 我会改写。如何通过滑动屏幕的 1/3 来触发 delete 或任何类似 WhatsApp swipe to reply to message 的操作并触发操作?
          【解决方案13】:

          您应该使用MGSwipeTableCell 自定义滑动单元格。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2012-02-24
            • 1970-01-01
            • 2020-05-22
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多