长表格的实现

主要功能特点:
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 
#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 *titleArray;
/**
 *  表左-固定(不传则无固定)
 *  表的左边部分,与表头不重合
 */
@property(nonatomic, strong) NSArray *leftArray;
/**
 *  主要数据
 *  第一层,有多少行
 *  第二层,每一行的数据
 */
@property(nonatomic, strong) NSArray *> *dataArray;
/**
 *  列与列之间的宽度,和表头数组等量
 *  如果不传,会引起适配问题
 */
@property(nonatomic, strong) NSArray *widthArray;
/**
 *  行与行之间的高度,和主要数据+1等量
 *  如果不传,默认高度是44
 */
@property(nonatomic, strong) NSArray *heightArray;
/**
 *  输入状态时的占位文本
 *  如果长度为0,默认是按钮;长度大于0,则为输入框
 */
@property(nonatomic, strong) NSArray *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 ()
/**
 *  左上角的第一个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 *)titleArray {
    _titleArray = titleArray;
}
- (void)setLeftArray:(NSArray *)leftArray {
    _leftArray = leftArray;
}
- (void)setWidthArray:(NSArray *)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 *)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

你可能感兴趣的:(长表格的实现)