最近有一个需求,就是页面不仅需要上下滑动,同时需要支持左右滑动,有点类似于 Excel 表格的样式,如下所示:

不积跬步,无以至千里

一. 利用UIScrollView 上添加UITableView

首先,制作种效果有两种思路,一种是在UIScrollView 上添加UITableView, 设置self.scrollView.contentSize的宽高,根据tableView需要展示的内容的高度来设置;具体如下:

代码计较简单,需要注意的是self.scrollView的 Frame 和contentSize是不同的,前者是self.scrollView本身的视图区域,后者是scrollView中内容的大小,比如你要在 100*100的的区域内展示 1000*1000的图片,那么Frame 是 100*100,contentSize就是 1000*1000 代码如下:

#import "ImitationExcelVC.h"

@interface ImitationExcelVC ()

@property(nonatomic, strong) UIScrollView *scrollView;

@property(nonatomic, strong) UITableView *tableView;

@end

@implementation ImitationExcelVC

- (void)viewDidLoad {

[super viewDidLoad];

self.scrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 0, Device_Width, Device_Height)];

self.scrollView.delegate = self;

self.scrollView.backgroundColor = [UIColor whiteColor];

self.scrollView.contentSize = CGSizeMake(2000, Device_Height);

self.scrollView.bounces = NO;

[self.view addSubview:self.scrollView];

self.tableView = [[UITableView alloc] initWithFrame:CGRectMake(0, 0, 2000, Device_Height)];

self.tableView.delegate = self;

self.tableView.dataSource = self;

self.tableView.bounces = NO;

self.tableView.backgroundColor = [UIColor whiteColor];

[self.scrollView addSubview:self.tableView];

}

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView{

return 1;

}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{

return 200;

}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{

static NSString *identifierStr = @"cell";

UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:identifierStr];

if (!cell) {

cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:identifierStr];

}

// cell.frame = CGRectMake(0, 0, 980, 50);

cell.backgroundColor = [UIColor whiteColor];

NSString *str = @"鲁迅(20张)鲁迅,中国现代伟大的文学家、思想家和革命家。清朝光绪七年辛巳年八月初三(1881年9月25日)出生于浙江绍兴府会稽县东昌坊口新台门周家,原名周树人,后来在南京求学时学名为“周樟寿”,字豫山、豫亭、豫才。是中国现代小说的奠基人、中国现代文学的奠基人之一。至三十八岁,使用鲁迅为笔名。二弟周作人,三弟周建人,合称为“周氏三兄弟”。鲁迅1902年考取留日官费生,赴日本进东京的弘文学院学习。1904年初,入仙台医科专门学医,后弃医从文(详见《藤野先生》一文),回国从事文艺工作,希望用以改变国民精神。1905—1907年,参加革命党人的活动,发表了《摩罗诗力说》《文化偏至论》等论文。期间曾奉母命回国结婚,夫人朱安。1909年,与其弟周作人一起合译《域外小说集》,介绍外国文学,同年回国,先后在杭州、绍兴等地担任教师。";

cell.textLabel.text = [NSString stringWithFormat:@"%ld,%@",(long)indexPath.row,str];

return cell;

}

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{

return 50;

}

@end

二. 方式二的左侧标题不左右滑动

展示效果:

方式二具体效果和方式一不太一样,具体就是内容和方式一无差别,但是自身的左侧标题是不会滑动的,方式二代码比较复杂,具体逻辑如下:

UITableViewCell

//首先,UITableViewCell上划分为左右两个区域,左侧为标题,右侧为内容,内容滑动而标题不滑动

@interface WatchDataTableViewCell : UITableViewCell

@property(nonatomic, strong) UILabel *headerTitleLabel;

@property(nonatomic, strong) UIView *lineView;

@property(nonatomic, strong) UIView *rightView;

@property(nonatomic, strong) NSMutableArray *labelArr;

+ (instancetype)initWithTableView:(UITableView *)tableView withIndexPath:(NSIndexPath *)indexPath WithIndex:(NSInteger)number;

@end

