iOS端UI控件-循环滚动表格

4 minute read

前言

学习iOS开发一个多月了,最近做项目的时候碰到一个这样的需求:一个展示数据的面板,里面有一个表格,表格的数据循环滚动。类似下图:

我这个人又比较懒,一开始直接上Google看看有没有现成的控件使用,翻了半天也没找到一些能用的东西,基本上都满足不了我的需求,然后我就打算自己写一个用吧,有一篇文章的思路倒是给了我很大的启发,这篇文章讲述的代码使用swift写的,由于我用的是Objective-C,最后我自己封装了一个控件。

思路

  1. 控件:一个父view,一个headerView,两个TableView。其中headerView是表头,两个TableView加载的是相同的数据。父视图的clipsToBounds属性一定要设置为YES。
  2. 滚动:如何让它滚动起来呢?这里我用的是一个定时器,通过控制时间间隔和位移距离,让其滚动的效果平滑顺畅。
  3. 循环:如何让其循环滚动呢?在第1步中使用到的两个TableView,把这两个TableView上下拼接起来,当地一个TableView滚动出视图外的时候,就把它移到第二个TableView的下面,如此循环往复就实现的循环滚动。

代码

  1. 新建ScrollTableView.hScrollTableView.m,其中ScrollTableView.h代码如下:

    #import <UIKit/UIKit.h>
       
    NS_ASSUME_NONNULL_BEGIN
       
    @interface LSWScrollTableView : UIView
       
    /**
     *
     * @param frame 位置
     * @param columns 表头数据 [{key:"", value:"", width:""}, ...] key为表头名字,value为与表头对应的字段,width为单元格宽度
     * @param data 表格数据
     * @param cellHeight 单元格高度
     * @param headerHeight 表头高度
     * @param cellBackgroundColor 单元格背景色只支持斑马纹 需要传两个UIColor组成的数组,例:@[[UIColor redColor], [UIColor blueColor]]
     * @param headerBackgroundColor 表头背景色
     * @param headerTextColor 表头文字颜色
     * @param cellTextColor 单元格文字颜色
     * @return 返回一个UIView
     */
    - (instancetype)initWithFrame:(CGRect)frame columns:(NSArray *)columns data:(NSArray *)data cellHeight: (CGFloat)cellHeight headerHeight:(CGFloat)headerHeight cellBackgroundColor:(NSArray *)cellBackgroundColor headerBackgroundColor:(UIColor *)headerBackgroundColor headerTextColor:(UIColor *)headerTextColor cellTextColor:(UIColor *)cellTextColor;
       
    /**
     * 关闭滚动
     */
    - (void)stopScroll;
       
    /**
     * 打开滚动
     */
    - (void)openScroll;
       
    @end
       
    NS_ASSUME_NONNULL_END
    
  2. ScrollTableView.m代码如下:

    #import "LSWScrollTableView.h"
       
    #define COLOR(R, G, B, A) [UIColor colorWithRed:R/255.0 green:G/255.0 blue:B/255.0 alpha:A]
       
    @interface LSWScrollTableView ()<UITableViewDataSource, UITableViewDelegate>
       
    @property (nonatomic, strong) NSTimer *timer; ///定时器,用于开启、关闭滚动
    @property (nonatomic, strong) NSArray *data; ///传入的原始数据
    @property (nonatomic) CGFloat cellHeight; ///单元格高度
    @property (nonatomic, strong) NSArray *columns; ///表头数据
    @property (nonatomic) CGFloat headerHeight; ///表头高度
    @property (nonatomic, strong) NSArray *cellBackgroundColor; ///单元格背景色
    @property (nonatomic, strong) UIColor *cellTextColor; ///文字颜色
    @property (nonatomic, strong) UITableView *tableView1; ///表格1
    @property (nonatomic, strong) UITableView *tableView2; ///表格2
       
    @end
       
    @implementation LSWScrollTableView
       
    static NSString *const cellId = @"cellId";
       
    - (instancetype)initWithFrame:(CGRect)frame columns:(NSArray *)columns data:(NSArray *)data cellHeight: (CGFloat)cellHeight headerHeight:(CGFloat)headerHeight cellBackgroundColor:(NSArray *)cellBackgroundColor headerBackgroundColor:(UIColor *)headerBackgroundColor headerTextColor:(UIColor *)headerTextColor cellTextColor:(UIColor *)cellTextColor
    {
        self = [super initWithFrame:frame];
        if (self) {
            self.data = data;
            self.columns = columns;
            self.cellHeight = cellHeight;
            self.headerHeight = headerHeight;
            self.cellBackgroundColor = cellBackgroundColor;
            self.cellTextColor = cellTextColor;
            self.backgroundColor = COLOR(0, 0, 0, 0);
               
            ///表头部分
            UIView *headerView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, frame.size.width, self.headerHeight)];
            headerView.backgroundColor = headerBackgroundColor;
            if (columns && columns.count > 0) {
                __block CGFloat headerX = 0;
                [self.columns enumerateObjectsUsingBlock:^(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
                    UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(headerX, 0, [obj[@"width"] floatValue], self.headerHeight)];
                    label.text = obj[@"key"];
                    label.textColor = headerTextColor;
                    label.textAlignment = NSTextAlignmentCenter;
                    label.backgroundColor = COLOR(0, 0, 0, 0);
                    headerX += [obj[@"width"] floatValue];
                    [headerView addSubview:label];
                }];
            } else {
                NSLog(@"columns不能为空");
            }
            [self addSubview:headerView];
               
            ///表格部分
            UIView *tableView = [[UIView alloc] initWithFrame:CGRectMake(0, self.headerHeight, frame.size.width, frame.size.height - self.headerHeight)];
            tableView.clipsToBounds = YES;
            tableView.backgroundColor = COLOR(0, 0, 0, 0);
            self.tableView1 = [[UITableView alloc] initWithFrame:CGRectMake(0, 0, frame.size.width, self.data.count * self.cellHeight)];
            self.tableView2 = [[UITableView alloc] initWithFrame:CGRectMake(0, self.tableView1.frame.origin.y + self.tableView1.frame.size.height, frame.size.width, self.data.count * self.cellHeight)];
            self.tableView1.backgroundColor = COLOR(0, 0, 0, 0);
            self.tableView2.backgroundColor = COLOR(0, 0, 0, 0);
            self.tableView1.delegate = self;
            self.tableView1.dataSource = self;
            self.tableView2.delegate = self;
            self.tableView2.dataSource = self;
            self.tableView1.scrollEnabled = NO; ///禁止表格1自带的滚动
            self.tableView2.scrollEnabled = NO; ///禁止表格2自带的滚动
            [tableView addSubview:self.tableView1];
            [tableView addSubview:self.tableView2];
            [self addSubview:tableView];
       
            ///开始滚动
            [self openScroll];
        }
           
           
        return self;
    }
       
    #pragma mark - UITableViewDelegate
       
    ///渲染数据
    - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
        UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellId];
        if (cell == nil) {
            cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellId];
        }
        __block CGFloat headerX = 0;
        [self.columns enumerateObjectsUsingBlock:^(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
            UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(headerX, 0, [obj[@"width"] floatValue], self.headerHeight)];
            label.text = self.data[indexPath.row][obj[@"value"]];
            label.textAlignment = NSTextAlignmentCenter;
            label.backgroundColor = COLOR(0, 0, 0, 0);
            label.textColor = self.cellTextColor;
            headerX += [obj[@"width"] floatValue];
            [cell addSubview:label];
        }];
           
        ///这里我把单元格颜色做成斑马纹的样式
        if (self.data.count % 2 == 1) {
            if (tableView == self.tableView1) {
                if (indexPath.row % 2 == 1) {
                    cell.backgroundColor = self.cellBackgroundColor[0];
                } else {
                    cell.backgroundColor = self.cellBackgroundColor[1];
                }
            } else if (tableView == self.tableView2) {
                if (indexPath.row % 2 == 0) {
                    cell.backgroundColor = self.cellBackgroundColor[0];
                } else {
                    cell.backgroundColor = self.cellBackgroundColor[1];
                }
            }
        } else if (self.data.count % 2 == 0) {
            if (indexPath.row % 2 == 1) {
                cell.backgroundColor = self.cellBackgroundColor[0];
            } else {
                cell.backgroundColor = self.cellBackgroundColor[1];
            }
        }
        cell.selectionStyle = UITableViewCellSelectionStyleNone; ///禁止选中
        return cell;
    }
       
    ///cell个数
    - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
        return self.data.count;
    }
       
    ///分区个数
    - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
        return 1;
    }
       
    #pragma mark - UITableViewDataSource
       
    ///cell高度
    - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
        return self.cellHeight;
    }
       
       
    ///停止滚动
    - (void)stopScroll{
        [self.timer invalidate];
        self.timer = nil;
    }
       
    ///开启滚动
    - (void)openScroll{
        if (self.timer == nil) {
            self.timer = [NSTimer timerWithTimeInterval:0.03 repeats:YES block:^(NSTimer *timer) {
                CGRect newTable1ViewFrame = self.tableView1.frame;
                newTable1ViewFrame.origin.y -= 0.9;
                if (newTable1ViewFrame.origin.y < -(self.tableView1.frame.size.height)) {
                    newTable1ViewFrame.origin.y = self.tableView1.frame.size.height;
                }
                self.tableView1.frame = newTable1ViewFrame;
       
                CGRect newTable2ViewFrame = self.tableView2.frame;
                newTable2ViewFrame.origin.y -= 0.9;
                if (newTable2ViewFrame.origin.y < -(self.tableView1.frame.size.height)) {
                    newTable2ViewFrame.origin.y = self.tableView1.frame.size.height;
                }
                self.tableView2.frame = newTable2ViewFrame;
            }];
            [[NSRunLoop currentRunLoop] addTimer:self.timer forMode:NSDefaultRunLoopMode];
        } else {
            NSLog(@"已经在滚动中,请勿重复开启");
        }
    }
       
    /*
     // Only override drawRect: if you perform custom drawing.
     // An empty implementation adversely affects performance during animation.
     - (void)drawRect:(CGRect)rect {
     // Drawing code
     }
     */
       
    @end
    

