如何在左侧对UITableViewCell进行重新排序控制?

时间:2012-06-21 15:50:55

标签: ios uitableview custom-controls

我正在做一个新闻阅读器应用程序,我想这样做,以便用户可以选择显示/隐藏新闻类别(如热门新闻,商业,技术,体育等),并重新排序他们像BBC新闻应用程序机器人。

见下图:

enter image description here

我的问题是:

  • 如何在单元格的左侧进行重新排序控制? (其默认位置在编辑模式下位于单元格的右侧)
  • 我有一个复选框自定义控件。我怎么把它放在牢房的右侧?

10 个答案:

答案 0 :(得分:37)

回答[如果您没有使用任何配件(详细信息披露,& c)]

(0)我假设您已设置好表格,& c。

(1)此解决方案仅在表格单元格始终可拖动时才有效。将viewDidLoad中的这个添加到Table View Controller .m文件中。

- (void)viewDidLoad
{
    [super viewDidLoad];
    [self setEditing:YES];
}

(2)为了使你可以重新排序你的单元格,将cell.showsReorderControl = YES;添加到你的tableView:cellForRowAtIndexPath:。

(3)确保你有tableView:canMoveRowAtIndexPath:和tableView:moveRowAtIndexPath:toIndexPath:methods。

- (BOOL)tableView:(UITableView *)tableview canMoveRowAtIndexPath:(NSIndexPath *)indexPath
{
    return YES; 
}

- (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)fromIndexPath toIndexPath:(NSIndexPath *)toIndexPath
{
}

(4)因为你想要左边的重新排序控件,我们必须通常删除那里的删除圈,并且tableView:editingStyleForRowAtIndexPath:方法会这样做。

- (UITableViewCellEditingStyle)tableView:(UITableView *)tableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath
{
    return UITableViewCellEditingStyleNone;
}

(5)最后一步,魔术发生的地方 - 添加tableView:willDisplayCell:forRowAtIndexPath:方法,搜索单元格的子视图,将其缩小到私有UITableViewCellReorderControl,最后覆盖它。

- (void) tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
{
    for(UIView* view in cell.subviews)
    {
        if([[[view class] description] isEqualToString:@"UITableViewCellReorderControl"])
        {
            // Creates a new subview the size of the entire cell
            UIView *movedReorderControl = [[UIView alloc] initWithFrame:CGRectMake(0, 0, CGRectGetMaxX(view.frame), CGRectGetMaxY(view.frame))];
            // Adds the reorder control view to our new subview
            [movedReorderControl addSubview:view];
            // Adds our new subview to the cell
            [cell addSubview:movedReorderControl];
            // CGStuff to move it to the left
            CGSize moveLeft = CGSizeMake(movedReorderControl.frame.size.width - view.frame.size.width, movedReorderControl.frame.size.height - view.frame.size.height);
            CGAffineTransform transform = CGAffineTransformIdentity;
            transform = CGAffineTransformTranslate(transform, -moveLeft.width, -moveLeft.height);
            // Performs the transform
            [movedReorderControl setTransform:transform];
        }
    }
}

我花了将近十个小时试图找出配件的解决方案而我无法做到。我需要更多的经验和知识。简单地对披露按钮上的重新排序控件做同样的事情是行不通的。所以我希望现在能有效;如果我将来弄明白,我一定会更新。

  

嗨Hoang Van Ha,这是我的第一个回答,我一个月前才开始学习Objective-C,所以我还是一个菜鸟。

     

我一直在尝试用我的应用程序做同样的事情,并一直在寻找数小时如何做到这一点。就此而言,Apple的文档并没有太大帮助。当人们简单地链接到文档时,我发现它非常令人沮丧。我想对他们大喊大叫。我希望我的回答对你有帮助。

     

b2Cloud有很多赞誉,因为我使用了this article中的一些代码并根据我的答案进行了调整。

答案 1 :(得分:23)

我建议UITableViewCellReorderControl使用UILongPressGestureRecognizer而不是寻找UITableView,这是非公开API,可能会在将来发生变化,实现自己的重新排序逻辑。

我在HPReorderTableView中解决了一般情况(响应对单元格任何部分的长按),这是UITableView的直接替换。可以轻松修改它以响应对单元格特定部分的触摸,例如通过实施gestureRecognizer:shouldReceiveTouch:委托的reorderGestureRecognizer