+ (instancetype)initWithTableView:(UITableView *)tableView withIndexPath:(NSIndexPath *)indexPath WithIndex:(NSInteger)number {

static NSString *identifierStr = @"cell";

WatchDataTableViewCell *cell = (WatchDataTableViewCell *) [tableView dequeueReusableCellWithIdentifier:identifierStr];

if (cell == nil) {

cell = [[WatchDataTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:identifierStr];

//这里是展示左侧标题的Label

cell.headerTitleLabel = [[UILabel alloc] init];

[cell.contentView addSubview:cell.headerTitleLabel];

cell.headerTitleLabel.textColor = [UIColor blackColor];

cell.headerTitleLabel.backgroundColor = [UIColor clearColor];

//为了防止重复添加元素,添加数据之前先将数组清空,

cell.labelArr = [NSMutableArray arrayWithCapacity:0];

[cell.labelArr removeAllObjects];

//初始化内容 view

cell.rightView = [[UIView alloc] init];

for (int i = 0; i < number; i++) { //把rightLabel 加入到scrollView里面

UILabel *label = [[UILabel alloc] init];

[cell.rightView addSubview:label];

// 将数据添加到数组里面

[cell.labelArr addObject:label];

//cell.rightView是展示数据内容的 view

cell.rightView.backgroundColor = [UIColor clearColor];

}

}

cell.selectionStyle = UITableViewCellSelectionStyleNone;

cell.backgroundColor = [UIColor colorWithRed:255/255.0 green:255/255.0 blue:255/255.0 alpha:1.0];

return cell;

}

- (void)layoutSubviews {

[super layoutSubviews];

[self.headerTitleLabel setFrame:CGRectMake(0, 0, 145, 40)];

for (int i = 0; i < self.labelArr.count; i++) { //表示第 n 行,遍历当前行的所搜 label,

UILabel *label = self.labelArr[i];

label.frame = CGRectMake(detailLblWith * i, 0, detailLblWith, cellHeight - 2);

label.textAlignment = NSTextAlignmentCenter;

label.font = [UIFont systemFontOfSize:13];

label.textColor = [UIColor colorWithRed:22 / 255.0 green:117 / 255.0 blue:250 / 255.0 alpha:1.0];

}

}

这里的意思就是创建number 个 label 用于数据展示,添加到数组里面并在layoutSubviews 中将其frame 设置好,搭建好 UI 框架,等待数据填充

数据填充

此处展示核心代码

#define CELL_BACKGROUND_COLOR [UIColor grayColor]

#define detailLblWith 60

#define machineFactorWith 145

#define cellHeight 40

- (instancetype)initWithFrame:(CGRect)frame {

if (self = [super initWithFrame:frame]) {

//因为先加bigTableView后加smallScrollView才不至于让bigTableView的section header把smallScrollView挡住

self.tableView = [[UITableView alloc] initWithFrame:CGRectMake(0, 0, Device_Width, Device_Height - topLayout - bottomLayout - 54) style:UITableViewStylePlain];

[self addSubview:self.tableView];

// 标题,这是是水平方向的标题

self.titleScrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(machineFactorWith, 0, Device_Width - machineFactorWith, 30)];

self.titleScrollView.backgroundColor = [UIColor whiteColor];

self.titleScrollView.delegate = self;

// 内容的宽度,也即滑动的宽度

self.titleScrollView.contentSize = CGSizeMake(detailLblWith * 4, 30);

self.titleScrollView.showsHorizontalScrollIndicator = NO;

self.titleScrollView.bounces = NO;

// 数据标题

self.titleView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, detailLblWith * 4, 40)];

self.titleView.backgroundColor = [UIColor clearColor];

[self.titleScrollView addSubview:self.titleView];

[self addSubview:self.titleScrollView];

//为了防止 cell 高度问题导致 cell 不展示或者高度塌陷

if (@available(iOS 15.0, *)) {

self.tableView.sectionHeaderTopPadding = 0;

self.tableView.estimatedRowHeight = 40;

self.tableView.estimatedSectionHeaderHeight = 0;

self.tableView.estimatedSectionFooterHeight = 0;

}

self.tableView.delegate = self;

self.tableView.dataSource = self;

self.tableView.showsVerticalScrollIndicator = NO;

self.tableView.bounces = NO;//弹簧效果

// 此时scrollview的frame相对的是tableView的内容视图而不是其frame,所以高度应设置为CELL_HEIGHT 40 *CELL_COUNT 30 才不会显示不全

// frame的height小于contenSize的height的话scrollview就可以上下滑动,所以这里设置成一样

self.scrollview = [[UIScrollView alloc] initWithFrame:CGRectMake(machineFactorWith, 30, Device_Width - machineFactorWith, cellHeight * 64)];

self.scrollview.delegate = self;

// content高设置为scrollview的frame的高度则无法上下滑动

self.scrollview.contentSize = CGSizeMake(detailLblWith * 64, cellHeight * self.inDoorDataArr.count);

self.scrollview.bounces = NO;//弹簧效果

[self.tableView addSubview:self.scrollview];

// 加载titleView

