iOS - 二級連動(tableview包含 collectionview)

weixin_34185364發表於2018-09-03

基本結構

1,一級分類結構
一個 tableview列表
2,二級分類結構
一個 tableview 的每個 cell 都是一個 collectionview
根據一級分類選擇的cell重新整理二級分類 tableview
3,二級分類帶索引
引用三方iOS列表的索引功能

模型結構

1,一級分類模型fModels
核心屬性(一級分類包含二級分類的所有資訊)

//記錄一級分類是否選中,避免 cell 複用的 UI錯誤
@property (assign,nonatomic) BOOL select;
//帶索引的二級品類資訊
@property (nonatomic,strong) NSDictionary <NSString *,NSArray *>*sModelDicts;
//二級分類有序的索引陣列,用於有序的載入二級分類 cell
@property (strong,nonatomic) NSArray * secondIndexKeyArr;
//二級分類序號對應的cell的高度,根據索引陣列儲存索引的高度資訊 key:height
@property (strong,nonatomic) NSMutableDictionary* secondHeightDicts;

基本屬性

@property (nonatomic,assign) NSInteger fID;
@property (nonatomic,strong) NSString * fName;

2,二級分類模型sModel
基本屬性

@property (nonatomic,assign) NSInteger sID;
@property (nonatomic,strong) NSString * sName;
@property (assign,nonatomic) BOOL select;

載入一級分類和二級分類資料

通過網路載入資料,資料格式為

{
   Fmodel1的資料
  FModel1.SModel = {
                                "索引1" :[ 該索引下的全部二級分類 ]
                                "索引2" :[ 該索引下的全部二級分類 ]
                            }
 Fmodel2的資料
  FModel2.SModel = {
                                "索引1" :[ 該索引下的全部二級分類 ]
                                "索引2" :[ 該索引下的全部二級分類 ]
                            }
}

資料載入原理
1,一級分類資料
直接根據字典的方式載入
2,二級分類資料
·首先將二級分類索引排序,儲存到secondIndexKeyArr 陣列
·遍歷每個索引下的二級分類物件陣列儲存到sModelDicts字典
·將該字典儲存到一級分類物件屬性sModelDicts
·將一級分類物件屬性新增到一級分類陣列
核心程式碼

//成功獲取網路資料
if(success)
        {
//            儲存一級分類物件的陣列
            NSMutableArray * fModels = [NSMutableArray array];
//            遍歷一級分類
            NSArray * cateArr = response[@"resultMap"][@"rows"];
            [cateArr enumerateObjectsUsingBlock:^(NSDictionary * obj, NSUInteger idx, BOOL * _Nonnull stop) {
//               一個一級分類物件
                FModel * model  = [[FModel alloc] init];
                model.fID       = [obj[@"firstProductCategoryCodeId"] integerValue];
                model.fName     = obj[@"firstProductCategoryName"];
//                獲取一級分類下的全部索引和對應的二級分類
                NSDictionary * sDicts = obj[@"dataListCfs"];
                // 將索引排序
                NSArray *keys = sDicts.allKeys;
                keys = [keys sortedArrayUsingComparator:^NSComparisonResult(id  _Nonnull obj1, id  _Nonnull obj2) {
                    NSComparisonResult result = [obj1 compare:obj2];
                    return result == NSOrderedDescending;
                }];
//                儲存有序的二級分類索引
                model.secondIndexKeyArr = [NSArray arrayWithArray:keys];
//                建立二級分類字典
                NSMutableDictionary * sModels = [NSMutableDictionary dictionary];
            
//                遍歷索引取得每個索引下的二級品類陣列 每個索引對應該索引下的二級分類陣列物件
                [sDicts enumerateKeysAndObjectsUsingBlock:^(NSString *key, NSArray* obj, BOOL * _Nonnull stop) {
//                 遍歷將每個二級品類陣列下的陣列轉二級品類物件陣列
                    NSMutableArray *sobjArr = [NSMutableArray array];
                    [obj enumerateObjectsUsingBlock:^(NSDictionary* sobj, NSUInteger idx, BOOL * _Nonnull stop) {
                        sModel * models = [[sModel alloc] init];
                        models.sID      = [sobj[@"secondProductCategoryCodeIds"] integerValue];
                        models.sName    = sobj[@"secondProductCategoryName"];
                        [sobjArr addObject:models];
                    }];
                    
                    [sModels setObject:sobjArr forKey:key];
                }];
//                將二級分類字典儲存到一級分類
                model.sModelDicts = [NSDictionary dictionaryWithDictionary:sModels];
                [fModels addObject:model];
                
            }];
            
        }

控制器

1,一級分類選中的複用問題
第一個 cell 預設選中
當點選 cell時,先將前一個 cell選中標誌清除,再將當前 cell 選中,最後將當前 cell 賦值給選中cell屬性
2,選中時重新整理二級分類列表

