iOS实现在collectionView顶部插入数据效果

有时候,我们会遇到这种需求,就是下拉刷新的时候,在

collectionView顶部插入数据,这个时候,需要我们注意

主要有两点

1 关闭隐式动画

由于我们使用insert在collectionView顶部插入数据是有从头部插入的隐式动画的,这个不符合我们的需要,所以要关闭隐式动画 [CATransaction setDisableActions:YES];

2 我们执行过插入数据操作之后,还要将偏移量滚动到之前展示的位置达到在页面顶部插入数据的效果,所以我们要精确的捕获到插入结束的时机,就需要用到performBatchUpdates 方法,在结束的回调中设置偏移量

3 因为涉及到如果没有上一页了,要修改列表的头部,

因为刷新视图就是在头部中,这个时候(头部在顶部,未展示出来),我们需要使用performBatchUpdates 刷新列表,而不能使用reloadData,因为reloadData 刷新头部的时候,会造成列表的cell偏移, 而performBatchUpdates 则不会

代码

复制代码
//
//  LBUpdateBatchViewController.m
//  TEXT
//
//  Created by mac on 2025/1/4.
//  Copyright © 2025 刘博. All rights reserved.
//

#import "LBUpdateBatchViewController.h"
#import "PerformBatchCell.h"

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

@property (nonatomic, strong) UICollectionView *collectionView;

@property (nonatomic, strong) UIButton *firstButton;
@property (nonatomic, strong) UIButton *secondButton;

@property (nonatomic, strong) UIButton *thirdButton;

@property (nonatomic, assign) NSInteger flag;

@property (nonatomic, assign) NSInteger itemCount;

@end

@implementation LBUpdateBatchViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    self.view.backgroundColor = [UIColor whiteColor];
    self.flag = 1;
    self.itemCount = 20;
    [self.view addSubview:self.collectionView];
    [self.view addSubview:self.firstButton];
    [self.view addSubview:self.secondButton];
    [self.view addSubview:self.thirdButton];
    
    // Do any additional setup after loading the view.
}

- (void)firstClick
{
    self.flag = 0;
    [self.collectionView reloadData];
}

- (void)secondClick
{
    self.flag = 2;
    [self.collectionView performBatchUpdates:nil completion:nil];
}

- (void)loadPreviousPage
{
    self.itemCount += 6;
    NSMutableArray *array = [NSMutableArray array];
    for (int i = 0; i < 6; i ++) {
        NSIndexPath *indexPath = [NSIndexPath indexPathForItem:i inSection:0];
        [array addObject:indexPath];
    }
    [CATransaction setDisableActions:YES];
    [self.collectionView performBatchUpdates:^{
        [self.collectionView insertItemsAtIndexPaths:array];
        } completion:^(BOOL finished) {
            [CATransaction setDisableActions:NO];
            if (finished) {
                CGRect frame = [self.collectionView layoutAttributesForItemAtIndexPath:[NSIndexPath indexPathForItem:6 inSection:0]].frame;
                [self.collectionView setContentOffset:CGPointMake(0, frame.origin.y - 100)];
                self.flag --;
                if (self.flag <= 0) {
                    ///注意,这里一定要用 performBatchUpdates 不能用reloadData
                    [self.collectionView performBatchUpdates:nil completion:nil];
                }
            }
        }];

}

#pragma mark - UICollectionViewDelegate, UICollectionViewDataSource

- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
{
    return self.itemCount;
}

- (UICollectionReusableView *)collectionView:(UICollectionView *)collectionView viewForSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath
{
    if ([kind isEqualToString:UICollectionElementKindSectionHeader]) {
        UICollectionReusableView *header = [collectionView dequeueReusableSupplementaryViewOfKind:kind withReuseIdentifier:@"header" forIndexPath:indexPath];
        header.backgroundColor = [UIColor cyanColor];
        return header;
    }
    return nil;
}

- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout referenceSizeForHeaderInSection:(NSInteger)section
{
    CGFloat height = 0;
    if (self.flag >  0) {
        height = 100;
    }
    return CGSizeMake(300, height);
}


- (__kindof UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
    PerformBatchCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"cell" forIndexPath:indexPath];
    NSString *title = [NSString stringWithFormat:@"%ld", indexPath.item];
    [cell updateWithText:title];
    return cell;
}