for (int i = 0; i < 64; i++) {

UILabel *label = [[UILabel alloc] init];

[self.titleView addSubview:label];

if (i == 0) {

label.text = @"主机";

} else {

label.text = [NSString stringWithFormat:@"内机%d", i];

}

label.textAlignment = NSTextAlignmentCenter;

label.font = [UIFont systemFontOfSize:13];

//Masonry布局

[label mas_makeConstraints:^(MASConstraintMaker *make) {

make.centerY.equalTo(self.titleView.mas_centerY);

make.left.mas_equalTo(detailLblWith * i);

make.width.mas_equalTo(detailLblWith);

make.height.mas_equalTo(cellHeight);

}];

}

self.addedArr = [NSMutableArray arrayWithCapacity:self.inDoorDataArr.count];

for (int i = 0; i < self.inDoorDataArr.count; i++) {

[self.addedArr addObject:[NSNumber numberWithBool:NO]];

}

}

return self;

}

#pragma mark - tableview delegate and datasource

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {

WatchDataTableViewCell *cell = [WatchDataTableViewCell initWithTableView:tableView withIndexPath:indexPath WithIndex:64];

cell.headerTitleLabel.text = [NSString stringWithFormat:@"%@", self.inDoorDataArr[indexPath.row]];

cell.rightView.frame = CGRectMake(0, indexPath.row * cellHeight, 64 * detailLblWith, cellHeight);

//此处是设置 label 填充数据的函数,可忽略

[self assignValueToCell:cell with:indexPath];

// ⚠️注意点一

// 解决rightView重复添加到scrollview的 bug,找出属于scrollview不属于tableView的rightView,也即冗余数据,删除即可

NSArray *arrs = self.scrollview.subviews;

NSArray *arrs1 = self.tableView.subviews;

NSMutableArray *rightArr = [NSMutableArray arrayWithCapacity:0];

NSMutableArray *right1111Arr = [NSMutableArray arrayWithCapacity:0];

// 将所有的 cell 的 rightView 加入到数组中

for (int i = 0; i < arrs1.count; i++) {

if ([arrs1[i] isKindOfClass:[AUXWatchDataTableViewCell class]]) {

AUXWatchDataTableViewCell *tempcell = arrs1[i];

[rightArr addObject:tempcell.rightView];

}

}

// 比较两个数组,找出scrollview.subviews中存在而tableView.subviews;不存在的 view

for (int i = 0; i < arrs.count; i++) {

if ([arrs[i] isKindOfClass:[UIView class]]) {

UIView *temp = arrs[i];

if ([rightArr containsObject:temp]) {

} else {

[right1111Arr addObject:temp];

[temp removeFromSuperview];

}

}

}

// ⚠️注意点二

// 查看 cell 上是否已经包含元素,如果没有,则添加,这个一定要写在“解决rightView重复添加到scrollview的 bug”这段代码后面,否则这代码带存在的意义就没有,应该先看有没有重复添加,再看有没有缺失

if ([self.scrollview.subviews containsObject:cell.rightView]) {

// NSLog(@"=================");

} else {

[self.scrollview addSubview:cell.rightView];

}

return cell;

}

后记

不知道是我代码的原因,还是 oc 的原因,如果不写注意一处的代码,会出现元素重复添加的问题,也就是多个 label 叠加在了一个位置,导致字体很粗很重,同时,查看 UI 层级,你会神奇的发现,这些重复添加的元素都属于rightView 上的,也就是rightView 重复添加了;再仔细研究你又会神奇的发现,这些重复的rightView ,不属于任何一个 cell,你使用cell.subviews发现,cell 的子 view并没有这个rightView (是否是相同元素通过比较指针地址可以得到),所以没有办法通过使用cell.subviews去除,采用了注意一的方法;

如果有知道的大手子可以告知一声,(8胜感激先啵一个)

同时,如果把注意一下面的代码的注意二下面的代替换,你会又双叒会出现发现,部分cell上元素消失了!!消失了,消失了,(确实自己水平比较低,专业写 bug的),也就是rightView 以及其上的元素都消失了,有的 cell 上有元素,有的 cell 上没有元素,就很尴尬,到现在,这个问题还没有发现是为什么出现的,如果有知道的大手子可以告知一声,(8胜感激再啵一个),因为我最初是先写的⚠️注意点二,在这代码后面写的⚠️注意点一的代码,会出现部分cell上元素的问题,但是将两者替换,呕吼,bug 不见了,哈哈,当时考虑的是自己顺序写错了,应该按照上方的顺序来,一看果然如此,程序员要多思考啊。

入行已经一年了,2021.7.1–2022.7.6,370天,好像自己并没有怎么努力,一年以来,自己的水平也并没有质的飞跃,只是经历了从入门到能干活,想要成长为独当一面的技术人员,道阻且长啊,加油吧

学习,然后进步

相关文章

评论可见,请评论后查看内容,谢谢!!!
 您阅读本篇文章共花了: