更改 UITableView 中移动单元格的默认图标

Change default icon for moving cells in UITableView

提问人:devgeek 提问时间:12/22/2011 最后编辑:Baby Grootdevgeek 更新时间:10/13/2023 访问量:27743

问:

我需要更改UITableView中移动单元格的默认图标。

这个:

enter image description here

可能吗?

iOS UITableView

评论


答:

65赞 Ashley Mills 12/22/2011 #1

这是一个非常棘手的解决方案,可能不会长期有效,但可能会给你一个起点。重新排序控件是一个 ,但这是一个私有类,因此您不能直接访问它。但是,您可以查看子视图的层次结构并找到其 imageView。UITableViewCellReorderControl

您可以通过子类化和重写其方法来执行此操作,如下所示:UITableViewCellsetEditing:animated:

- (void) setEditing:(BOOL)editing animated:(BOOL)animated
{
    [super setEditing: editing animated: YES];

    if (editing) {

        for (UIView * view in self.subviews) {
            if ([NSStringFromClass([view class]) rangeOfString: @"Reorder"].location != NSNotFound) {
                for (UIView * subview in view.subviews) {
                    if ([subview isKindOfClass: [UIImageView class]]) {
                        ((UIImageView *)subview).image = [UIImage imageNamed: @"yourimage.png"];
                    }
                }
            }
        }
    }   
}

或者在 Swift 中

override func setEditing(_ editing: Bool, animated: Bool) {
    super.setEditing(editing, animated: animated)

    if editing {
        for view in subviews where view.description.contains("Reorder") {
            for case let subview as UIImageView in view.subviews {
                subview.image = UIImage(named: "yourimage.png")
            }
        }
    }
}

不过要注意......这可能不是一个长期的解决方案,因为苹果可以随时改变视图层次结构。

评论

1赞 RealCasually 7/19/2012
有没有人找到一种方法来做到这一点,而不会冒着 App Store 被拒绝的风险?这是一种非常脆弱和笨拙的处理方式。
0赞 arlomedia 9/1/2012
如果这失败了,我认为最有可能的结果是我们将再次看到默认图标,这还不错。我还尝试了 Phil 的方法,如果失败,它会在错误的位置给我自定义图标,默认图标也可见。因此,我将抓住这种方法的机会。
0赞 arlomedia 9/1/2012
我的自定义图标与默认图标的大小不同,因此我还将找到的子视图的框架设置为我的 UIImage 的框架。我还扩展了它以自定义出现在表格单元格左侧的插入和删除图标。好!
2赞 arlomedia 9/12/2014
这同样适用于iOS 8。因此,您可以在 iOS 5、6 和 8 以及 OgreSwamp 对 iOS 7 的答案上使用它。唯一的区别是视图层次结构在 iOS 7 中开始更深一级。
1赞 Ashley Mills 1/28/2015
@Brian - 这意味着在代码中引用私有类名,这会使应用程序面临被拒绝的风险。我的疯狂是有方法的!
4赞 Padavan 12/22/2011 #2
- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
{
    for (UIControl *control in cell.subviews)
    {       
        if ([control isMemberOfClass:NSClassFromString(@"UITableViewCellReorderControl")] && [control.subviews count] > 0)
        {           
            for (UIControl *someObj in control.subviews)
            {
                if ([someObj isMemberOfClass:[UIImageView class]])
                {
                    UIImage *img = [UIImage imageNamed:@"reorder_icon.png"];
                    ((UIImageView*)someObj).frame = CGRectMake(0.0, 0.0, 43.0, 43.0);
                    ((UIImageView*)someObj).image = img;
                }
            }
        }
    }   
}

评论