使用效果图

  1. 效果图:

  2. 示例数据:

    NSArray *columns = @[
            @{
                @"key": @"项目类型",
                @"value": @"projectType",
                @"width": [NSNumber numberWithInt:150]
            },
            @{
                @"key": @"客户名称",
                @"value": @"customerName",
                @"width": [NSNumber numberWithInt:250]
            },
            @{
                @"key": @"电梯数量",
                @"value": @"elevatorNum",
                @"width": [NSNumber numberWithInt:150]
            }
        ];
        NSArray *data = @[
            @{
                @"projectType": @"T类",
                @"customerName": @"南昌轨道交通二号线",
                @"elevatorNum": @"1"
            },
            @{
                @"projectType": @"T类",
                @"customerName": @"南昌轨道交通三号线",
                @"elevatorNum": @"2"
            },
            @{
                @"projectType": @"T类",
                @"customerName": @"南昌轨道交通四号线",
                @"elevatorNum": @"3"
            },
            @{
                @"projectType": @"T类",
                @"customerName": @"南昌轨道交通五号线",
                @"elevatorNum": @"4"
            },
            @{
                @"projectType": @"T类",
                @"customerName": @"南昌轨道交通六号线",
                @"elevatorNum": @"5"
            },
            @{
                @"projectType": @"T类",
                @"customerName": @"南昌轨道交通七号线",
                @"elevatorNum": @"6"
            },
            @{
                @"projectType": @"T类",
                @"customerName": @"南昌轨道交通八号线",
                @"elevatorNum": @"7"
            },
            @{
                @"projectType": @"T类",
                @"customerName": @"南昌轨道交通九号线",
                @"elevatorNum": @"8"
            },
            @{
                @"projectType": @"T类",
                @"customerName": @"南昌轨道交通十号线",
                @"elevatorNum": @"9"
            },
            @{
                @"projectType": @"T类",
                @"customerName": @"南昌轨道交通十一号线",
                @"elevatorNum": @"10"
            },
        ];
    
  3. 使用方式

    // rgb颜色转换(16进制->10进制)
    #define UIColorFromRGB(rgbValue) [UIColor colorWithRed:((float)((rgbValue & 0xFF0000) >> 16))/255.0 green:((float)((rgbValue & 0xFF00) >> 8))/255.0 blue:((float)(rgbValue & 0xFF))/255.0 alpha:1.0]
    #define ODD_COLOR UIColorFromRGB(0x12141e) ///表格奇数行背景颜色
    #define EVEN_COLOR UIColorFromRGB(0x18212f) ///表格偶数行背景颜色
    #define TABLE_HEADER_COLOR UIColorFromRGB(0x0f3b47) ///表头背景颜色
       
    ScrollTableView *scrollTableView = [[ScrollTableView alloc] initWithFrame:CGRectMake(0, 0, 550, 320) columns:columns data:data cellHeight:40 headerHeight:50 cellBackgroundColor:@[ODD_COLOR,EVEN_COLOR] headerBackgroundColor:TABLE_HEADER_COLOR headerTextColor:[UIColor whiteColor] cellTextColor:[UIColor whiteColor]];
    

写在最后

我已经把这个控件发布到cocoapods上面了,如果不想自己封装可以使用pod 'LSWScrollTableView',然后在项目中使用#import "LSWScrollTableView.h"就可以使用了,这是GitHub地址