答案 2 :(得分:12)

现在有一个更简单的iOS 9及更高版本的解决方案

tableView.semanticContentAttribute = .forceRightToLeft

修改

正如Roland提到的那样,您应该注意到“如果您通过leadingtrailing锚点而不是leftright对齐您的单元格子视图,设置将翻转所有内容。“

答案 3 :(得分:4)

Luke Dubert,对于iOS 7有一个很酷的答案,应该稍微改变一下:

UIView *cellSubview = cell.subviews[0];
    for(UIView* view in cellSubview.subviews)
    {
        if([[[view class] description] isEqualToString:@"UITableViewCellReorderControl"])
        {

答案 4 :(得分:1)

此解决方案在iOS7中对我不起作用,所以我为两者创建了一个版本,我希望它有用:

- (void)moveReorderControl:(UITableViewCell *)cell subviewCell:(UIView *)subviewCell
{
    if([[[subviewCell class] description] isEqualToString:@"UITableViewCellReorderControl"]) {
    static int TRANSLATION_REORDER_CONTROL_Y = -20;

        //Code to move the reorder control, you change change it for your code, this works for me
        UIView* resizedGripView = [[UIView alloc] initWithFrame:CGRectMake(0, 0,    CGRectGetMaxX(subviewCell.frame), CGRectGetMaxY(subviewCell.frame))];
        [resizedGripView addSubview:subviewCell];
        [cell addSubview:resizedGripView];

        //  Original transform
        const CGAffineTransform transform = CGAffineTransformMakeTranslation(subviewCell.frame.size.width - cell.frame.size.width, TRANSLATION_REORDER_CONTROL_Y);
        //  Move custom view so the grip's top left aligns with the cell's top left

        [resizedGripView setTransform:transform];
   }
}

//This method is due to the move cells icons is on right by default, we need to move it.
- (void) tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
{
    if (tableView.tag == MEETING_BLOCK_TABLE_TAG) {
        for(UIView* subviewCell in cell.subviews)
        {
            if ([ROCFunctions isIOS7]) {
                if([[[subviewCell class] description] isEqualToString:@"UITableViewCellScrollView"]) {
                    for(UIView* subSubviewCell in subviewCell.subviews) {
                        [self moveReorderControl:cell subviewCell:subSubviewCell];
                    }
                }
            }
            else{
                [self moveReorderControl:cell subviewCell:subviewCell];
            }
        }
    }
}

答案 5 :(得分:1)

受卢克(Luke)解决方案的启发,我找到了一种可靠而直接的方法来实现这一目标:

@implementation CustomTableViewCell

-(void) layoutSubviews {
    [super layoutSubviews];

    // set the frames of the other subviews here if necessary, for example: 
    // self.textLabel.frame = CGRectMake(80, 0, self.textLabel.frame.size.width, self.textLabel.frame.size.height);

    for (UIView* view in self.subviews) {
        if ([[[view class] description] containsString:@"UITableViewCellReorderControl"]) {
            [view setFrame:CGRectMake(20, 0, view.frame.size.width, view.frame.size.height)];
        }
    }
}

@end

只需创建一个自定义UITableViewCell,覆盖其子视图并修改每个视图的位置,即可在所有已知情况下正常工作。

在willDisplayCellAtIndexPath下执行UITableViewReorderControl自定义无法100%正常工作,第一个重新排序按钮有时不会正确定位。

答案 6 :(得分:0)

感谢上面的所有人。这是我的解决方案。已在iOS7和iOS9上测试

@implementation StoryPreviewCell
-(void)layoutSubviews{
    [super layoutSubviews];

    //subviews contains first level and second level views. 
    NSMutableArray *subviews = [NSMutableArray array];
    [subviews addObjectsFromArray:self.subviews];
    for (NSInteger i=0; i<self.subviews.count; i++) {
        UIView *firstLevelView = self.subviews[i];
        if(firstLevelView.subviews.count) [subviews addObjectsFromArray:firstLevelView.subviews];
    }

    for(UIView* view in subviews) {
        if([[[view class] description] isEqualToString:@"UITableViewCellReorderControl"]) {
            UIView *reorderControl = view;
            CGFloat leftMoveDistance = 100;//can customize
            CGAffineTransform transform = CGAffineTransformTranslate(CGAffineTransformIdentity, -leftMoveDistance, 0);
            reorderControl.transform = transform;
        }
    }
}
@end

答案 7 :(得分:0)

对于那些使用Xamarin的人来说,这是我基于@ luke-dubert答案的解决方案,但在重用时要注意删除其他添加的视图:

在TableViewSource上:

public override void WillDisplay(UITableView tableView, UITableViewCell cell, NSIndexPath indexPath)
{
    (cell as GraphBarLineCell)?.SetEditionMode(tableView.Editing);
}

单元格:

private UIView _reorderControlContainer;

public void SetEditionMode(bool editing)
    {
        _title.Frame = editing ? TitleFrameForEditing : TitleFrame;

        if (editing)
        {
            var reorderControl = Subviews.FirstOrDefault(x => x.Class.Name.Equals("UITableViewCellReorderControl"));

            if (reorderControl != null)
            {
                _reorderControlContainer?.RemoveFromSuperview();

                // Creates a new subview the size of the entire cell
                _reorderControlContainer = new UIView(new CGRect(0, 0, reorderControl.Frame.GetMaxX(), reorderControl.Frame.GetMaxY()));

                // Adds the reorder control view to our new subview
                _reorderControlContainer.AddSubview(reorderControl);

                // Adds our new subview to the cell
                AddSubview(_reorderControlContainer);

                // CGStuff to move it to the left
                var moveLeft = new CGSize(_reorderControlContainer.Frame.Size.Width - reorderControl.Frame.Size.Width,
                    _reorderControlContainer.Frame.Size.Height - reorderControl.Frame.Size.Height);

                var transform = CGAffineTransform.MakeIdentity();
                transform = CGAffineTransform.Translate(transform, -moveLeft.Width, -moveLeft.Height);
                _reorderControlContainer.Transform = transform;

                // Align the icon with the title
                var icon = reorderControl.Subviews[0];
                icon.Frame = new CGRect(10, 25, icon.Frame.Width, icon.Frame.Height);
            }
        }
    }

答案 8 :(得分:0)

我在iOS 12上使用swift 4.2进行了测试,而不是使用view.self.description == "UITableViewCellReorderControl",而是使用view.self.description.contains("UITableViewCellReorderControl")

func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
    for view in cell.subviews {
        if view.self.description.contains("UITableViewCellReorderControl") {
            let movedReorderControl = UIView(frame: CGRect(x: 0, y: 0, width: view.frame.maxX, height: view.frame.maxY))
            movedReorderControl.addSubview(view)
            cell.addSubview(movedReorderControl)
            let moveLeft = CGSize(width: movedReorderControl.frame.size.width - view.frame.size.width, height: movedReorderControl.frame.size.height - view.frame.size.height)
            var transform: CGAffineTransform = .identity
            transform = transform.translatedBy(x: -moveLeft.width, y: -moveLeft.height)
            movedReorderControl.transform = transform
        }
    }
}

答案 9 :(得分:-1)

我发现了一个更简单的解决方案,不需要任何绝对的手动定位,甚至可以使用从/到编辑模式的动画过渡。应该适用于iOS6和7。

在UIView的某处定义此类别:

@interface UIView (ClassSearch)

- (instancetype)subviewOfClassMatching:(NSString*)partialName;

@end

@implementation UIView (ClassSearch)

-(instancetype)subviewOfClassMatching:(NSString *)partialName
{
    if ([[[self class] description] rangeOfString:partialName options:NSCaseInsensitiveSearch].location != NSNotFound) {
        return self;
    }

    for (UIView* v in self.subviews) {
        id match = [v subviewOfClassMatching:partialName];
        if (match) {
            return match;
        }
    }

    return nil;
}

@end

然后,在你的UITableViewCell子类中,覆盖willTransitionToState,如下所示:

-(void)willTransitionToState:(UITableViewCellStateMask)state
{
    [super willTransitionToState:state];

    if (state == UITableViewCellStateShowingEditControlMask) {
        UIView* reorderControl = [self subviewOfClassMatching:@"ReorderControl"];
        UIView* reorderContainer = [[UIView alloc] initWithFrame:self.bounds];
        CGAffineTransform t = CGAffineTransformMakeScale(-1, 1);
        reorderContainer.transform = t;
        [self addSubview:reorderContainer];
        [reorderContainer addSubview:reorderControl];
    }
}

请注意,需要将单元格配置为仅显示重新排序控件,否则需要完成更多工作。