1赞 Ashley Mills 12/22/2011
由于显式使用 UITableViewCellReorderControl 类名,这可能会导致应用商店审批过程失败。
0赞 Padavan 12/23/2011
@Ashley Mills 我们在应用程序中使用它,该应用程序现在在 AppStore 中。
1赞 Ashley Mills 12/23/2011
对你有好处...但我不会自己冒险!
0赞 Lukasz 8/31/2012
自 iOS 5 以来,这不再起作用(至少是完美的)。当您将单元格置于编辑模式时,不会立即调用 willDisplayCell,而是在完成拖动时调用它。这意味着默认的重新排序控件最初显示时不会更改,直到您触摸并将其移动到 UITableView 中的其他位置。仅当您放下它时,它才会被刷新(只有这样,您的控件才会可见)。
0赞 Trenskow 12/18/2012
从技术上讲,这不是使用未记录的 API。您正在使用公共 API 遍历视图层次结构,并根据需要进行更改。这与使用未记录的 API 之间的区别在于,如果 Apple 更改视图层次结构,这不会使应用程序崩溃,它只是将原始图像放回原处。据我了解,这是不允许的未记录的 API,因为一旦 Apple 更改它们,它们就会使应用程序崩溃。我保证这段代码是安全的。
0赞 Phil 3/29/2012 #3

您也可以简单地将自己的自定义重新排序视图添加到单元格内的所有其他视图之上。

您所要做的就是确保此自定义视图始终高于其他视图,可以签入[UITableViewDelegate tableView: willDisplayCell: forRowAtIndexPath: indexPath:].

为了允许标准重新排序控件交互,自定义视图必须将其设置为 NO。userInteractionEnabled

根据单元格的外观,您可能需要或多或少复杂的自定义重新排序视图(以模拟单元格背景)。

评论

0赞 PEZ 4/21/2012
Phil,您将如何使自定义重新排序视图作为内置视图工作?似乎在userInteractionEnabled注释中有一个线索,但我不明白。=)
0赞 arlomedia 9/1/2012
我尝试了这种方法,将自定义图标的 UIImageView 添加到单元格的 contentView 中,然后将其框架移动到适当的位置,它奏效了。但是,这需要一些硬编码的 xy 值,这些值在某些情况下似乎可能不准确,这将导致重复的图标。无论如何,为了回答 PEZ 的问题,UIImageView 默认禁用其用户交互,因此我的自定义图标不会干扰单元格的默认功能。
5赞 OgreSwamp 11/6/2013 #4

Ashley Mills的更新解决方案(适用于iOS 7.x)

if (editing) {
    UIView *scrollView = self.subviews[0];
    for (UIView * view in scrollView.subviews) {
        NSLog(@"Class: %@", NSStringFromClass([view class]));
        if ([NSStringFromClass([view class]) rangeOfString: @"Reorder"].location != NSNotFound) {
            for (UIView * subview in view.subviews) {
                if ([subview isKindOfClass: [UIImageView class]]) {
                    ((UIImageView *)subview).image = [UIImage imageNamed: @"moveCellIcon"];
                }
            }
        }
    }
}
14赞 Rick Morgan 9/6/2014 #5

Ashley Mills 的回答在提供时非常出色,但正如其他人在评论中指出的那样,视图层次结构因 iOS 版本而异。为了正确找到重新排序控件,我使用了一种遍历整个视图层次结构的方法;希望这将使该方法有机会继续工作,即使 Apple 更改了视图层次结构。

下面是我用来查找重新排序控件的代码:

-(UIView *) findReorderView:(UIView *) view
{
    UIView *reorderView = nil;
    for (UIView *subview in view.subviews)
    {
        if ([[[subview class] description] rangeOfString:@"Reorder"].location != NSNotFound)
        {
            reorderView = subview;
            break;
        }
        else
        {
            reorderView = [self findReorderView:subview];
            if (reorderView != nil)
            {
                break;
            }
        }
    }
    return reorderView;
}

这是我用来覆盖子类中方法的代码:-(void) setEditing:animated:

