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
To handle swiping as well: (self.editing && !self.showingDeleteConfirmation)
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];
}
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];
}
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.
There are actually four edite states.
UITableViewCellStateDefaultMask = 0
UITableViewCellStateShowingEditControlMask = 1<<0 = 0b01 = 1 by setting the tableView.edit = YES
UITableViewCellStateShowingDeleteConfirmationMask = 1<<1 = 0b10 = 2 by swiping to delete
UITableViewCellStateShowingEditControlMask and UITableViewCellStateShowingDeleteConfirmationMask = (1<<1)&(1<<0) = 0b11 = 3 by setting the tableView.edit = YES and press the "stop" button
What confused me is that when state 3 transfer to state 1, no selector would get called which make no use of self.showingDeleteConfirmation to change layout in iOS7.Therefore, in order to make clear between state 2 and state 3, I added a instance variable to implement it. It works great for me.
-(void)willTransitionToState:(UITableViewCellStateMask)state{
[super willTransitionToState:state];
//Custom delete button from
//http://stackoverflow.com/questions/19159476/uitableviewcelldeleteconfirmationcontrol-issue
if((state & UITableViewCellStateShowingDeleteConfirmationMask) == UITableViewCellStateShowingDeleteConfirmationMask){
[self recurseAndReplaceSubViewIfDeleteConfirmationControl:self.subviews];
[self performSelector:@selector(recurseAndReplaceSubViewIfDeleteConfirmationControl:) withObject:self.subviews afterDelay:0];
}
_swipeDelete = NO;
if(state==2){
//Swipe to delete confirm
_swipeDelete = YES;
}
if(state==3){
//Edit state to delete confirm
}
}
- (void)layoutSubviews{
[super layoutSubviews];
self.contentView.frame = CGRectMake(10, 5.0, 300, self.frame.size.height - 10);
if(self.editing){
self.contentView.frame = CGRectMake(40, 5.0, 270, self.frame.size.height - 10);
}
if(_swipeDelete){
self.contentView.frame = CGRectMake(10, 5.0, 300, self.frame.size.height - 10);
}
}
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)