Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

popToRootViewController crashes when tableView is still scrolling

When I give a good swipe to my tableView and press the "Back" button before the tableView ended it's scrolling, my app crashes. I've tried the following:

- (void) closeViewController
{
    [self killScroll];
    [self.navigationController popToRootViewControllerAnimated:YES];
    [self dismissViewControllerAnimated:YES completion:nil];
}

- (void)killScroll
{
    CGPoint offset = sellersTableView.contentOffset;
    [sellersTableView setContentOffset:offset animated:NO];
}

That didn't work, same crash. I don't see why, the error I'm getting is the following:

*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'UITableView dataSource must return a cell from tableView:cellForRowAtIndexPath:'

So that means that the tableView is still requesting a cell when everything is already being deallocated. Makes no sense. Then I tried this:

- (void) closeViewController
{
    [self.navigationController popToRootViewControllerAnimated:YES];
    [self dismissViewControllerAnimated:YES completion:nil];
}

- (void)dealloc
{
    sellersTableView.dataSource = nil;
    sellersTableView.delegate = nil;
    sellersTableView = nil;
}

Gives me the same error. Any ideas?

Update: My delegate methods

creation

if (textField == addSellerTextField) {
        sellersTableView = [[UITableView alloc] initWithFrame:CGRectMake(addSellerTextField.frame.origin.x + addSellerTextField.frame.size.width + 10, addSellerTextField.frame.origin.y - [self heightForTableView] + 35, 200, [self heightForTableView])];
        sellersTableView.delegate = self;
        sellersTableView.dataSource = self;
        sellersTableView.backgroundColor = [[UIColor grayColor] colorWithAlphaComponent:0.05];
        sellersTableView.separatorColor = [[UIColor grayColor] colorWithAlphaComponent:0.15];
        sellersTableView.rowHeight = 44;
        sellersTableView.layer.opacity = 0;
        [self.companyView addSubview:sellersTableView];
        [UIView animateWithDuration:0.3 delay:0 options:UIViewAnimationOptionCurveEaseIn animations:^{sellersTableView.layer.opacity = 1;} completion:nil];
    }

cellForRowAtIndexPath

if (tableView == sellersTableView) {
        if (!cell) {
            cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
        }
        cell.backgroundColor = [UIColor clearColor];
        if ([sellersArray count] > 0) {
            cell.textLabel.text = [sellersArray objectAtIndex:indexPath.row];
        } else {
            UILabel *noSellersYetLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, sellersTableView.frame.size.width, [self heightForTableView])];
            noSellersYetLabel.text = @"no sellers yet";
            noSellersYetLabel.textAlignment = NSTextAlignmentCenter;
            noSellersYetLabel.textColor = [UIColor grayColor];
            [cell addSubview:noSellersYetLabel];
            sellersTableView.separatorStyle = UITableViewCellSeparatorStyleNone;
        }
    }

removing

- (void) textFieldDidEndEditing:(UITextField *)textField
{
    if (textField == addSellerTextField) {
        [self updateSellers:textField];
    }
}
- (void)updateSellers:(UITextField *)textField
{
    [textField resignFirstResponder];
    [self hideSellersTableView];
}

- (void)hideSellersTableView
{
    [UIView animateWithDuration:0.3 delay:0 options:UIViewAnimationOptionCurveEaseIn animations:^{sellersTableView.layer.opacity = 0;} completion:nil];
    sellersTableView.dataSource = nil;
    sellersTableView.delegate = nil;
    [sellersTableView removeFromSuperview];
    sellersTableView = nil;
}

Solution So apparently putting the dataSource = nil and delegate = nil into textFieldDidEndEditing fixed the problem. Thanks everybody for the answers!

like image 503
Yoko Avatar asked Sep 26 '14 13:09

Yoko


4 Answers

It's strange behaviour of UITableView. The easiest way to resolve this issue just set the dataSource and delegate property of UITAbleView to nil before you make a call of function popToRootViewControllerAnimated. Furthermore you can use more common solution and add the code that set the properties to nil into the -dealloc method. In addition you no need the -killScroll method.

After a short research I have realized what the problem is. This unusual behaviour appeared in iOS 7. The scroll view retained by its superview may send message to delegate after the delegate is released. It happens due to -removeFromSuperview implementation UIScrollView triggers -setContentOffset: and, eventually, send message to delegate.

like image 198
tikhop Avatar answered Nov 06 '22 00:11

tikhop


Just add following lines at the beginning of dealloc method:

sellersTableView.delegate = nil;
sellersTableView.dataSource = nil;

No need to use hacks like your killScroll method.

Also, I can't see why you want to call both popToRootViewController and dismissViewController. If you dismiss a view controller which is embedded in a navigation controller, navigation controller itself as well as all contained view controllers will be released.

In your case you'll have just weird animation.

like image 44
hybridcattt Avatar answered Nov 06 '22 02:11

hybridcattt


setContentOffset method won't help you, try to set

sellersTableView.dataSource = nil;

somewhere in your viewWillDisappear method. This is not a good practice of course.

like image 1
Bogdan Matveev Avatar answered Nov 06 '22 00:11

Bogdan Matveev


Change you closeViewController like below and see if works

  • (void) closeViewController

{

sellersTableView.dataSource = nil;
sellersTableView.delegate = nil;
[self.navigationController popToRootViewControllerAnimated:YES];
[self dismissViewControllerAnimated:YES completion:nil];

}

like image 1
Kuntal Gajjar Avatar answered Nov 06 '22 02:11

Kuntal Gajjar