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

Posted

技术标签:

【中文标题】更改 UITableView 中移动单元格的默认图标【英文标题】:Change default icon for moving cells in UITableView 【发布时间】:2011-12-22 11:38:48 【问题描述】:

我需要。

这个:

有可能吗?

【问题讨论】:

【参考方案1】:

这是一个非常老套的解决方案,可能无法长期使用,但可以为您提供一个起点。重新排序控件是UITableViewCellReorderControl,但这是一个私有类,因此您无法直接访问它。但是,您可以只查看子视图的层次结构并找到它的 imageView。

您可以通过继承UITableViewCell 并覆盖其setEditing: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")
            
        
    

请注意...这可能不是一个长期的解决方案,因为 Apple 可以随时更改视图层次结构。

【讨论】:

有没有人找到一种方法来做到这一点而不会冒 App Store 被拒绝的风险?这是处理这个问题的一种非常脆弱和老套的方法。 如果失败,我认为最可能的结果是我们将再次看到默认图标,这还不错。我还尝试了 Phil 的方法,如果失败,它会给我在错误位置的自定义图标,默认图标也可见。所以我会用这种方法抓住机会。 我的自定义图标与默认图标的大小不同,因此我还将找到的子视图的框架设置为我的 UIImage 的框架。我还扩展了它以自定义出现在表格单元格左侧的插入和删除图标。不错! 这又适用于 ios 8。所以你可以在 iOS 5、6 和 8 以及 OgreSwamp 对 iOS 7 的回答中使用它。唯一的区别是视图层次结构在 iOS 7 中开始更深一层。 @Brian - 这意味着在代码中引用私有类名,这会使应用程序面临被拒绝的风险。我的疯狂中有方法!【参考方案2】:

Ashley Mills 的回答在提供时非常出色,但正如其他人在 cmets 中指出的那样,视图层次结构已从 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);
                
            
        
    

【讨论】:

太棒了,一个 3 岁的问题仍然得到答案!有一个 +1 !【参考方案3】:

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

【讨论】:

这是 2019 年的解决方案 不适用于默认单元格。我们需要继承uitableviewcell吗【参考方案4】:

Swift 版本的 Rick 答案,几乎没有改进:

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

【讨论】:

【参考方案5】:

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"];
                
            
        
    

【讨论】:

【参考方案6】:

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

    创建 UITableViewCell 的子类。 覆盖 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;


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

【讨论】:

虽然这可以显示自定义editingAccessoryView,但我实际上无法通过按下并拖动单元格来移动它。 嗯...我不记得我在哪里使用过这个...当我找到它时我会更新我的答案。【参考方案7】:
- (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;
                
            
        
       

【讨论】:

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

我找不到任何其他适合我的答案,但我找到了解决方案。

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

【讨论】:

【参考方案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)
                    
                
            
        
    

【讨论】:

【参考方案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];

【讨论】:

【参考方案11】:

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

您所要做的就是确保此自定义视图始终位于其他视图之上,这可以在[UITableViewDelegate tableView: willDisplayCell: forRowAtIndexPath: indexPath:]. 中查看

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

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

【讨论】:

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

以上是关于更改 UITableView 中移动单元格的默认图标的主要内容,如果未能解决你的问题,请参考以下文章

在 UITableView 中点击时更改单元格的颜色

不移动单元格的uitableview拖放

在普通样式的 UITableView 中更改自定义 UITableViewCell 单元格的背景颜色

UITableView - 如何在拖放期间更改单元格的背景颜色?

使用动画将uitableview单元格移动到顶部

使用情节提要更改 UITableView 单元格的高度