封装了一个iOS联动滚动效果

效果图

实现逻辑和原理

就是在 didEndDisplayingCell 方法中通过indexPathsForVisibleItems 接口获取当前可见的cell对应的indexPath, 然后获取到item最小的那一个,即可,同时,还要在 willDisplayCell 方法中直接设置标题的选中属性,否则

由于重用机制,导致选中的展示错乱

代码

复制代码
//
//  LBLinkedContentView.m
//  LBTwoLinkedScrollingCollectionView
//
//  Created by mac on 2024/6/25.
//

#import "LBLinkedContentView.h"
#import "LBLinkedContentViewTittleCell.h"
#import "LBLinkedContentViewContentCell.h"

@interface LBLinkedContentView () <UICollectionViewDelegate, UICollectionViewDataSource>

@property (nonatomic, strong) UICollectionView *titleView;
@property (nonatomic, strong) UICollectionView *contentView;

@property (nonatomic, strong) NSMutableArray *titleArray;
@property (nonatomic, strong) NSMutableArray *contentArray;

@property (nonatomic, strong) NSIndexPath *selectedGroupIndex;

@end

@implementation LBLinkedContentView

- (instancetype)initWithFrame:(CGRect)frame
{
    if (self = [super initWithFrame:frame]) {
        [self configData];
        [self setUpUI];
    }
    return self;
}

- (void)setUpUI
{
    [self addSubview:self.titleView];
    [self addSubview:self.contentView];
    [self.titleView reloadData];
    [self.contentView reloadData];
    self.selectedGroupIndex = [NSIndexPath indexPathForRow:0 inSection:0];
    LBLinkedContentViewTittleCell *titleCell = [self.titleView cellForItemAtIndexPath:self.selectedGroupIndex];
    titleCell.selected = YES;
}

#pragma mark - configData

- (void)configData
{
    for (int i = 0; i < 10; i ++)  {
        NSString *title = [NSString stringWithFormat:@"%d",i];
        [self.titleArray addObject:title];
        NSMutableArray *array = [NSMutableArray array];
        for (int j = 0; j < 10; j ++) {
            NSString *content = [NSString stringWithFormat:@"第%d分区第%d条", i,j];
            [array addObject:content];
        }
        [self.contentArray addObject:array];
    }
}

#pragma mark - data Delegate

- (__kindof UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
    if (collectionView == self.contentView) {
        LBLinkedContentViewContentCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:NSStringFromClass([LBLinkedContentViewContentCell class]) forIndexPath:indexPath];
        NSString *title = self.contentArray[indexPath.section][indexPath.item];
        [cell updateWithContent:title];
        return cell;
    }
    
    LBLinkedContentViewTittleCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:NSStringFromClass([LBLinkedContentViewTittleCell class]) forIndexPath:indexPath];
    [cell updateWithIndexPath:indexPath];
    return cell;
}

- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView
{
    if (collectionView == self.titleView) {
        return 1;
    }
    return self.contentArray.count;
}

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


- (void)collectionView:(UICollectionView *)collectionView willDisplayCell:(UICollectionViewCell *)cell forItemAtIndexPath:(NSIndexPath *)indexPath
{
    if (collectionView == self.titleView) {
        if (self.selectedGroupIndex && [self.selectedGroupIndex compare:indexPath] == NSOrderedSame) {
            cell.selected = YES;
        } else {
            cell.selected = NO;
        }
    }
}

- (void)collectionView:(UICollectionView *)collectionView didEndDisplayingCell:(UICollectionViewCell *)cell forItemAtIndexPath:(NSIndexPath *)indexPath
{
    if (collectionView == self.contentView) {
        [self handleGroupViewWithIndex:nil];
    }
}

#pragma mark - private

