长表格的实现
2019-03-29 本文已影响86人
李贤立
主要功能特点:
1,宽和高都会超出屏幕外;
2,表头固定,最左一列固定可选;
3,表格的数据可编辑,并且监听改动的状态;
3,没有使用网络,纯数据库操作:增删改查和排序。
相比于前端开发的其它语言,iOS对于表格的支持表示太遗憾。不过第三方框架也有很多,我试着找了一个,从CocoaPods中集成,发现是Swift的。试着分析了一下,我只需一2个长表格,却没必要把人家整个框架拿过来吧,而且我还需要对表格数据进行编辑,恐怕人家的框架不一定能够支持。与其去GitHub上dao'chu研读英文,不如下定决心自己写一个。
长表格特点:
1,左上角第一栏必须是固定不变的;
2,表头一行在竖直方向固定(这一行必须有),
3,表左一列在水平方向固定(这一列可以没有);
4,表的其它部分就是主要数据的呈现,既可以左右滑动,又可以上下滑动。左右滑动,表头与之连动;上下滑动,表左与之连动。
我设计的方案如下:
1,左上角第一栏就是普通的View;
2,表左是一个和第一栏同宽的TableView,上下滑动;
3,右边是一个ScrollView,可以左右滑动;
4,ScrollView包含上面一个View和下面一个TableView。View用作表头,与第一栏等高,与ScrollView等宽;TableView用作主要数据的载体,与ScrollView等宽,其ContentSize与左边的TableView的ContentSize相等。
以下是对外提供的接口
其中TYZItemView、TYZItemButton、TYZItemField,是专为此表格自定义的控件,方便统一管理。
#import <UIKit/UIKit.h>
#define Default_Width 80
#define Default_Height 44
NS_ASSUME_NONNULL_BEGIN
@interface TYZItemField: UITextField
@end
@interface TYZItemButton: UIButton
@end
@interface TYZItemView: UIView
@end
@interface TYZTableToolView : UIView
/**
* 表头-固定
*/
@property(nonatomic, strong) NSArray<NSString *> *titleArray;
/**
* 表左-固定(不传则无固定)
* 表的左边部分,与表头不重合
*/
@property(nonatomic, strong) NSArray<NSString *> *leftArray;
/**
* 主要数据
* 第一层,有多少行
* 第二层,每一行的数据
*/
@property(nonatomic, strong) NSArray<NSArray<NSString *> *> *dataArray;
/**
* 列与列之间的宽度,和表头数组等量
* 如果不传,会引起适配问题
*/
@property(nonatomic, strong) NSArray<NSString *> *widthArray;
/**
* 行与行之间的高度,和主要数据+1等量
* 如果不传,默认高度是44
*/
@property(nonatomic, strong) NSArray<NSString *> *heightArray;
/**
* 输入状态时的占位文本
* 如果长度为0,默认是按钮;长度大于0,则为输入框
*/
@property(nonatomic, strong) NSArray<NSString *> *placeholderArray;
/**
* 点击每个表格的回调
* X:表左起第几列(表左为0)
* Y:表头起第几行(表头为0)
*/
@property(nonatomic) void(^clickTableItem)(NSInteger x, NSInteger y, id sender);
/**
* 是否编辑状态
*/
@property(nonatomic, assign) BOOL isEditing;
/**
* 是否添加状态
*/
@property(nonatomic, assign) BOOL isAdding;
/**
* 所有的确认按钮
*/
@property(nonatomic, strong) NSMutableDictionary *sureButtonDict;
/**
刷新UI
*/
- (void)setUpUI;
/**
刷新主要数据
*/
- (void)refreshMainView;
/**
刷新某一行
*/
- (void)refreshWithRow:(NSInteger)row;
@end
NS_ASSUME_NONNULL_END
有数据做了特殊处理,建议使用以下特点的数据传入:
dataArray = @[@"2019-03-06", @"70km", @"92#", @"50.3L", @"5.84¥", @"293.75¥", @"/", @"43056km", @"1148km", @"/", @"西溪加油站", @""]
widthArray = @[@"40", @"80", @"60", @"40", @"50", @"60", @"70", @"150", @"95", @"95", @"95", @"150", @"80"];
placeholderArray = @[@"", @"", @"km", @"#", @"L", @"¥", @"¥", @"", @"km", @"km", @"", @"请输入备注", @""];
titleArray = @[@"序号", @"日期", @"剩余里程", @"油号", @"加油量", @"油品单价", @"加油总价", @"本次平均油耗", @"汽车行驶总里程", @"预估可行驶里程", @"实际行驶里程", @"备注", @"计算上次油耗"];
leftArray = @[@"1"];
有以上数据就可以保证以下代码可以正常跑起来,不过只有表格只有一行。
具体的实现。
#import "TYZTableToolView.h"
@implementation TYZItemField
- (instancetype)init {
if (self = [super init]) {
self.textAlignment = NSTextAlignmentCenter;
self.font = [UIFont systemFontOfSize:12];
self.textColor = TYZLightColor;
}
return self;
}
@end
@implementation TYZItemButton
- (instancetype)init {
if (self = [super init]) {
[self setTitleColor:TYZLightColor forState:UIControlStateNormal];
self.titleLabel.font = [UIFont systemFontOfSize:12];
}
return self;
}
@end
@implementation TYZItemView
- (instancetype)init {
if (self = [super init]) {
self.layer.borderColor = TYZLightMMColor.CGColor;
self.layer.borderWidth = 0.5;
}
return self;
}
@end
@interface TYZTableToolView ()<UITableViewDelegate, UITableViewDataSource, UIScrollViewDelegate>
/**
* 左上角的第一个View
*/
@property(nonatomic, strong) TYZItemView *firstView;
/**
* 左边tableView
*/
@property(nonatomic, strong) UITableView *leftTableView;
/**
* scrollView
*/
@property(nonatomic, strong) UIScrollView *scrollView;
/**
* 右上边的b标题View
*/
@property(nonatomic, strong) UIView *titleView;
/**
* 右边tableView
*/
@property(nonatomic, strong) UITableView *rightTableView;
/**
* 总宽度
*/
@property(nonatomic, assign) CGFloat width;
/**
* 总高度
*/
@property(nonatomic, assign) CGFloat height;
@end
#define Tag_ScrollView 2019032700
#define Tag_LeftTableView 2019032701
#define Tag_RightTableView 2019032702
@implementation TYZTableToolView
- (instancetype)init {
if (self = [super init]) {
}
return self;
}
#pragma mark - setUI
- (void)setUpUI {
[self setUpNorthWest];
[self setUpLeftTableView];
[self setUpScrollView];
}
/**
设置左上角第一个完全固定的View
*/
- (void)setUpNorthWest {
if (self.leftArray) {
if (self.firstView.superview) {
[self.firstView removeFromSuperview];
}
[self.firstView.subviews enumerateObjectsUsingBlock:^(__kindof UIView * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
[obj removeFromSuperview];
}];
CGFloat height = Default_Height;
if (self.heightArray) {
height = [self.heightArray.firstObject floatValue];
}
CGFloat width = Default_Width;
if (self.widthArray) {
width = [self.widthArray.firstObject floatValue];
}
[self addSubview:self.firstView];
self.firstView.frame = CGRectMake(0, 0, width, height);
TYZItemButton *button = [[TYZItemButton alloc] init];
[self.firstView addSubview:button];
[button mas_makeConstraints:^(MASConstraintMaker *make) {
make.edges.equalTo(self.firstView);
}];
button.enabled = NO;
[button setTitle:self.titleArray.firstObject forState:UIControlStateNormal];
}
}
/**
设置左边的TableView
*/
- (void)setUpLeftTableView {
if (self.leftArray) {
if (self.leftTableView.superview) {
[self.leftTableView removeFromSuperview];
}
[self addSubview:self.leftTableView];
CGFloat topOffset = Default_Height;
if (self.heightArray) {
topOffset = [self.heightArray.firstObject floatValue];
}
CGFloat width = Default_Width;
if (self.widthArray) {
width = [self.widthArray.firstObject floatValue];
}
dispatch_async(dispatch_get_main_queue(), ^{
CGFloat tableViewHeight = CGRectGetHeight(self.bounds) - topOffset;
self.leftTableView.frame = CGRectMake(0, topOffset, width, tableViewHeight);
});
}
}
/**
设置右边的ScrollView
*/
- (void)setUpScrollView {
if (self.scrollView.superview) {
[self.scrollView removeFromSuperview];
}
[self addSubview:self.scrollView];
CGFloat topOffset = Default_Height;
__block CGFloat height = topOffset * self.leftArray.count;
if (self.heightArray) {
topOffset = [self.heightArray.firstObject floatValue];
height = self.height;
}
CGFloat leftOffset = Default_Width;
CGFloat width = leftOffset * (self.titleArray.count - 1);
if (self.widthArray) {
leftOffset = [self.widthArray.firstObject floatValue];
width = self.width - leftOffset;
}
dispatch_async(dispatch_get_main_queue(), ^{
CGFloat selfHeight = CGRectGetHeight(self.bounds);
if (height > selfHeight) {
height = selfHeight;
}
self.scrollView.contentSize = CGSizeMake(width, height);
});
[self.scrollView mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.equalTo(self).offset(leftOffset);
make.top.equalTo(self);
make.right.bottom.equalTo(self);
}];
if (self.titleView.superview) {
[self.titleView removeFromSuperview];
}
[self.titleView.subviews enumerateObjectsUsingBlock:^(__kindof UIView * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
[obj removeFromSuperview];
}];
[self.scrollView addSubview:self.titleView];
self.titleView.frame = CGRectMake(0, 0, width - leftOffset, topOffset);
CGFloat itemWidth = Default_Width;
CGFloat itemLeftOffset = 0;
for (NSInteger i = self.leftArray ? 1 : 0; i < self.titleArray.count; i ++) {
TYZItemView *itemView = [[TYZItemView alloc] init];
[self.titleView addSubview:itemView];
if (self.widthArray) {
itemWidth = [self.widthArray[i] floatValue];
}
itemView.frame = CGRectMake(itemLeftOffset, 0, itemWidth, topOffset);
itemLeftOffset += itemWidth;
TYZItemButton *button = [[TYZItemButton alloc] init];
[itemView addSubview:button];
button.enabled = NO;
[button setTitle:self.titleArray[i] forState:UIControlStateNormal];
[button mas_makeConstraints:^(MASConstraintMaker *make) {
make.edges.equalTo(itemView);
}];
}
dispatch_async(dispatch_get_main_queue(), ^{
if (self.rightTableView.superview) {
[self.rightTableView removeFromSuperview];
}
CGFloat tableViewHeight = CGRectGetHeight(self.bounds) - topOffset;
self.rightTableView.frame = CGRectMake(0, topOffset, width, tableViewHeight);
[self.scrollView addSubview:self.rightTableView];
});
}
#pragma mark - delegate
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return self.dataArray.count;
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
if (self.heightArray) {
return [self.heightArray[indexPath.row] floatValue];
}
return Default_Height;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
if (tableView.tag == Tag_LeftTableView) {
UITableViewCell *cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"Tag_LeftTableViewCell"];
TYZItemView *itemView = [[TYZItemView alloc] init];
[cell.contentView addSubview:itemView];
[itemView mas_makeConstraints:^(MASConstraintMaker *make) {
make.edges.equalTo(cell.contentView);
}];
TYZItemButton *button = [[TYZItemButton alloc] init];
[itemView addSubview:button];
[button mas_makeConstraints:^(MASConstraintMaker *make) {
make.edges.equalTo(itemView);
}];
button.enabled = NO;
[button setTitle:self.leftArray[indexPath.row] forState:UIControlStateNormal];
return cell;
}
UITableViewCell *cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"Tag_RightTableViewCell"];
CGFloat itemWidth = Default_Width;
CGFloat itemLeftOffset = 0;
CGFloat itemHeight = Default_Height;
for (NSInteger i = 0; i < self.dataArray[indexPath.row].count; i ++) {
TYZItemView *itemView = [[TYZItemView alloc] init];
[cell.contentView addSubview:itemView];
if (self.widthArray) {
if (self.leftArray) {
itemWidth = [self.widthArray[i + 1] floatValue];
} else {
itemWidth = [self.widthArray[i] floatValue];
}
}
if (self.heightArray) {
itemHeight = [self.heightArray[i + 1] floatValue];
}
itemView.frame = CGRectMake(itemLeftOffset, 0, itemWidth, itemHeight);
itemLeftOffset += itemWidth;
if (self.placeholderArray[i + 1].length > 0) {
TYZItemField *textField = [[TYZItemField alloc] init];
[itemView addSubview:textField];
[textField mas_makeConstraints:^(MASConstraintMaker *make) {
make.edges.equalTo(itemView);
}];
textField.text = self.dataArray[indexPath.row][i];
textField.tag = indexPath.row * 100 + i;
textField.placeholder = self.placeholderArray[i + 1];
textField.enabled = self.isEditing;
if (indexPath.row == 0 && self.isAdding) {
textField.enabled = YES;
textField.text = @"";
}
[textField addTarget:self action:@selector(didClickTableViewItem:) forControlEvents:UIControlEventEditingDidBegin];
} else {
TYZItemButton *button = [[TYZItemButton alloc] init];
[itemView addSubview:button];
button.tag = indexPath.row * 100 + i;
button.enabled = self.isEditing;
if (indexPath.row == 0 && self.isAdding) {
button.enabled = YES;
}
if (i == 11) {
[button mas_makeConstraints:^(MASConstraintMaker *make) {
make.center.equalTo(itemView);
make.width.mas_equalTo(itemWidth * 0.618);
make.height.mas_equalTo(itemHeight * 0.618);
}];
button.layer.cornerRadius = 5.0;
if ([self.dataArray[indexPath.row][i] isEqualToString:@""]) {
button.enabled = NO;
[button setTitleColor:TYZLightColor forState:UIControlStateNormal];
[button setBackgroundColor:TYZLightMMColor];
} else {
button.enabled = YES;
[button setTitleColor:TYZWhiteColor forState:UIControlStateNormal];
[button setBackgroundColor:TYZBlueColor];
}
[button setTitle:[@"确认" S] forState:UIControlStateNormal];
[self.sureButtonDict setValue:button forKey:[NSString stringWithFormat:@"%zd", indexPath.row]];
} else {
[button mas_makeConstraints:^(MASConstraintMaker *make) {
make.edges.equalTo(itemView);
}];
[button setTitle:self.dataArray[indexPath.row][i] forState:UIControlStateNormal];
}
[button addTarget:self action:@selector(didClickTableViewItem:) forControlEvents:UIControlEventTouchUpInside];
}
}
return cell;
}
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
if (scrollView.tag == Tag_LeftTableView) {
self.rightTableView.contentOffset = scrollView.contentOffset;
} else if (scrollView.tag == Tag_RightTableView) {
self.leftTableView.contentOffset = scrollView.contentOffset;
}
}
- (BOOL)tableView:(UITableView *)tableView shouldHighlightRowAtIndexPath:(NSIndexPath *)indexPath {
return NO;
}
- (void)didClickTableViewItem:(id)sender {
UIView *view = (UIView *)sender;
NSInteger x = view.tag % 100 + 1;
NSInteger y = view.tag / 100 + 1;
if (self.clickTableItem) {
self.clickTableItem(x, y, sender);
}
}
- (void)refreshMainView {
[self.rightTableView reloadData];
}
- (void)refreshWithRow:(NSInteger)row {
[self.rightTableView reloadRowsAtIndexPaths:@[[NSIndexPath indexPathForRow:row inSection:0]] withRowAnimation:UITableViewRowAnimationNone];
}
#pragma mark - setter & getter
- (void)setIsEditing:(BOOL)isEditing {
_isEditing = isEditing;
[self setUpUI];
[self.leftTableView reloadData];
[self.rightTableView reloadData];
}
- (void)setIsAdding:(BOOL)isAdding {
_isAdding = isAdding;
[self setUpUI];
[self.leftTableView reloadData];
[self.rightTableView reloadData];
}
- (void)setTitleArray:(NSArray<NSString *> *)titleArray {
_titleArray = titleArray;
}
- (void)setLeftArray:(NSArray<NSString *> *)leftArray {
_leftArray = leftArray;
}
- (void)setWidthArray:(NSArray<NSString *> *)widthArray {
_widthArray = widthArray;
__block CGFloat width = 0;
[widthArray enumerateObjectsUsingBlock:^(NSString * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
width += [obj floatValue];
}];
self.width = width;
}
- (void)setHeightArray:(NSArray<NSString *> *)heightArray {
_heightArray = heightArray;
__block CGFloat height = 0;
[heightArray enumerateObjectsUsingBlock:^(NSString * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
height += [obj floatValue];
}];
self.height = height;
}
- (void)setWidth:(CGFloat)width {
_width = width;
self.scrollView.contentSize = CGSizeMake(width, self.height);
}
- (void)setHeight:(CGFloat)height {
_height = height;
self.scrollView.contentSize = CGSizeMake(self.width, height);
}
- (NSMutableDictionary *)sureButtonDict {
if (_sureButtonDict == nil) {
_sureButtonDict = [NSMutableDictionary dictionary];
}
return _sureButtonDict;
}
- (TYZItemView *)firstView {
if (_firstView == nil) {
_firstView = [[TYZItemView alloc] init];
}
return _firstView;
}
- (UIView *)titleView {
if (_titleView == nil) {
_titleView = [[UIView alloc] init];
}
return _titleView;
}
- (UIScrollView *)scrollView {
if (_scrollView == nil) {
_scrollView = [[UIScrollView alloc] init];
_scrollView.delegate = self;
_scrollView.tag = Tag_ScrollView;
_scrollView.bounces = NO;
_scrollView.showsVerticalScrollIndicator = NO;
_scrollView.showsHorizontalScrollIndicator = NO;
_scrollView.contentInset = UIEdgeInsetsMake(0, 0, 0, 0);
}
return _scrollView;
}
- (UITableView *)leftTableView {
if (_leftTableView == nil) {
_leftTableView = [[UITableView alloc] init];
_leftTableView.delegate = self;
_leftTableView.dataSource = self;
_leftTableView.tag = Tag_LeftTableView;
_leftTableView.bounces = NO;
_leftTableView.showsVerticalScrollIndicator = NO;
_leftTableView.separatorStyle = UITableViewCellSeparatorStyleNone;
[_leftTableView registerClass:[UITableViewCell class] forCellReuseIdentifier:@"Tag_LeftTableViewCell"];
}
return _leftTableView;
}
- (UITableView *)rightTableView {
if (_rightTableView == nil) {
_rightTableView = [[UITableView alloc] init];
_rightTableView.delegate = self;
_rightTableView.dataSource = self;
_rightTableView.tag = Tag_RightTableView;
_rightTableView.bounces = NO;
_rightTableView.showsVerticalScrollIndicator = NO;
_rightTableView.separatorStyle = UITableViewCellSeparatorStyleNone;
[_rightTableView registerClass:[UITableViewCell class] forCellReuseIdentifier:@"Tag_RightTableViewCell"];
}
return _rightTableView;
}
@end