//記錄選中的一級分類,避免 cell 複用引起的 UI 問題
@property (strong,nonatomic) FirstCategoryCell * FirstCell;
//使用
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    if (tableView == self.FirstCategoryTable) {
        FirstCategoryCell *cell = [self.FirstCategoryTable cellForRowAtIndexPath:indexPath];
        self.FirstCell.leftLine.hidden = YES;
        cell.leftLine.hidden = NO;
        self.selectCellRow = indexPath.row;
        self.FirstCell = cell;
        self.selectIndex = indexPath.row;
        [self reloadIndex];
    }else{
        
    }
}

下面程式碼的主要功能
1,根據選中的一級分類重新整理二級分類
2,切換一級分類時索引重新回到最上方
3,相關的 tableView代理方法

//選中的二級分類序號
@property (assign,nonatomic) NSInteger  selectSection;
//選擇一級分類的索引
@property (assign,nonatomic) NSInteger selectIndex;
@property (assign,nonatomic) NSInteger selectCellRow;
//選擇的二級分類字典
@property (strong,nonatomic) NSDictionary * sModelDicts;


//根據選中的一級品類重新整理二級品類
-(void)reloadIndex{

    //        根據key來儲存每個cell的高度
    self.fModels[self.selectIndex].secondHeightDicts = [NSMutableDictionary dictionary];
    [self.fModels[self.selectIndex].secondIndexKeyArr enumerateObjectsUsingBlock:^(NSString * obj, NSUInteger idx, BOOL * _Nonnull stop) {
        
        NSInteger count =  self.fModels[self.selectIndex].sModelDicts[obj].count;
        CGFloat height;
        if (count%self.rowItems>0) {
            height = (count/self.rowItems+1)*(self.itemHeight);
        }else{
            height = (count/self.rowItems)*(self.itemHeight);
        }
        [self.fModels[self.selectIndex].secondHeightDicts setObject:@(height) forKey:obj];
        
    }];
    //    重新整理到頂部和序號重新整理
    [self.SecondCategoryTable reloadData];
//
    [self.SecondCategoryTable scrollToRow:0 inSection:0 atScrollPosition:UITableViewScrollPositionTop animated:YES];
    self.selectSection = 0;
    //    索引列表
    self.sModelDicts = [NSDictionary dictionaryWithDictionary:self.fModels[self.selectIndex].sModelDicts];
    SCIndexViewConfiguration *indexViewConfiguration = [SCIndexViewConfiguration configuration];
    self.SecondCategoryTable.sc_indexViewDelegate = self;
    self.SecondCategoryTable.sc_indexViewConfiguration = indexViewConfiguration;
    self.SecondCategoryTable.sc_translucentForTableViewInNavigationBar = YES;
    self.SecondCategoryTable.sc_indexViewDataSource = self.fModels[self.selectIndex].secondIndexKeyArr;
}
#pragma mark 索引重新整理


//每次重新選擇一級品類重新整理序號到頂部
- (NSUInteger)sectionOfTableViewDidScroll:(UITableView *)tableView{
    if (tableView == self.SecondCategoryTable) {
        return self.selectSection;
    }
    return 0;
}

- (void)scrollViewDidScroll:(UIScrollView *)scrollView{
    
    if ([scrollView isEqual: self.SecondCategoryTable]) {
        
        if (self.SecondCategoryTable.contentOffset.y > _oldY) {
            // 上滑
            _isUpScroll = YES;
        }
        else{
            // 下滑
            _isUpScroll = NO;
        }
        _isFirstLoad = NO;
    }
}
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView{
    
    // 獲取開始拖拽時tableview偏移量
    
    _oldY = self.SecondCategoryTable.contentOffset.y;
    
}

- (void)tableView:(UITableView *)tableView willDisplayHeaderView:(UIView *)view forSection:(NSInteger)section{
    
    if (tableView == self.SecondCategoryTable) {
        
        
        UITableViewHeaderFooterView *header = (UITableViewHeaderFooterView *)view;
        
        header.textLabel.textColor = color_custom(@"333333");
        
        header.contentView.backgroundColor = color_custom(@"FFFFFF");
        
        if(!_isUpScroll && (self.selectSection - section) == 1){
            
            //最上面組頭(不一定是第一個組頭,指最近剛被頂出去的組頭)又被拉回來
            self.selectSection = section;

            
        }
    }
    
}


