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

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

这个:

有可能吗?

【问题讨论】:

    标签: ios uitableview


    【解决方案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来替换重新排序图标。

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

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

        【讨论】:

        • 虽然这可以显示自定义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 默认禁用其用户交互,因此我的自定义图标不会干扰单元格的默认功能。
              猜你喜欢
              • 1970-01-01
              • 2010-10-14
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2012-04-30
              • 2019-01-02
              • 2012-07-22
              • 1970-01-01
              相关资源
              最近更新 更多