You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Swift4自定义UITableView滑动删除按钮方案求助(适配iOS11)

Swift 4+ 自定义 UITableView 滑动删除按钮的可行方案

Hey there! I totally get your frustration—Apple overhauled the internal view hierarchy of UITableView's swipe actions starting in iOS 11, which broke that old subview-traversing hack you were using. Let's ditch the unstable private API approach and use Apple's officially supported methods to customize swipe delete buttons reliably in Swift 4 and later, including adding shadow effects to image-based buttons.

Why Your Old Code Stopped Working

In iOS 11, Apple replaced the old UITableViewCellActionButton structure with a new system built around UIContextualAction. This means your loop to find private subviews will no longer locate the button elements, and even if it did, relying on private view classes is always risky (Apple can change them at any time without warning).

Solution 1: Use UIContextualAction (iOS 11+, Official Approach)

This is the recommended way to create custom swipe actions. It's stable, flexible, and lets you customize colors, images, and even add shadows without hacking subviews.

Basic Custom Text Delete Button

Here's how to replicate your original text-based button customization:

func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? {
    // Create the delete action
    let deleteAction = UIContextualAction(style: .destructive, title: "删除") { [weak self] (action, view, completionHandler) in
        // Handle your delete logic here
        self?.yourDataSourceArray.remove(at: indexPath.row)
        tableView.deleteRows(at: [indexPath], with: .automatic)
        completionHandler(true)
    }
    
    // Customize button background color
    deleteAction.backgroundColor = .white
    
    // Customize text color (access the titleLabel via valueForKey)
    if let titleLabel = deleteAction.value(forKey: "titleLabel") as? UILabel {
        titleLabel.textColor = SharedInstance.hexStringToUIColor(hex: Color.TodayActivityColor.Type_LEAVE)
    }
    
    // Configure the swipe actions
    let configuration = UISwipeActionsConfiguration(actions: [deleteAction])
    // Allow full swipe to trigger the first action (optional)
    configuration.performsFirstActionWithFullSwipe = true
    return configuration
}

Image-Based Button with Shadow

If you want to use an image instead of text and add a shadow, you can directly access the action's imageView to modify its layer properties:

func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? {
    let deleteAction = UIContextualAction(style: .destructive, title: "") { (action, view, completionHandler) in
        // Handle delete logic
        completionHandler(true)
    }
    
    // Set your image (use alwaysTemplate to match tint color)
    deleteAction.image = UIImage(named: "delete-icon")?.withRenderingMode(.alwaysTemplate)
    deleteAction.backgroundColor = .white
    deleteAction.imageView?.tintColor = SharedInstance.hexStringToUIColor(hex: Color.TodayActivityColor.Type_LEAVE)
    
    // Add shadow to the image view
    deleteAction.imageView?.layer.shadowColor = UIColor.black.cgColor
    deleteAction.imageView?.layer.shadowOffset = CGSize(width: 2, height: 2)
    deleteAction.imageView?.layer.shadowOpacity = 0.5
    deleteAction.imageView?.layer.shadowRadius = 2
    
    let configuration = UISwipeActionsConfiguration(actions: [deleteAction])
    configuration.performsFirstActionWithFullSwipe = true
    return configuration
}

Solution 2: Fully Custom Button (For iOS 11-12 Compatibility)

If you need even more customization (like combining image + text) and need to support iOS 11-12 (where UIContextualAction doesn't have a backgroundView property), you can convert a custom UIView into an image and use it as the action's background:

private func createCustomDeleteAction(tableView: UITableView) -> UIContextualAction {
    let action = UIContextualAction(style: .destructive, title: "") { (action, view, completion) in
        // Handle your action logic here
        completion(true)
    }
    
    // Create your custom button view
    let customButtonView = UIView(frame: CGRect(x: 0, y: 0, width: 80, height: tableView.rowHeight))
    customButtonView.backgroundColor = .white
    
    // Add image with shadow
    let deleteImageView = UIImageView(image: UIImage(named: "delete-icon")?.withRenderingMode(.alwaysTemplate))
    deleteImageView.tintColor = SharedInstance.hexStringToUIColor(hex: Color.TodayActivityColor.Type_LEAVE)
    deleteImageView.frame = CGRect(x: 20, y: (customButtonView.frame.height - 24)/2, width: 24, height: 24)
    
    // Configure shadow
    deleteImageView.layer.shadowColor = UIColor.black.cgColor
    deleteImageView.layer.shadowOffset = CGSize(width: 1, height: 1)
    deleteImageView.layer.shadowOpacity = 0.6
    deleteImageView.layer.shadowRadius = 1.5
    customButtonView.addSubview(deleteImageView)
    
    // Add text label (optional)
    let deleteLabel = UILabel(frame: CGRect(x: 50, y: 0, width: 30, height: customButtonView.frame.height))
    deleteLabel.text = "删除"
    deleteLabel.textColor = SharedInstance.hexStringToUIColor(hex: Color.TodayActivityColor.Type_LEAVE)
    deleteLabel.font = UIFont.systemFont(ofSize: 14)
    customButtonView.addSubview(deleteLabel)
    
    // Convert custom view to image
    UIGraphicsBeginImageContextWithOptions(customButtonView.bounds.size, false, UIScreen.main.scale)
    customButtonView.drawHierarchy(in: customButtonView.bounds, afterScreenUpdates: true)
    guard let customButtonImage = UIGraphicsGetImageFromCurrentImageContext() else {
        UIGraphicsEndImageContext()
        return action
    }
    UIGraphicsEndImageContext()
    
    // Set the image as the action's background
    action.backgroundColor = UIColor(patternImage: customButtonImage)
    return action
}

// Use it in your swipe configuration method
func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? {
    let customDeleteAction = createCustomDeleteAction(tableView: tableView)
    let configuration = UISwipeActionsConfiguration(actions: [customDeleteAction])
    configuration.performsFirstActionWithFullSwipe = true
    return configuration
}

Key Takeaways

  • Avoid private view hacks: Apple changes internal view structures with iOS updates, so these methods will break. Stick to official APIs like UIContextualAction.
  • UIContextualAction is flexible: It supports text, images, and custom colors out of the box.
  • Shadows are easy to add: For image actions, directly modify the imageView's layer properties. For full custom views, build the view with shadows first, then convert it to an image.

内容的提问来源于stack exchange,提问作者Vishal N

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 06:56:46