- (void)tableView:(UITableView *)tableView didEndDisplayingHeaderView:(UIView *)view forSection:(NSInteger)section{
    
    if(!_isFirstLoad && _isUpScroll){
        
        self.selectSection = section + 1;
        
        //最上面的組頭被頂出去
        
    }
    
}
#pragma mark --tableViewDelegate
-(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
    if (tableView == self.FirstCategoryTable) {
        return 1;
    }else{
        return self.fModels[self.selectIndex].sModelDicts.allKeys.count;
    }
}
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    if (tableView == self.FirstCategoryTable) {
        return self.fModels.count;
    }else{
//每個section都只有一個cell,裡面是collection
        return  1;
    }
}
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    if (tableView == self.FirstCategoryTable) {
        return 49;
    }else{
//        根據索引取出每個key對應的高度
        NSString *key = self.fModels[self.selectIndex].secondIndexKeyArr[indexPath.section];
        CGFloat height = [self.fModels[self.selectIndex].secondHeightDicts[key] doubleValue];
        return height;
        
    }
}

- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
{
    if (tableView == self.SecondCategoryTable) {
        return self.fModels[self.selectIndex].secondIndexKeyArr[section];
    }
    return nil;
}
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    if (tableView == self.FirstCategoryTable) {
        
        
        FirstCategoryCell *cell = [tableView dequeueReusableCellWithIdentifier:FirstCategoryCellID];
        cell.leftLine.hidden = YES;
        cell.selectionStyle = UITableViewCellSelectionStyleNone;
        if (indexPath.row == self.selectCellRow) {
            cell.leftLine.hidden = NO;
        }
        [cell setUpModel:self.fModels[indexPath.row]];
        return cell;
    }else{
        SecondCategoryCell *cell = [tableView dequeueReusableCellWithIdentifier:SecondCategoryCellID];
        NSString *key =  self.fModels[self.selectIndex].secondIndexKeyArr[indexPath.section];
        [cell setupArrModel:self.fModels[self.selectIndex].sModelDicts[key]];
        return cell;
    }
    
}
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    if (tableView == self.FirstCategoryTable) {
        FirstCategoryCell *cell = [self.FirstCategoryTable cellForRowAtIndexPath:indexPath];
        self.FirstCell.leftLine.hidden = YES;
        cell.leftLine.hidden = NO;
        self.selectCellRow = indexPath.row;
        self.FirstCell = cell;
        self.selectIndex = indexPath.row;
        [self reloadIndex];
    }else{
        
    }
}


二級分類的 Cell 包含collection相關方法

//載入資料。 注意要重新整理 collectionview
-(void)setupArrModel:(NSArray <sModel *>*)model{
    self.smodelArr = model;
    [self.SecondCollectionView reloadData];
}

#pragma mark - collection delegate

-(NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView
{
    return 1;
}

-(NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
{
    NSLog(@"self.smodelArr.count = %ld",self.smodelArr.count);
    return self.smodelArr.count;
}

-(UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
    NSTCollectionCell * cell = [collectionView dequeueReusableCellWithReuseIdentifier:cellID forIndexPath:indexPath];
    
    sModel * smodel = [self.smodelArr objectAtIndex:indexPath.row];
//    sModel * smodel = [self.smodelArr ]
    [cell setUPsModel:smodel];
    [self.cellSet addObject:cell];
    if([self.delegate respondsToSelector:@selector(selectSModel)])
    {
        sModel * model = [self.delegate selectSModel];
        if([smodel isEqual:model])
        {
            SFModel * model = [[SFModel alloc] init];
            model.s_fModel = self.myModel;
            model.s_sModel = smodel;
            
            if([self.delegate respondsToSelector:@selector(cellDefaultModel:inCell:)])
            {
                [self.delegate cellDefaultModel:model inCell:cell];
            }
        }
        else
            [cell setToNormalAnimation:NO];
    }
    
    return cell;
}

-(void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath
{
    NSTCollectionCell * cell = (NSTCollectionCell *)[collectionView cellForItemAtIndexPath:indexPath];
    
    if([self.delegate respondsToSelector:@selector(canSelectModel:)])
    {
        if(self.single)
        {
            [self.cellSet enumerateObjectsUsingBlock:^(NSTCollectionCell * obj, BOOL * _Nonnull stop) {
                
                [obj setToNormalAnimation:YES];
                
            }];
        }
        SFModel * model = [[SFModel alloc] init];
        model.s_fModel = self.myModel;
        model.s_sModel = cell.mySModel;
        BOOL can = [self.delegate canSelectModel:model];
        if(can)
        {
            if([self.delegate respondsToSelector:@selector(selectModel:inCell:)])
            {
                [self.delegate selectModel:model inCell:cell];
            }
            else
                [cell selectModel];
        }
    }
    else
        [cell selectModel];
}

#pragma mark - UICollectionViewDelegateFlowLayout

-(CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath
{
    return CGSizeMake(self.frame.size.width/3 - 8, 60);
}

因為是在開發中寫的程式碼,就不上 demo 了,需要的可以參考一下。

相關文章