UICollectionView relodItemsAtIndexPaths перезагружает все элементы представления коллекции - PullRequest
0 голосов
/ 15 апреля 2020

Пожалуйста, рассмотрите этот простой код:

@interface ViewController ()<UICollectionViewDelegate, UICollectionViewDataSource>

@property (nonatomic, strong) IBOutlet UICollectionView* collectionView;

@end

@implementation ViewController

- (void)viewDidLoad {
     [super viewDidLoad];

     self.view.backgroundColor = [UIColor redColor];

     [self.collectionView registerClass:[UICollectionViewCell class] forCellWithReuseIdentifier:@"Hello"];

     dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(20 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{

         [self.collectionView reloadItemsAtIndexPaths:@[ [NSIndexPath indexPathForItem:0 inSection:0]]];
});
}

- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
{
    return 20;
}

- (__kindof UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
    NSLog(@"RELOADING %ld", indexPath.row);

    NSString* identifier = @"Hello";

    UICollectionViewCell* cell = [collectionView dequeueReusableCellWithReuseIdentifier:identifier forIndexPath:indexPath];

    NSArray* colors = @[[UIColor redColor], [UIColor blueColor]];

    cell.backgroundColor = colors[indexPath.row % colors.count];

    return cell;
}

@end

Таким образом, здесь есть очень базовый c UICollectionView, который создан в раскадровке. В этом нет ничего особенного. Итак, я пытаюсь перезагрузить только один элемент. И когда я звоню reloadItemsAtIndexPaths, он сначала перезагружает все элементы и сразу же перезагружает тот, который я действительно хочу перезагрузить. Это явно неправильно. Мне нужно избегать всей этой дополнительной работы. Почему он так себя ведет? Это ошибка iOS? Можно ли что-нибудь сделать, чтобы он работал правильно?

1 Ответ

2 голосов
/ 16 апреля 2020

Если ваш UICollectionView использует макет, который может иметь динамические ячейки c, макет будет переоцениваться при каждом обновлении ячейки. Это означает, что cellForItemAtIndexPath будет вызываться для большого количества ячеек.

Обратите внимание, однако, что с вашим кодом только элемент 0,0 фактически обновляется.

Измените свой код на это (обратите внимание, я изменил ваше время отправки на 3 секунды, чтобы нам не пришлось так долго ждать обновления):

@interface WithCollectionViewController ()<UICollectionViewDelegate, UICollectionViewDataSource>

@property (nonatomic, strong) IBOutlet UICollectionView* collectionView;
@property (nonatomic, strong) NSArray *colors;

@end

@implementation WithCollectionViewController

- (void)viewDidLoad {
    [super viewDidLoad];

    // start with colors array of red and blue
    self.colors = @[[UIColor redColor], [UIColor blueColor]];

    [self.collectionView registerClass:[UICollectionViewCell class] forCellWithReuseIdentifier:@"Hello"];
    [self.collectionView setDataSource:self];
    [self.collectionView setDelegate:self];

    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        NSLog(@"calling RELOAD");
        // change colors array to green and yellow
        self.colors = @[[UIColor greenColor], [UIColor yellowColor]];
        [self.collectionView reloadItemsAtIndexPaths:@[ [NSIndexPath indexPathForItem:0 inSection:0]]];
    });

}

- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
    return 20;
}

- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {

    NSLog(@"RELOADING %ld", indexPath.row);

    NSString* identifier = @"Hello";

    UICollectionViewCell* cell = [collectionView dequeueReusableCellWithReuseIdentifier:identifier forIndexPath:indexPath];

    cell.backgroundColor = self.colors[indexPath.row % self.colors.count];

    return cell;

}

@end

Сначала вы получите красный / синий узор. При срабатывании reloadItemsAtIndexPaths массив цветов будет изменен на зеленый / желтый ... вы увидите отладочный вывод для всех 20 ячеек, но изменится только первая ячейка цвет (зеленый).

enter image description here

Если вы не хотите повторных вызовов, присвойте макету фиксированный размер элемента:

@interface WithCollectionViewController ()<UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout>

@property (nonatomic, strong) IBOutlet UICollectionView* collectionView;
@property (nonatomic, strong) NSArray *colors;

@end

@implementation WithCollectionViewController

- (void)viewDidLoad {
    [super viewDidLoad];

    // start with colors array of red and blue
    self.colors = @[[UIColor redColor], [UIColor blueColor]];

    [self.collectionView registerClass:[UICollectionViewCell class] forCellWithReuseIdentifier:@"Hello"];
    [self.collectionView setDataSource:self];
    [self.collectionView setDelegate:self];

    UICollectionViewFlowLayout *layout = [UICollectionViewFlowLayout new];
    layout.itemSize = CGSizeMake(50, 50);
    [self.collectionView setCollectionViewLayout:layout];

    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        NSLog(@"calling RELOAD");
        // change colors array to green and yellow
        self.colors = @[[UIColor greenColor], [UIColor yellowColor]];
        [self.collectionView reloadItemsAtIndexPaths:@[ [NSIndexPath indexPathForItem:0 inSection:0]]];
    });

}

- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
    return 20;
}

- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {

    NSLog(@"RELOADING %ld", indexPath.row);

    NSString* identifier = @"Hello";

    UICollectionViewCell* cell = [collectionView dequeueReusableCellWithReuseIdentifier:identifier forIndexPath:indexPath];

    cell.backgroundColor = self.colors[indexPath.row % self.colors.count];

    return cell;

}

@end

Тот же результат, но cellForItemAtIndexPath вызывается только для указанного вами пути индекса 0,0.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...