封装了一个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

相关推荐
Sim14807 小时前
iPhone将内置本地大模型,手机端AI实现0 token成本时代来临?
人工智能·ios·智能手机·iphone
Digitally9 小时前
如何将 iPad 上的照片传输到 U 盘(4 种解决方案)
ios·ipad
报错小能手11 小时前
ios开发方向——swift并发进阶核心 @MainActor 与 DispatchQueue.main 解析
开发语言·ios·swift
LcGero11 小时前
Cocos Creator 业务与原生通信详解
android·ios·cocos creator·游戏开发·jsb
ii_best12 小时前
lua语言开发脚本基础、mql命令库开发、安卓/ios基础开发教程,按键精灵新手工具
android·ios·自动化·编辑器
用户223586218202 天前
WebKit WebPage API 的引入尝试与自研实现
ios
啦啦啦!2 天前
ChatGPT和Gemini的接入和封装
人工智能·ios·chatgpt
报错小能手2 天前
ios开发方向——swift并发进阶核心 async/await 详解
开发语言·ios·swift
开心就好20252 天前
HTTPS超文本传输安全协议全面解析与工作原理
后端·ios
牛马1112 天前
Flutter iOS 权限配置完整指南(定位权限)
flutter·ios