- (void)handleGroupViewWithIndex:(NSIndexPath *)indexPathCell
{
    NSArray <NSIndexPath *> *visibleIndexPaths = [self.contentView indexPathsForVisibleItems];
    if (!visibleIndexPaths.count) {
        return;
    }
    NSInteger section = indexPathCell ? indexPathCell.section : [self.contentView numberOfSections] - 1;
    for (NSIndexPath *indexPath in visibleIndexPaths) {
        if (indexPath.section < section) {
            section = indexPath.section;
        }
    }
    NSIndexPath *groupIndexPath = [NSIndexPath indexPathForItem:section inSection:0];
    if(self.selectedGroupIndex && [self.selectedGroupIndex compare:groupIndexPath] != NSOrderedSame) {
        LBLinkedContentViewTittleCell *cell = [self.titleView cellForItemAtIndexPath:self.selectedGroupIndex];
        cell.selected = NO;
        self.selectedGroupIndex = groupIndexPath;
        LBLinkedContentViewTittleCell *titleCell = [self.titleView cellForItemAtIndexPath:groupIndexPath];
        titleCell.selected = YES;
        [self.titleView scrollToItemAtIndexPath:groupIndexPath atScrollPosition:UICollectionViewScrollPositionLeft animated:YES];
    }
}

#pragma mark - lazy load

- (UICollectionView *)titleView
{
    if (!_titleView) {
        UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init];
        layout.minimumLineSpacing = 29;
        layout.minimumInteritemSpacing = 29;
        layout.scrollDirection = UICollectionViewScrollDirectionHorizontal;
        layout.itemSize = CGSizeMake(40, 50);
        _titleView = [[UICollectionView alloc] initWithFrame:CGRectMake(0, 0, CGRectGetWidth(self.bounds), 60) collectionViewLayout:layout];
        [_titleView registerClass:[LBLinkedContentViewTittleCell class] forCellWithReuseIdentifier:NSStringFromClass([LBLinkedContentViewTittleCell class])];
        _titleView.delegate = self;
        _titleView.dataSource = self;
    }
    return _titleView;
}

- (UICollectionView *)contentView
{
    if (!_contentView) {
        UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init];
        layout.minimumLineSpacing = 10;
        layout.minimumInteritemSpacing = 10;
        layout.itemSize = CGSizeMake(100, 80);
        layout.scrollDirection = UICollectionViewScrollDirectionHorizontal;
        layout.sectionInset = UIEdgeInsetsMake(0, 40, 0, 40);
        _contentView = [[UICollectionView alloc] initWithFrame:CGRectMake(0, 80, CGRectGetWidth(self.bounds), 80) collectionViewLayout:layout];
        [_contentView registerClass:[LBLinkedContentViewContentCell class] forCellWithReuseIdentifier:NSStringFromClass([LBLinkedContentViewContentCell class])];
        _contentView.delegate = self;
        _contentView.dataSource = self;
        _contentView.backgroundColor = [UIColor yellowColor];
    }
    return _contentView;
}

- (NSMutableArray *)titleArray
{
    if (!_titleArray) {
        _titleArray = [NSMutableArray array];
    }
    return _titleArray;
}

- (NSMutableArray *)contentArray
{
    if (!_contentArray) {
        _contentArray = [NSMutableArray array];
    }
    return _contentArray;
}

@end

link

相关推荐
柚鸥ASO优化14 小时前
一篇讲透安卓ASO!开发者千万别只盯着iOS了
android·ios·aso优化
黑科技iOS上架15 小时前
Swift Package Manager包管理工具的优缺点
经验分享·ios
大熊猫侯佩18 小时前
Swift 6.4 的 Ref / MutableRef 大揭秘:给值类型开一扇“安全的小窗”
ios·swift·编程语言
黑科技iOS上架19 小时前
没有mac电脑如何借助windows系统上传ipa到App Store
经验分享·ios
Layer20 小时前
从 WWDC 26 空间重构(Spatial Reframing)再看端侧 2D 转 3D 的技术演进
ios·aigc
Cutecat_1 天前
视频字幕处理工具横向:提取模式 vs 编辑模式,该如何选择
android·前端·ios·语音识别
大熊猫侯佩1 天前
WWDC26 SwiftUI 进化之路:砸碎黑盒,彻底迎来开发自由!
ios·swiftui·swift
游戏开发爱好者81 天前
iPhone真机调试有哪些方法?一次定位推送权限问题时整理出来的几种方案
ide·vscode·ios·objective-c·个人开发·swift·敏捷流程
大熊猫侯佩2 天前
WWDC26 最被忽视的王炸:告别“伪并发”陷阱,Swift 6.4 的 async defer
ios·swift·编程语言
h-189-53-6712072 天前
苹果开发者账号防关联3.2f隔离环境传包提审iOS开发上架的高效隔离方案:iOSUploader工具实用解析
ios·ios上架·ios审核·苹果审核·苹果开发者账号·苹果开发者封号