views:

4664

answers:

8

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.

+4  A: 

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];
}
craig
Hi Craig, the drawing is currently done in FirstLastExampleTableViewCell's -(void)drawContentView:(CGRect)r method, which is called from ABTableViewCell's override of UITableViewCell's -(void)drawRect:(CGRect)r method. Code is here: http://github.com/adamalex/fast-scrolling
Adam Alexander
Your solution looks good where multiple subviews are being used but here there is a single custom-drawn subview containing the entire cell's content. Possibly layoutSubviews would be the key for this too, I will investigate that and would appreciate any other ideas, thanks!
Adam Alexander
I'm not sure how well this would work using their method, since the text is being drawn directly to the screen, and not embedded in a UIView, or any object that supports CoreAnimation. Is there a significant improvement with this method over just using a UITextField/UITextView?
craig
Thanks again Craig, this was a big step in the right direction! Please see the answer I posted for info on how I was able to use this info. Also about the performance it explains more in the blog post I linked; it seems the performance gains would be most noticeable for complicated cell layouts.
Adam Alexander
+6  A: 

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)

Adam Alexander
Your solution looks perfect, exactly what I was thinking. Glad it worked out. :)
craig
I had this EXACT question, using the EXACT code. If I had looked here BEFORE beating my head against the wall for hours, I would have saved myself some forehead bruises. Your solution worked perfectly. Thanks.
mmc
+2  A: 
Chilloutman
Thanks for pointing this out and please see ben's answer for the fix - I updated my github project as well, in this commit http://github.com/adamalex/fast-scrolling/commit/4036c80f9bc0b01dcfec552ebafa4eb5d785a5a4
Adam Alexander
+2  A: 

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

ben
Thanks ben that works great! I updated my github project accordingly in this commit http://github.com/adamalex/fast-scrolling/commit/4036c80f9bc0b01dcfec552ebafa4eb5d785a5a4
Adam Alexander
A: 

Has anybody managed to get this fully working?

I tried implementing fast scrolling into the app I'm working on, but having a usable Edit mode is essential.

As I see it, there are still at least two problems:

  1. if you're in edit mode and you use the red circle toggle on the left, the text of your cell shifts to the left, and slides underneath the red circle (to make room for the Delete button)

  2. if you use something like drawInRect: to write lines of text to your cell, there doesn't appear to be any way to resize that when you enter edit mode. So, in my app, the text inside my CGRect slides to the right underneath the reorder control.

I'd love to have this working, but so far it's looking like I'll need to forget about "fast scrolling" and instead use UILabels and add them as subviews to my cell.

Jim Rhoades
+1  A: 

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.

AngeDeLaMort
A: 

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];
}
Gaj
A: 

im having a pretty bizarre problem...

I am using the built in edit button UINavigationController supplies when i add

self.navigationItem.rightBarButtonItem = self.editButtonItem;

to it's view did load method. however i want to overrride the default editing functionality by implementing the function ...

- (void)willTransitionToState:(UITableViewCellStateMask)state
  {
      //do nothing
      int i = 0; // this is in place so i can set a break point here

  }

however this never seems to get called when the editButton is pressed

Is there something else i need to put in place in order to start creating the functionality i want?

Thanks Steve

Steve
solution: makesure tableview:canEditRowAtIndexPath: returns YES
Steve