PopToRootViewController crashes when tableView is still scrolling

When I draw my table file well and hit the back button before the tableView finishes 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];
}

      

It didn't work, same crash. I don't understand 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 this means that the tableView still asks for the cell when everything has already been freed. No difference. 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

Creature

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

      

deletion

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

      

The decision thus appears to be a problem dataSource = nil

and delegate = nil

to textFieldDidEndEditing

solve the problem. Thanks everyone for the answers!

+3


source to share


5 answers


This is the strange behavior of the UITableView. The easiest way to solve this problem is to simply set the property dataSource

and delegate

value UITAbleView

to nil before calling the function popToRootViewControllerAnimated

. Alternatively, you can use a more common solution and add code that sets properties to nil to the method -dealloc

. Also, you don't need a method -killScroll

.



After a short research, I figured out what the problem was. This unusual behavior was introduced in iOS 7. The scroll view saved by its supervisor can send a message to a delegate after the delegate has been released. This is due to the -removeFromSuperview

implementation of UIScrollView

triggers -setContentOffset:

and ultimately sending a message to the delegate.

+2


source


Just add the following lines at the beginning of the method dealloc

:

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

      

No need to use hacks like the killScroll method.



Also, I don't understand why you want to call both popToRootViewController

and dismissViewController

. If you remove a view controller that is built into a navigation controller, the navigation controller itself, as well as any view controllers it contains, are freed.

In your case, you just have a weird animation.

+2


source


setContentOffset

method won't help, try installing

sellersTableView.dataSource = nil;

      

somewhere in your method viewWillDisappear

. Of course, this is not a good practice.

+1


source


Replace closeViewController as below and see if

  • (void) closeViewController

{

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

      

}

+1


source


I don't think the problem with the tableView (or its delegation) is null. You should be able to execute both rejectViewControllerAnimated or popToRootViewController separately, without having to modify the tableView that way.

So the problem is most likely caused by calling both of these methods at the same time (and with animated = YES) and doing something unnatural in your viewController setting.

It looks like by clicking the "close" button, you both exit on rootViewController

out UINavigationController

and also dismiss the modal viewController

.

With this, you reject the modal viewController

that is most likely presented topViewController

navigationController

(which is why the top vc contains a link to the modal vc). And you are trying to kill the top vc with a call to the popToRootViewController method. And you do both of these things using animated = YES

, which means they take a while to complete and you cannot be sure when it will end (i.e. you cannot be sure when dealloc is called).

You can do one of several things depending on your needs.

Consider adding a delegate property to your modal vc. Reject the modal vc and finally. Blocking the modal vc inform your delegate that it has finished firing. At this point, call popToRootViewController (because at this point you can be sure that the modal has disappeared and the scrolling has not been interrupted).

If this is your navController that was presented modally, do it in reverse order. Notifying the delegate about the completion of the pop operation and then disabling the mod.

0


source







All Articles