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

相关推荐
1024小神2 小时前
tauri2.0版本开发苹果ios和安卓android应用,环境搭建和最后编译为apk
android·ios·tauri
lzhdim3 小时前
iPhone 17 Air看点汇总:薄至6mm 刷新苹果轻薄纪录
ios·iphone
安和昂3 小时前
【iOS】知乎日报第四周总结
ios
麦田里的守望者江5 小时前
KMP 中的 expect 和 actual 声明
android·ios·kotlin
_黎明7 小时前
【Swift】字符串和字符
开发语言·ios·swift
ZVAyIVqt0UFji9 小时前
iOS屏幕共享技术实践
macos·ios·objective-c·cocoa
hfxns_10 小时前
iOS 18.2 Beta 4开发者预览版发布,相机新增辅助功能
ios
AirDroid_cn20 小时前
如何控制自己玩手机的时间?两台苹果手机帮助自律
ios·智能手机·ipad·手机使用技巧·苹果手机使用技巧
郝晨妤1 天前
鸿蒙原生应用开发元服务 元服务是什么?和App的关系?(保姆级步骤)
android·ios·华为od·华为·华为云·harmonyos·鸿蒙
tealcwu1 天前
【Unity踩坑】在Mac上安装Cocoapods失败
unity·ios·游戏引擎