- (UICollectionView *)collectionView
{
    if (!_collectionView) {
        UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init];
        layout.itemSize = CGSizeMake(100, 100);
        layout.minimumLineSpacing = 10;
        layout.minimumInteritemSpacing = 10;
        _collectionView = [[UICollectionView alloc] initWithFrame:CGRectMake(10, 100, 300, 600) collectionViewLayout:layout];
        [_collectionView registerClass:[PerformBatchCell class] forCellWithReuseIdentifier:@"cell"];
        [_collectionView registerClass:[UICollectionReusableView class] forSupplementaryViewOfKind:UICollectionElementKindSectionHeader withReuseIdentifier:@"header"];
        _collectionView.delegate = self;
        _collectionView.dataSource = self;
    }
    return _collectionView;
}

- (UIButton *)firstButton
{
    if (!_firstButton) {
        _firstButton = [UIButton buttonWithType:UIButtonTypeCustom];
        _firstButton.frame = CGRectMake(100, 100, 200, 100);
        _firstButton.titleLabel.font = [UIFont systemFontOfSize:14];
        _firstButton.backgroundColor = [UIColor redColor];
        [_firstButton setTitle:@"隐藏头部并reloadData" forState:UIControlStateNormal];
        [_firstButton addTarget:self action:@selector(firstClick) forControlEvents:UIControlEventTouchUpInside];
    }
    return _firstButton;
}

- (UIButton *)secondButton
{
    if (!_secondButton) {
        _secondButton = [UIButton buttonWithType:UIButtonTypeCustom];
        _secondButton.frame = CGRectMake(100, 250, 200, 100);
        _secondButton.backgroundColor = [UIColor greenColor];
        [_secondButton setTitle:@"展示头部并performBatchUpdates" forState:UIControlStateNormal];
        [_secondButton addTarget:self action:@selector(secondClick) forControlEvents:UIControlEventTouchUpInside];
        _secondButton.titleLabel.font = [UIFont systemFontOfSize:14];

    }
    return _secondButton;
}

- (UIButton *)thirdButton
{
    if (!_thirdButton) {
        _thirdButton = [UIButton buttonWithType:UIButtonTypeCustom];
        _thirdButton.frame = CGRectMake(100, 400, 200, 100);
        _thirdButton.backgroundColor = [UIColor blueColor];
        [_thirdButton setTitle:@"模拟加载上一页" forState:UIControlStateNormal];
        [_thirdButton addTarget:self action:@selector(loadPreviousPage) forControlEvents:UIControlEventTouchUpInside];
        _thirdButton.titleLabel.font = [UIFont systemFontOfSize:14];
    }
    return _thirdButton;
}

/*
#pragma mark - Navigation

// In a storyboard-based application, you will often want to do a little preparation before navigation
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
    // Get the new view controller using [segue destinationViewController].
    // Pass the selected object to the new view controller.
}
*/

@end

效果图

相关推荐
Jouzzy16 分钟前
【iOS安全】iPhone X iOS 16.7.11 (20H360) WinRa1n 越狱教程
安全·ios·iphone
roman_日积跬步-终至千里10 小时前
【Go语言基础】基本语法
开发语言·golang·xcode
二流小码农13 小时前
鸿蒙开发:实现一个标题栏吸顶
android·ios·harmonyos
season_zhu13 小时前
iOS开发:关于日志框架
ios·架构·swift
Digitally17 小时前
如何在电脑上轻松访问 iPhone 文件
ios·电脑·iphone
安和昂17 小时前
【iOS】YYModel源码解析
ios
pop_xiaoli17 小时前
UI学习—cell的复用和自定义cell
学习·ui·ios
Daniel_Coder19 小时前
Xcode 16.4 + iOS 18 系统运行时崩溃:___cxa_current_primary_exception 符号丢失的原因与解决方案
ios·xcode·ios 18·dyld·libc++abi
烈焰晴天1 天前
使用ReactNative加载Svga动画支持三端【Android/IOS/Harmony】
android·react native·ios
sg_knight1 天前
Flutter嵌入式开发实战 ——从树莓派到智能家居控制面板,打造工业级交互终端
android·前端·flutter·ios·智能家居·跨平台