iOS 制作一個多聯(lián)動支持上下合并的表格

概述

項目中要實現(xiàn)一個樓盤表,要求實現(xiàn)以下功能:
1、支持無數(shù)據(jù)的地方進(jìn)行補(bǔ)位;
2、支持行列任意拓展;
3、支持支持多向聯(lián)動;
4、支持上下表格合并。
后面經(jīng)過研究,考慮通過UICollectionView的靈活嵌套的方式來進(jìn)行實現(xiàn)。先看下最后實現(xiàn)效果:


表格.jpeg

聯(lián)動.gif

功能實現(xiàn)

一個表格制作完總結(jié)起來主要就是三個要點(diǎn):

  • 視圖布局
  • 聯(lián)動實現(xiàn)
  • 數(shù)據(jù)構(gòu)造

1、視圖布局

由于需求要求要實現(xiàn)行列任意拓展,首先就想到UICollectionView的復(fù)用特性,有了復(fù)用再多的數(shù)據(jù)頁面滑起來也會流程一些,還有就是需求要求上下表格支持合并,然后就可以利用UICollectionView設(shè)置item高度的方式來進(jìn)行實現(xiàn)。但是一般手機(jī)屏幕一般不會大到把橫向或者豎向的表格都能展示出來,所以就得要求樓盤表既能橫向滑動又能豎向滑動。為了實現(xiàn)這個要求單個UICollectionView肯定也是實現(xiàn)不了要求了,單個UICollectionView只能基本實現(xiàn)橫向或者豎向的滑動并復(fù)用了,所以這時候想到的是UICollectionView雙層嵌套,UICollectionView中的UICollectionViewCell套UICollectionView,大的一個負(fù)責(zé)橫向滑動,嵌套的負(fù)責(zé)豎向滑動?;静季志褪沁@樣子了,可以看到整個頁面基本上都是用UICollectionView來進(jìn)行實現(xiàn)的。


布局.png

如果想控制表格合并與否,合并哪些,這些就是根據(jù)數(shù)據(jù)源傳值過來的數(shù)據(jù)來進(jìn)行基本控制了,我們定義的規(guī)則是當(dāng)highNominalLayer大于lowerNominalLayer時就進(jìn)行表格合并,和并數(shù)量就是highNominalLayer與lowerNominalLayer差值加一,當(dāng)然進(jìn)行表格合并表格時還要注意把中間的間距加上,不然會出現(xiàn)對不齊的情況了。

- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath
{
    if (self.itemArr.count > indexPath.row) {
        //處理表格合并
        ZHItemModel *infoModel = self.itemArr[indexPath.row];
        NSInteger difference = infoModel.highNominalLayer - infoModel.lowerNominalLayer + 1;
        CGFloat height = KITEMHEIGHT * difference + KSPACE * (difference - 1);
        return CGSizeMake(KITEMWIDTH, height);
    }
    return CGSizeMake(KITEMWIDTH, KITEMHEIGHT);
}

2、聯(lián)動實現(xiàn)

我們知道iOS中滑動的結(jié)果顯現(xiàn)就是列表contentOffset偏移量值的改變,所以要實現(xiàn)聯(lián)動的基本原理也就很簡單了,只要滑動一個控件時把當(dāng)前控件的contentOffset偏移量賦值給想要聯(lián)動控件的contentOffset就OK了。這樣兩個控件的contentOffset偏移量始終一致,動與停也就會一致了,聯(lián)動也就基本實現(xiàn)了。兩個單獨(dú)的控件這樣賦值可以很快實現(xiàn),但是由于表格中用了UICollectionView的嵌套,滑動的時候會出現(xiàn)復(fù)用的情況,所以如果想要簡單的賦值估計會出現(xiàn)無從賦值的情況,怎么去解決這個賦值的問題呢,我的處理方式是循環(huán)遍歷取出再賦值。

/** 處理多向滑動 */
- (void)itemCollectionViewDidScroll:(UIScrollView *)scrollView
{
    [self scrollViewDidScroll:scrollView];
}

- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
    if (scrollView == self.headCollectionView){
        self.bgCollectionView.contentOffset = CGPointMake(self.headCollectionView.contentOffset.x, 0);
    }else if (scrollView == self.bgCollectionView){
        self.headCollectionView.contentOffset = CGPointMake(self.bgCollectionView.contentOffset.x, 0);
    }
    
    if (scrollView == self.headCollectionView || scrollView == self.bgCollectionView || scrollView == self.leftCollectionView){
        [self updateCollectionViewOffictYWithView:self.leftCollectionView];
    }else{
        self.leftCollectionView.contentOffset = CGPointMake(0, scrollView.contentOffset.y);
        [self updateCollectionViewOffictYWithView:self.leftCollectionView];
    }
}

//循環(huán)取出賦值偏移量
- (void)updateCollectionViewOffictYWithView:(UIScrollView *)view
{
    NSIndexPath *indexPath = [self.bgCollectionView indexPathForItemAtPoint:self.bgCollectionView.contentOffset];
    NSInteger min = indexPath.section - self.refreshCount;
    NSInteger max = indexPath.section + self.refreshCount;
    max = max > self.dataArr.count ? self.dataArr.count : max;
    min = min > 0 ? min : 0;
    for (NSInteger i = min; i < max; i ++) {
        for (NSInteger j = 0; j < [self.dataArr[i] count]; j ++) {
            ZHBgCollectionViewCell *cell = (ZHBgCollectionViewCell *)[self.bgCollectionView cellForItemAtIndexPath:[NSIndexPath indexPathForItem:j inSection:i]];
            if (!cell) {
                continue;
            }
            if (cell.collectionView.contentOffset.y == view.contentOffset.y) {
                continue;
            }
            cell.collectionView.contentOffset = CGPointMake(0, view.contentOffset.y);
        }
    }
}

通過屆定一個范圍,通過滑動時調(diào)用cellForItemAtIndexPath方法取出collectionView再賦值contentOffset這樣的方式來解決無法賦值的情況,以此來解決因為嵌套聯(lián)動的問題。

3、數(shù)據(jù)構(gòu)造

由于我們收到后臺返回的數(shù)據(jù)是一維數(shù)組,并且是只返回有值的數(shù)據(jù),空白表格是不返回數(shù)據(jù)的。但是由于制作的表格是用的嵌套的方式來實現(xiàn)的界面,所以需要根據(jù)后臺返回數(shù)據(jù)來進(jìn)行一下本地處理,轉(zhuǎn)換成三維數(shù)組,而且空白表格也不返回數(shù)據(jù),所以本地也要處理一下空白數(shù)據(jù)占位填充。為了更快捷的去數(shù)據(jù)處理,在這里推薦一個為Objective-C帶來Linq風(fēng)格的流暢查詢的庫LinqToObjectiveC?;咎幚磉壿嬍峭ㄟ^NSSortDescriptor類按單元、單元內(nèi)序號、樓層進(jìn)行一次整體排序,排序好后進(jìn)行分組切割,空值補(bǔ)位,去重,然后進(jìn)行值的保存,最后實現(xiàn)了數(shù)據(jù)的構(gòu)造。

