简体   繁体   中英

Why tableView reloadData doesn't call cellForRowAtIndexPath

I have two ViewControllers:

An UIViewController with a "push" button, Another UIViewController with a tableView.

PushAction

- (IBAction)pushViewController:(id)sender {
    NSArray *dataArr = @[@1,@2,@3,@4,@5];

    NextViewController *nextViewController = [[NextViewController alloc] init];
    [nextViewController setDataAndReload:dataArr];
    [self.navigationController pushViewController:nextViewController animated:YES];
}

NextViewController.h

@interface NextViewController : UIViewController

- (void)setDataAndReload:(NSArray *)dataArr;

@end

NextViewController.m

#import "NextViewController.h"

@interface NextViewController () <UITableViewDelegate, UITableViewDataSource>

@property (nonatomic, strong) UITableView *tableView;

@end

@implementation NextViewController {
    NSMutableArray *_arr;
}

- (void)viewDidLoad {
    [super viewDidLoad];

    [self.view addSubview:self.tableView];
    [self.tableView reloadData];
 }

- (void)setDataAndReload:(NSArray *)dataArr {
    //[self loadViewIfNeeded];
    _arr = [dataArr mutableCopy];
    [self.tableView reloadData];
}

 #pragma mark - data source
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    return _arr.count;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    static NSString *cellIdentifier = @"CellIdentifier";
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
    if (cell == nil) {
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier];
    }

    cell.textLabel.text = [NSString stringWithFormat:@"Cell %@", _arr[indexPath.row]];

    return cell;
}

#pragma mark - delegate
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
    [_arr removeObjectAtIndex:indexPath.row];
    [self.tableView reloadData];
}

- (UITableView *)tableView {
    if (!_tableView) {
        _tableView = [[UITableView alloc] initWithFrame:self.view.bounds style:UITableViewStyleGrouped];
        _tableView.delegate = self;
        _tableView.dataSource = self;
        _tableView.backgroundColor = [UIColor whiteColor];
    }
    return _tableView;
}

TableView是可见的

You have noticed that I called [self.tableView reloadData] before tableView was added to any view

Then, when I select any cell on the tableView, which will call [self.tableView reloadData] after selected, the tableView doesn't reload as expected.

After debugging with adding some breakpoints to tableView's dataSource methods, I found that numberOfRowsInSection: has been called correctly, but cellForRowAtIndexPath has not been called.

Then I think what cause the issue maybe is that setDataAndReload: call [self.tableView reloadData] before I added the tableView to viewController's view. So I added [self loadViewIfNeeded] and cellForRowAtIndexPath: was called correctly.

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
    [_arr removeObjectAtIndex:indexPath.row];
    [self.tableView reloadData];
}

Thus, I wonder why cellForRowAtIndexPath can not be called after [self.tableView reloadData] in the tableView's delegate method didSelectRowAtIndexPath: .

I have found what cause cellForRowAtIndexPath can not be called after [self.tableView reloadData]

When I call [self.tableView reloadData] in setDataAndReload: It will call the lazy method of tableView.

As I used self.view.bounds to initialize the table view, an at the first time setDataAndReload: was called, the view has not been loaded. So it will load as below (while the tableView has not been created and returned) :

loadView -> viewDidLoad -> [self.view addSubview:self.tableView] -> lazy method agian

-> return second tableView

-> self.tableView is the second tableView

-> second tableView is added to superView

-> return first tableView

-> self.tableView is the first tableView

Thus, the tableView that self.tableView finally pointed to, was never added to a superView!

When you are setting your in - (void)setDataAndReload:(NSArray *)dataArr , like you sad, the tableView is not even initialised and not part of the view hierarchy. so i would recommend to rename the method to - (void)setData:(NSArray *)dataArr and reload the tableView in viewDidLoad only.

So here is your the updated code:

- (void)viewDidLoad {
    [super viewDidLoad];
    [self.view addSubview:self.tableView];
    [self.tableView reloadData];
 }

- (void)setDataAndReload:(NSArray *)dataArr {
    _arr = [dataArr mutableCopy];
}

In didSelectRowAtIndexPath , if the tableView doesn't reloadData.You should check the caller self.tableView , it must have some problem. And you will find the self.tableView has change its pointer to a new tableView . Then you should check your getter method. In getter method, if we want to use some properties, we must be careful.Because sometimes we may be call the getter more than once,Why?. In your example, you try to get the bounds from self.view , and self.view will send message to -(void)loadView; ,which is the lifecycle method of VC, and -(void)loadView will send message to -(void)viewDidLoad; Now, -(void)viewDidLoad call the self.tableView again. Fortuantely, it doesn't cause a loop.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM