How do I make reorder control of UITableViewCell in left side?

天大地大妈咪最大 提交于 2019-12-17 08:53:10

问题


I am doing a news reader app and I want to make it so that the user can choose show/hide news categories (such as top news, business, technology, sports, etc) and reorder them like the BBC news app in Android.

See the picture below:

My question is:

  • How do I make reorder control in left side of cell? (Its default position is in the right side of the cell in edit mode)
  • I have a checkbox custom control. How do I put it in right side of the cell?

回答1:


Answer [If you're not using any accessories (detail disclosure, &c)]

(0) I assume you have your tables set up, &c.

(1) This solution only works if your tables cells are always draggable. Add this in your viewDidLoad to your Table View Controller .m file.

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

(2) To make it so you can reorder your cells, add cell.showsReorderControl = YES; to your tableView:cellForRowAtIndexPath:.

(3) Ensure that you have the tableView:canMoveRowAtIndexPath: and tableView:moveRowAtIndexPath:toIndexPath: methods.

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

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

(4) Because you want the reorder control on the left, we have to take away the Delete circle usually there and the tableView:editingStyleForRowAtIndexPath: method does that.

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

(5) Last step, where the magic happens - add the tableView:willDisplayCell:forRowAtIndexPath: method, search for the cell's subviews, narrow it down to the private UITableViewCellReorderControl, and finally override it.

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

I spent nearly ten hours trying to figure out a solution for when accessories and I couldn't do it. I need more experience and knowledge first. Simply doing the same thing to the reorder control on the disclosure button didn't work. So I hope this would works for now; and if I figure it out in the future I'll be sure to update this.

Hi Hoang Van Ha, this is my first answer ever, and I only started learning Objective-C a month ago, so I'm still quite a noob.

I've been trying to do the same thing with my app and have been searching for hours how to do it. Apple's documentation isn't very much help when it comes to this. I found it quite frustrating when people would simply link to the documentation. I wanted to yell at them. I hope my answer helped you.

Lots of kudos to b2Cloud because I used some of their code from this article and adapted it for my answers.




回答2:


Instead of hunting for UITableViewCellReorderControl, which is non-public API and might change in the future, I recommend using a UILongPressGestureRecognizer over the UITableView and implement your own reordering logic.

I solved the general case (responding to a long press on any part of the cell) in HPReorderTableView, a drop-in replacement of UITableView. It can be easily modified to respond to touches on specific parts of the cell, for example by implementing gestureRecognizer:shouldReceiveTouch: of the reorderGestureRecognizer delegate.




回答3:


Now there is a much easier solution for iOS 9 and later

tableView.semanticContentAttribute = .forceRightToLeft

Edit:

As Roland mentioned you should notice that "if you align your cells' subviews via leading and trailing anchors instead of left and right, this setting will flip everything."




回答4:


Luke Dubert, have cool answer for iOS 7 it should be changed a little bit to:

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



回答5:


This solution didn't work for me in iOS7 so I have created a version for both, I hope this it usefull:

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



回答6:


Inspired by Luke's solution, I've found a reliable and straight forward way to make this happen:

@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

Simply create a custom UITableViewCell, override its subview and modify the position of each view will work nicely in all known scenarios.

Performing the UITableViewReorderControl customization under willDisplayCellAtIndexPath doesn't work 100%, the first reorder button won't position correctly occasionally.




回答7:


I tested on iOS 12 using swift 4.2, and instead of using view.self.description == "UITableViewCellReorderControl", use 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
        }
    }
}



回答8:


Thanks everyone above.Here is my solution. have tested on iOS7 and 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



回答9:


For those using Xamarin, here it is my solution based on @luke-dubert answer, but taking care of removing the additional added views when reusing:

On TableViewSource:

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

On cell:

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



回答10:


I've found a far easier solution that requires no absolute manual positioning of anything, and even works with animated transitions from/to editing mode. Should work for both iOS6 and 7.

Define this category on UIView somewhere:

@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

Then, in your UITableViewCell subclass, override willTransitionToState like so:

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

Note that the cell needs to be configured to show only the reorder control, otherwise more work needs to be done.



来源:https://stackoverflow.com/questions/11141947/how-do-i-make-reorder-control-of-uitableviewcell-in-left-side

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!