-(void) setEditing:(BOOL)editing animated:(BOOL)animated
{
    [super setEditing:editing animated:animated];
    if (editing)
    {
        // I'm assuming the findReorderView method noted above is either
        // in the code for your subclassed UITableViewCell, or defined
        // in a category for UIView somewhere
        UIView *reorderView = [self findReorderView:self];
        if (reorderView)
        {
            // I'm setting the background color of the control
            // to match my cell's background color
            // you might need to do this if you override the
            // default background color for the cell
            reorderView.backgroundColor = self.contentView.backgroundColor;
            for (UIView *sv in reorderView.subviews)
            {
                // now we find the UIImageView for the reorder control
                if ([sv isKindOfClass:[UIImageView class]])
                {
                    // and replace it with the image we want
                    ((UIImageView *)sv).image = [UIImage imageNamed:@"yourImage.png"];
                    // note:  I have had to manually change the image's frame
                    // size to get it to display correctly
                    // also, for me the origin of the frame doesn't seem to
                    // matter, because the reorder control will center it
                    sv.frame = CGRectMake(0, 0, 48.0, 48.0);
                }
            }
        }
    }
}

评论

2赞 Ashley Mills 9/25/2014
太棒了,一个 3 年前的问题仍然得到答案!有一个+1!
4赞 Hubert Wang 10/28/2014 #6

我使用 editingAccessoryView 替换重新排序图标。

  1. 创建 UITableViewCell 的子类。
  2. 覆盖 setEditing。只需隐藏重新排序控件,并将 editingAccessoryView 设置为包含重新排序图像的 uiimageview。
 - (void) setEditing:(BOOL)editing animated:(BOOL)animated
{

    [super setEditing: editing animated: YES];

    self.showsReorderControl = NO;

    self.editingAccessoryView = editing ? [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"yourReorderIcon"]] : nil;

}

如果您不使用编辑附件视图,这可能是一个不错的选择。

评论

2赞 jhelzer 8/1/2017
虽然这可以显示自定义 editingAccessoryView,但我实际上无法通过按下并拖动来移动单元格。
1赞 Hubert Wang 8/2/2017
嗯。。。我不记得我在哪里用过这个......当我找到它时,我会更新我的答案。
7赞 Andrey Gordeev 5/26/2016 #7

Rick 回答的 Swift 版本,略有改进:

override func setEditing(editing: Bool, animated: Bool) {
    super.setEditing(editing, animated: animated)

    if editing {
        if let reorderView = findReorderViewInView(self), 
            imageView = reorderView.subviews.filter({ $0 is UIImageView }).first as? UIImageView {
            imageView.image = UIImage(named: "yourImage")
        }
    }
}

func findReorderViewInView(view: UIView) -> UIView? {
    for subview in view.subviews {
        if String(subview).rangeOfString("Reorder") != nil {
            return subview
        }
        else {
            findReorderViewInView(subview)
        }
    }
    return nil
}
10赞 Grzegorz R. Kulesza 9/13/2018 #8

斯威夫特 4

   // Change default icon (hamburger) for moving cells in UITableView
    func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
        let imageView = cell.subviews.first(where: { $0.description.contains("Reorder") })?.subviews.first(where: { $0 is UIImageView }) as? UIImageView

        imageView?.image = #imageLiteral(resourceName: "new_hamburger_icon") // give here your's new image
        imageView?.contentMode = .center

        imageView?.frame.size.width = cell.bounds.height
        imageView?.frame.size.height = cell.bounds.height
    }

评论

3赞 Evan Kaminsky 1/27/2019
这是 2019 年的解决方案
0赞 Meet 3/8/2019
不适用于 Defatult Cell。我们是否需要对 UITableViewCell 进行子类化
1赞 meow2x 4/9/2019 #9

我在 iOS 12 上使用 swift 4.2 做到了这一点

我希望这能有所帮助:

    func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
        for view in cell.subviews {
            if view.self.description.contains("UITableViewCellReorderControl") {
                for sv in view.subviews {
                    if (sv is UIImageView) {
                        (sv as? UIImageView)?.image = UIImage(named: "your_image")
                        (sv as? UIImageView)?.contentMode = .center
                        sv.frame = CGRect(x: 0, y: 0, width: 25, height: 25)
                    }
                }
            }
        }
    }
1赞 bqlin 7/6/2019 #10

调试 UITableViewCell 后,可以使用 UITableViewCell 子类中的 KVC 来更改它。

// key
static NSString * const kReorderControlImageKey = @"reorderControlImage";

// setting when cellForRow calling
UIImage *customImage;
[self setValue:customImage forKeyPath:kReorderControlImageKey];

// to prevent crash
- (void)setValue:(id)value forUndefinedKey:(NSString *)key {
    if ([key isEqualToString:kReorderControlImageKey]) return;
    else [super setValue:value forUndefinedKey:key];
}
2赞 nickcin 11/5/2019 #11

我无法获得任何其他答案来为我工作,但我找到了解决方案。

Grzegorz R. Kulesza 的回答几乎对我有用,但我不得不做出一些改变。

这适用于 Swift 5iOS 13

// Change default reorder icon in UITableViewCell
func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
    let imageView = cell.subviews.first(where: { $0.description.contains("Reorder") })?.subviews.first(where: { $0 is UIImageView }) as? UIImageView
    imageView?.image = UIImage(named: "your_custom_reorder_icon.png")
    let size = cell.bounds.height * 0.6 // scaled for padding between cells
    imageView?.frame.size.width = size
    imageView?.frame.size.height = size
}
0赞 Arthur Stepanov 6/7/2022 #12

Swift 5 解决方案:

子类 UITableViewCell 并重写 didAddSubview 方法:

override func didAddSubview(_ subview: UIView) {
    if !subview.description.contains("Reorder") { return }  
    (subview.subviews.first as? UIImageView)?.removeFromSuperview()

    let imageView = UIImageView()
    imageView.image = UIImage()
    subview.addSubview(imageView)
        
    imageView.snp.makeConstraints { make in
        make.height.width.equalTo(24)
        make.centerX.equalTo(subview.snp.centerX)
        make.centerY.equalTo(subview.snp.centerY)
    }
}

我使用 SnapKit 来设置约束,您可以按照自己的方式进行设置。

请注意,它可能是按Apple更新顺序排列的临时解决方案。

0赞 Miguel Gallego 12/15/2022 #13

使用 iOS 16Swift 5

我尝试了上述解决方案,但有时我的自定义图像未显示在某些单元格中。 这段代码对我来说在子类中工作得很好:UITableViewCell

private lazy var customReorderImgVw: UIImageView = {
    let img = UIImage(named: "imgCustomReorder")!
    let imgVw = UIImageView(image: img)
    imgVw.contentMode = .center
    imgVw.frame = CGRect(origin: .zero, size: img.size)
    imgVw.alpha = 0
    return imgVw
}()

override func setEditing(_ editing: Bool, animated: Bool) {
    super.setEditing(editing, animated: animated)
    if editing {
        for subVw in subviews {
            if "\(subVw.classForCoder)" == "UITableViewCellReorderControl" {
                subVw.subviews.forEach { $0.removeFromSuperview() }
                customReorderImgVw.center.y = subVw.center.y
                subVw.addSubview(customReorderImgVw)
                break
            }
        }
    }
    showOrHideCustomReorderView(isToShow: editing)
}

private func showOrHideCustomReorderView(isToShow: Bool) {
    let newAlpha: CGFloat = (isToShow ? 1 : 0)
    UIView.animate(withDuration: 0.25) {
        self.customReorderImgVw.alpha = newAlpha
    }
}
0赞 nodebase 10/13/2023 #14

前面的 hack 是实现此目的的一种方法,但有一种非 hacky 方法可以做到这一点。这对我来说使用 Xcode 15 很好,但这些方法是在iOS 8.0

在您的 :UITableViewDelegate

func tableView(_ tableView: UITableView, shouldIndentWhileEditingRowAt indexPath: IndexPath) -> Bool {
  false
}

func tableView(_ tableView: UITableView, editingStyleForRowAt indexPath: IndexPath) -> UITableViewCell.EditingStyle {
  .none
}

然后在你的 :UITableViewCell

override func setEditing(_ editing: Bool, animated: Bool) {
  super.setEditing(editing, animated: animated)
  showsReorderControl = false
}