开发者

UITableViewCell custom reorder control

Is there any way to change the image of the reorder control that is displayed when the UITableView is in edit mode? I have a UIImage that I’d like to display instead of the 开发者_运维问答usual grey bars.

Do I have to subclass UITableViewCell to accomplish this?


I guess you're a long way past this by now, but this has come up in a new question.

See my answer here:

Change default icon for moving cells in UITableView


I recently ran across the need to change the image for the reorder control, because I subclassed UITableViewCell to provide my own custom table cell. As part of this effort, I changed the background of the cell to something other than the default color.

Everything works correctly, but when I put the UITableView into editing mode, the reorder control would appear, with a white background - instead of the background color I was using for the cell. This didn't look good, and I wanted the background to match.

During the course of various versions of iOS, the hierarchy of views in a UITableViewCell has changed. I've taken an approach that will traverse the entire set of views until it finds the UITableViewCellReorderControl private class. I believe this will work for iOS 5 and all subsequent iOS versions at the time of this answer. Please note that while the UITableViewCellReorderControl class itself is private, I am not using any private API's to find it.

First, here's the code to scan for the reorder control; I'm assuming that the text "Reorder" will be in the class name - which Apple could change in the future:

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

In your custom UITableViewCell subclass, you will override -(void) setEditing:animated: and find the reorder control here. If you try to find this control when the table is not in editing mode, the reorder control will not be in the view hierarchy for the cell:

-(void) setEditing:(BOOL)editing animated:(BOOL)animated
{
    [super setEditing:editing animated:animated];
    if (editing)
    {
        // find the reorder view here
        // place the previous method either directly in your
        // subclassed UITableViewCell, or in a category
        // defined on UIView
        UIView *reorderView = [self findReorderView:self];
        if (reorderView)
        {
            // here, I am changing the background color to match my custom cell
            // you may not want or need to do this
            reorderView.backgroundColor = self.contentView.backgroundColor;
            // now scan the reorder control's subviews for the reorder image
            for (UIView *sv in reorderView.subviews)
            {
                if ([sv isKindOfClass:[UIImageView class]])
                {
                    // and replace the image with one that you want
                    ((UIImageView *)sv).image = [UIImage imageNamed:@"yourImage.png"];
                    // it may be necessary to properly size the image's frame
                    // for your new image - in my experience, this was necessary
                    // the upper left position of the UIImageView's frame
                    // does not seem to matter - the parent reorder control
                    // will center it properly for you
                    sv.frame = CGRectMake(0, 0, 48.0, 48.0);
                }
            }
        }
    }
}

Your mileage may vary; I hope this works for you.


Here is my Swift solution based on Rick Morgan's answer:

func adjustSize() {
    // we're trying to leverage the existing reordering controls, however that means the table must be kept in editing mode,
    // which shrinks the content area to less than full width to make room for editing controls
    let cellBounds = bounds
    let contentFrame = contentView.convert(contentView.bounds, to: self)

    let leftPadding = contentFrame.minX - cellBounds.minX
    let rightPadding = cellBounds.maxX - contentFrame.maxX

    // adjust actual content so that it still covers the full length of the cell
    contentLeadingEdge.constant = -leftPadding
    // this should pull our custom reorder button in line with the system button
    contentTrailingEdge.constant = -rightPadding

    // make sure we can still see and interact with the content that overhangs
    contentView.clipsToBounds = false

    // recursive search of the view tree for a reorder control
    func findReorderControl(_ view: UIView) -> UIView? {
        // this is depending on a private API, retest on every new iPad OS version
        if String(describing: type(of: view)).contains("Reorder") {
            return view
        }
        for subview in view.subviews {
            if let v = findReorderControl(subview) {
                return v
            }
        }
        return nil
    }

    // hunt down the system reorder button and make it invisible but still operable
    findReorderControl(self)?.alpha = 0.05   // don't go too close to alpha 0, or it will be considered hidden
}

This worked pretty well. contentLeadingEdge and contentTrailingEdge are layout constraints I set up in Interface Builder between the contentView and the actual content. My code calls this adjustSize method from the tableView(_:, willDisplay:, forRowAt:) delegate method.

Ultimately, however, I went with Clifton's suggestion of just covering the reorder control. I added a UIImageView directly to the cell (not contentView) in awakeFromNib, positioned it, and when adjustSize is called I simply bring the image view to the front, and it covers the reorder control without having to depend on any private APIs.


I put a little work into this recently, but came up short. I tried setting my own editingAccesoryView but couldn't get the reorder control to change. Odd.

My guess is that it has something to do with the following comment in the UITableviewCell docs re: showsReorderControl:

If the value is YES , the reordering control temporarily replaces any accessory view.

In other words, the editingAccessoryView is being replaced by the reordering control view, which might be why we cannot override the reordering control. Hoping someone can find a workaround.


You set the cell's editingAccessoryView property to an image view containing the image you want.

As an aside, I would caution you to be careful when doing this. When you substitute a custom graphic for a system standard such as the reorder graphic, you run a serious risk of confusing the user. The standard UI grammar has told them to expect the standard graphic when reordering and they may not understand the significance of your custom graphic.


Maybe we're all overthinking this. :)

Just put a custom UIImageView over the top of the default move accessory so it covers it up. Done.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