Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Animating custom-drawn UITableViewCell when entering edit mode

Background

First of all, much gratitude to atebits for their very informative blog post Fast Scrolling in Tweetie with UITableView. The post explains in detail how the developers were able to squeeze as much scrolling performance as possible out of the UITableViews in Tweetie.

Goals

Beginning with the source code linked from the blog post (original) (my github repo):

  1. Allow a UITableView using these custom cells to switch to edit mode, exposing the UI for deleting an item from the table. (github commit)

  2. Move the cell's text aside as the deletion control slides in from the left. This is complete, although the text jumps back and forth without animation. (github commit)

  3. Apply animation to the text movement in goal 2 above for a smooth user experience. This is the step where I became stuck.

The Question

What is the best way to introduce this animation to complete goal 3? It would be nice if this could be done in a manner that keeps the logic from my last commit because I would love the option to move the conflicting part of the view only, while any non-conflicting portions (such as right-justified text) stay in the same place or move a different number of pixels. If the above is not possible then undoing my last commit and replacing it with an option that slides the entire view to the right would be a workable solution also.

I appreciate any help anyone can provide, from quick pointers and ideas all the way to code snippets or github commits. Of course you are welcome to fork my repo if you would like. I will be staying involved with this question to make sure any successful resolution is committed to github and fully documented here. Thanks very much for your time!

Updated thoughts

I have been thinking about this a lot since my first post and realized that moving some text items relative to others in the view could undo some of the original performance goals solved in the original blog post. So at this point I am thinking a solution where the entire single subview is animated to its new postion may be the best one.

Second, if it is done in this way there may be an instance where the subview has a custom color or gradient background. Hopefully this can be done in a way that in its normal position the background extends unseen off to the left enough so that when the view is slid to the right the custom background is still visible across the entire cell.

like image 421
Adam Alexander Avatar asked Apr 13 '09 01:04

Adam Alexander


5 Answers

Thanks to Craig's answer which pointed me in the right direction, I have a solution for this. I reverted my commit which moved the text position based on the editing mode and replaced it with a new solution that sets the entire content view to the correct position any time layoutSubviews is called, which results in an automatic animation when switching to and from edit mode:

- (void)layoutSubviews
{
    CGRect b = [self bounds];
    b.size.height -= 1; // leave room for the separator line
    b.size.width += 30; // allow extra width to slide for editing
    b.origin.x -= (self.editing) ? 0 : 30; // start 30px left unless editing
    [contentView setFrame:b];
    [super layoutSubviews];
}

By doing it this way I was able to remove the setFrame: override found in ABTableViewCell.m because its former logic plus my additions are now found in layoutSubviews.

I set a light grey background on the cells to verify a custom background works properly without allowing us to see behind it as it moves back and forth and it seems to work great.

Thanks again to Craig and anyone else who has looked into this.

GitHub commit for this solution: (link)

like image 106
Adam Alexander Avatar answered Oct 16 '22 17:10

Adam Alexander


How are you moving the text around currently? Or more specifically, in which UITableViewCell method are you performing the move?

From my experience, overriding the layoutSubviews method and setting the frame here will automatically be wrapped in an animation.

Eg:

- (void)layoutSubviews {
    if (self.editing) {
        [titleLabel setFrame:CGRectMake(62, 6, 170, 24)];
    }
    else {
        [titleLabel setFrame:CGRectMake(30, 6, 200, 24)];
    }
    [super layoutSubviews];
}
like image 43
Craig Otis Avatar answered Oct 16 '22 17:10

Craig Otis


For fully control on editing in your custom cell, you should override willTransitionToState method in your UITableViewCell subclass and check state mask

- (void)willTransitionToState:(UITableViewCellStateMask)state
{
    NSString *logStr = @"Invoked";
    if ((state & UITableViewCellStateShowingEditControlMask)
        != 0) {
        // you need to move the controls in left
        logStr = [NSString stringWithFormat:@"%@
                  %@",logStr,@"UITableViewCellStateShowingEditControlMask"];
    }
    if ((state & UITableViewCellStateShowingDeleteConfirmationMask)
        != 0) {
        // you need to hide the controls for the delete button
        logStr = [NSString stringWithFormat:@"%@
                  %@",logStr,@"UITableViewCellStateShowingDeleteConfirmationMask"];
    }
    NSLog(@"%@",logStr);
    [super willTransitionToState:state];
}

also you can override layoutSubviews

- (void)layoutSubviews {
    // default place for label
    CGRect alarmTimeRect = CGRectMake(37, 7, 75, 30);
    if (self.editing && !self.showingDeleteConfirmation) {
        // move rect in left
        alarmTimeRect = CGRectMake(77, 7, 75, 30);
    }
    [alarmTimeLabel setFrame:alarmTimeRect];
    [super layoutSubviews];
}
like image 9
UIBuilder Avatar answered Oct 16 '22 19:10

UIBuilder


To handle swiping as well: (self.editing && !self.showingDeleteConfirmation)

like image 8
ben Avatar answered Oct 16 '22 17:10

ben


I just had this question too: how to animate a custom editing mode? I didn't really like the solution here, so I decided to think a little bit and found a different solution. I don't know if it's better, but I prefer that one. So I decided to share it here:

In the custom cell (inherit from UITableViewCell), just overload the setEditing:

- (void)setEditing:(BOOL)editing animated:(BOOL)animated {
    [super setEditing:editing animated:animated];

    if (animated) {
        [UIView beginAnimations:@"setEditingAnimation" context:nil];
        [UIView setAnimationDuration:0.3];
    }

    if (editing) {
        /* do your offset and resize here */
    } else {
        /* return to the original here*/
    }

    if (animated)
        [UIView commitAnimations];
}

I don't check if the editing value is the same, but that's just an idea how I did it.

like image 3
Sauleil Avatar answered Oct 16 '22 17:10

Sauleil