概述
项目中要实现一个楼盘表,要求实现以下功能:
1、支持无数据的地方进行补位;
2、支持行列任意拓展;
3、支持支持多向联动;
4、支持上下表格合并。
后面经过研究,考虑通过UICollectionView的灵活嵌套的方式来进行实现。先看下最后实现效果:
功能实现
一个表格制作完总结起来主要就是三个要点:
- 视图布局
- 联动实现
- 数据构造
1、视图布局
由于需求要求要实现行列任意拓展,首先就想到UICollectionView的复用特性,有了复用再多的数据页面滑起来也会流程一些,还有就是需求要求上下表格支持合并,然后就可以利用UICollectionView设置item高度的方式来进行实现。但是一般手机屏幕一般不会大到把横向或者竖向的表格都能展示出来,所以就得要求楼盘表既能横向滑动又能竖向滑动。为了实现这个要求单个UICollectionView肯定也是实现不了要求了,单个UICollectionView只能基本实现横向或者竖向的滑动并复用了,所以这时候想到的是UICollectionView双层嵌套,UICollectionView中的UICollectionViewCell套UICollectionView,大的一个负责横向滑动,嵌套的负责竖向滑动。基本布局就是这样子了,可以看到整个页面基本上都是用UICollectionView来进行实现的。
如果想控制表格合并与否,合并哪些,这些就是根据数据源传值过来的数据来进行基本控制了,我们定义的规则是当highNominalLayer大于lowerNominalLayer时就进行表格合并,和并数量就是highNominalLayer与lowerNominalLayer差值加一,当然进行表格合并表格时还要注意把中间的间距加上,不然会出现对不齐的情况了。
- (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、联动实现
我们知道iOS中滑动的结果显现就是列表contentOffset偏移量值的改变,所以要实现联动的基本原理也就很简单了,只要滑动一个控件时把当前控件的contentOffset偏移量赋值给想要联动控件的contentOffset就OK了。这样两个控件的contentOffset偏移量始终一致,动与停也就会一致了,联动也就基本实现了。两个单独的控件这样赋值可以很快实现,但是由于表格中用了UICollectionView的嵌套,滑动的时候会出现复用的情况,所以如果想要简单的赋值估计会出现无从赋值的情况,怎么去解决这个赋值的问题呢,我的处理方式是循环遍历取出再赋值。
/** 处理多向滑动 */
- (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];
}
}
//循环取出赋值偏移量
- (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);
}
}
}
通过届定一个范围,通过滑动时调用cellForItemAtIndexPath方法取出collectionView再赋值contentOffset这样的方式来解决无法赋值的情况,以此来解决因为嵌套联动的问题。
3、数据构造
由于我们收到后台返回的数据是一维数组,并且是只返回有值的数据,空白表格是不返回数据的。但是由于制作的表格是用的嵌套的方式来实现的界面,所以需要根据后台返回数据来进行一下本地处理,转换成三维数组,而且空白表格也不返回数据,所以本地也要处理一下空白数据占位填充。为了更快捷的去数据处理,在这里推荐一个为Objective-C带来Linq风格的流畅查询的库LinqToObjectiveC。基本处理逻辑是通过NSSortDescriptor类按单元、单元内序号、楼层进行一次整体排序,排序好后进行分组切割,空值补位,去重,然后进行值的保存,最后实现了数据的构造。
/** 处理数据并进行传值 */
- (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];
//给数组添加排序规则
[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];
}
//去重保存 避免因为楼层合并出现重复值多的问题
NSArray *distinctLayers = [allLayerArr linq_distinct];
if (distinctLayers.count) {
[tempArr addObject:distinctLayers];
}
}
[dataArr addObject:tempArr];
}
// NSLog(@"modelArr:%@",self.houseArr);
//传入数据
self.topView.allKeysArr = allUnitKeys;
self.chartView.itemModel = layerModel;
self.chartView.allKeysArr = allUnitKeys;
self.chartView.dataArr = dataArr;
}
4、结尾
下载地址:ZHLinkageChartView,如果有更好的建议欢迎提出,喜欢的话记得给个star喽!