长表格的实现

作者: 李贤立 | 来源:发表于2019-03-29 17:38 被阅读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
    

    相关文章

      网友评论

        本文标题:长表格的实现

        本文链接:https://www.haomeiwen.com/subject/mpqrbqtx.html