/** 處理數(shù)據(jù)并進(jìn)行傳值 */
- (void)getQueryWithLayersCount:(NSInteger)layersCount topLayers:(NSInteger)topLayers
{
    NSMutableArray *dataArr = [NSMutableArray array];
    
    NSString *strPath = [[NSBundle mainBundle] pathForResource:@"layer" ofType:@"geojson"];
    NSString *layerJson = [[NSString alloc] initWithContentsOfFile:strPath encoding:NSUTF8StringEncoding error:nil];
    NSMutableArray *jsonArr = [ZHItemModel mj_objectArrayWithKeyValuesArray:layerJson];
    
    ZHItemModel *layerModel = [[ZHItemModel alloc] init];
    //總樓層
    layerModel.layersCount = layersCount;
    layerModel.topLayers = topLayers;
    
    //key :按照unitNum屬性 升序排序
    NSSortDescriptor *sort = [NSSortDescriptor sortDescriptorWithKey:@"unitNum" ascending:YES];
    //unitNum 相同 按照uIndex屬性 升序排序
    NSSortDescriptor *sort1 = [NSSortDescriptor sortDescriptorWithKey:@"uIndex" ascending:YES];
    //uIndex 相同 按照onNominalLayer屬性 降序排序
    NSSortDescriptor *sort2 = [NSSortDescriptor sortDescriptorWithKey:@"onNominalLayer" ascending:NO];
    //給數(shù)組添加排序規(guī)則
    [jsonArr sortUsingDescriptors:@[sort,sort1,sort2]];
    //    NSLog(@"dataArr:%@",[AppHouseInfoModel mj_keyValuesArrayWithObjectArray:dataArr]);
    
    NSDictionary *dict = [jsonArr linq_groupBy:^id(id item) {
        ZHItemModel *model = (ZHItemModel *)item;
        return [NSString stringWithFormat:@"%ld",model.unitNum];
    }];
    
    //key排序
    NSMutableArray *unitKeysArr = [NSMutableArray array];
    for (NSString *key in dict.allKeys) {
        [unitKeysArr addObject:[NSNumber numberWithInteger:key.integerValue]];
    }
    NSArray *allUnitKeys = [unitKeysArr linq_sort];
    for (NSNumber *unitKey in allUnitKeys) {
        NSMutableArray *tempArr = [NSMutableArray array];
        NSDictionary *unitDict = [[dict objectForKey:unitKey.stringValue] linq_groupBy:^id(id item) {
            ZHItemModel *tempModel = (ZHItemModel *)item;
            return [NSString stringWithFormat:@"%ld",tempModel.uIndex];
        }];
        //key排序
        NSMutableArray *indexKeysArr = [NSMutableArray array];
        for (NSString *key in unitDict.allKeys) {
            [indexKeysArr addObject:[NSNumber numberWithInteger:key.integerValue]];
        }
        NSArray *allIndexKeys = [indexKeysArr linq_sort];
        for (NSNumber *indexKey in allIndexKeys) {
            NSMutableArray *allLayerArr = [NSMutableArray array];
            NSMutableArray *layerArr = [unitDict objectForKey:indexKey.stringValue];
            for (int i = 0; i < layersCount; i ++) {
                ZHItemModel *placeholderModel = [[ZHItemModel alloc] init];
                NSInteger layer = topLayers - i;
                if (layer <= 0 && topLayers > 0) {
                    layer = layer - 1;
                }
                placeholderModel.physicLayers = layer;
                for (ZHItemModel *layerModel in layerArr) {
                    //符合判斷條件賦值,不再展示空值
                    if (layerModel.lowerNominalLayer <= layer
                        && layer <= layerModel.highNominalLayer) {
                        placeholderModel = layerModel;
                        break;
                    }
                }
                [allLayerArr addObject:placeholderModel];
            }
            //去重保存 避免因為樓層合并出現(xiàn)重復(fù)值多的問題
            NSArray *distinctLayers = [allLayerArr linq_distinct];
            if (distinctLayers.count) {
                [tempArr addObject:distinctLayers];
            }
        }
        [dataArr addObject:tempArr];
    }
    //    NSLog(@"modelArr:%@",self.houseArr);
    //傳入數(shù)據(jù)
    self.topView.allKeysArr = allUnitKeys;
    self.chartView.itemModel = layerModel;
    self.chartView.allKeysArr = allUnitKeys;
    self.chartView.dataArr = dataArr;
}

4、結(jié)尾

下載地址:ZHLinkageChartView,如果有更好的建議歡迎提出,喜歡的話記得給個star嘍!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Swift1> Swift和OC的區(qū)別1.1> Swift沒有地址/指針的概念1.2> 泛型1.3> 類型嚴(yán)謹(jǐn) 對...
    cosWriter閱讀 11,629評論 1 32
  • 車上面那些小圖標(biāo),你真的都能說出個所以然來?有了這個,那都不是事兒!
    汽車家族閱讀 738評論 0 2
  • 我目前所在的橙子學(xué)院#30天專注橙長計劃#社群,讓我有一種很上進(jìn)的感受。里面都是寫作輸出的人,每周以打卡形式輸出文...
    敏同學(xué)談成長閱讀 464評論 0 0
  • 這封隨筆就隨便寫寫了,之前沒在簡書寫過東西。 關(guān)于來意 有打算在簡書更新自己的學(xué)習(xí)進(jìn)程,但近期估計不會有記錄。等合...
    Cinque_Peng閱讀 352評論 0 0
  • 內(nèi)存泄漏(memory leakage)概述 定義:在編寫應(yīng)用程序的時候,程序分配了一塊內(nèi)存,但已經(jīng)不再持有引用這...
    MagicDong閱讀 14,717評論 3 4

友情鏈接更多精彩內(nèi)容