美文网首页
UI技术总结--UITableView

UI技术总结--UITableView

作者: 徐老茂 | 来源:发表于2018-11-27 11:57 被阅读32次
    UITableView是我们用的最多的一个控件,所以对于UITableView重用机制的掌握也就成了我们必须了解的一个知识点,对于UITableView重用机制的剖析网上已经有相当多的文章了,这里我结合图片和代码再来阐述一遍.
    cell = [tableView dequeueReusableCellWithIdentifier:@"identifier"];
    

    我们在编写代码的时候经常会写到这样一段话,根据一个指定的标识符来获取到一个可重用的cell,那么这个实际上就使用到了UITableView的重用机制,用一张图来解释一下.


    重用

    实线包含起来的部分是显示到屏幕上的部分,其中Cell3,cell4,cell5是完全显示到屏幕上的,而cell2和cell6是只有一部分显示到当前屏幕.如果当前正是向上滑动的一个过程的话,那么cell1这个时候已经被加入到了重用池当中,因为它已经滚出到屏幕外了,如果继续向上滑动的话,那么这个时候Cell7就会从重用池中根据指定的重用标识符来取出一个可重用的cell.如果cell1到cell7全部用同一个重用标识符的话,那么cell7就可以复用cell1创建的内存,这样就达到了这样一个重用的目的.

    接下来,通过自定义一个控件来更深入理解UITableView的重用机制

    首先,创建了一个叫ViewReusePool的类用于实现重用机制.

    #import <Foundation/Foundation.h>
    #import <UIKit/UIKit.h>
    // 实现重用机制的类
    @interface ViewReusePool : NSObject
    // 从重用池中取出一个可重用的View
    -(UIView *)dequeueReusableView;
    // 向重用池中添加一个view
    -(void)addUsingView:(UIView *)view;
    // 重置方法,将当前使用中的视图移动到可重用队列当中
    -(void)reset;
    @end
    #import "ViewReusePool.h"
    @interface ViewReusePool()
    // 等待使用的队列
    @property(nonatomic,strong) NSMutableSet *waitUsedQueue;
    // 使用中的队列
    @property(nonatomic,strong) NSMutableSet *usingQueue;
    @end
    @implementation ViewReusePool
    -(instancetype)init
    {
        self = [super init];
        if (self) {
            _waitUsedQueue = [NSMutableSet set];
            _usingQueue = [NSMutableSet set];
        }
        return self;
    }
    
    -(UIView *)dequeueReusableView
    {
        UIView *view = [_waitUsedQueue anyObject];
        if (!view) {
            return nil;
        }
        // 进行队列移动
        [_waitUsedQueue removeObject:view];
        [_usingQueue addObject:view];
        return view;
    }
    
    -(void)addUsingView:(UIView *)view
    {
        if (!view) {
            return;
        }
        //添加视图到使用中的队列
        [_usingQueue addObject:view];
    }
    
    -(void)reset
    {
        UIView *view = nil;
        while ((view = [_usingQueue anyObject])) {
            // 从使用队列中移除
            [_usingQueue removeObject:view];
            // 加入等待队列中
            [_waitUsedQueue addObject:view];
        }
    }
    @end
    

    这部分的代码简单明了的描述了重用机制的实现原理.接下来自定义一个使用了ViewReusePool重用机制的IndexedTableView.实现了类似索引条的功能.

    #import <UIKit/UIKit.h>
    @protocol IndexedTableViewDataSource <NSObject>
    // 获取一个tableview的字母索引条数据的方法
    - (NSArray<NSString *> *)indexTitlesForIndexTableView:(UITableView *)tableView;
    @end
    @interface IndexedTableView : UITableView
    @property (nonatomic,weak) id<IndexedTableViewDataSource> indexDataSource;
    @end
    #import "IndexedTableView.h"
    #import "ViewReusePool.h"
    @interface IndexedTableView ()
    {
        UIView *containerView;
        ViewReusePool *reusePool;
    }
    
    @end
    
    @implementation IndexedTableView
    
    -(void)reloadData{
        [super reloadData];
        
        if (!containerView) {
            containerView = [[UIView alloc]initWithFrame:CGRectZero];
            containerView.backgroundColor = [UIColor whiteColor];
            // 避免索引条随着tableView滚动
            [self.superview insertSubview:containerView aboveSubview:self];
        }
        if (!reusePool) {
            reusePool = [[ViewReusePool alloc]init];
        }
        // 标记所有视图为可重用状态
        [reusePool reset];
        // reload字母索引条
        [self reloadIndexBar];
    }
    
    -(void) reloadIndexBar
    {
        // 获取字母索引条的显示内容
        NSArray <NSString *> *arrayTitles = nil;
        if ([self.indexDataSource respondsToSelector:@selector(indexTitlesForIndexTableView:)]) {
            arrayTitles = [self.indexDataSource indexTitlesForIndexTableView:self];
        }
        //判断字母索引条是否为空
        if (!arrayTitles || arrayTitles.count <= 0) {
            containerView.hidden = YES;
            return;
        }
        NSUInteger count = arrayTitles.count;
        CGFloat buttonWidth = 60;
        CGFloat buttonHeight = self.frame.size.height / count;
        
        for (int i = 0; i < arrayTitles.count; i++) {
            NSString *title = arrayTitles[i];
            //从重用池中取出一个button来
            UIButton *button = (UIButton *)[reusePool dequeueReusableView];
            // 如果没有可重用的button就创建一个
            if (!button) {
                button = [[UIButton alloc]initWithFrame:CGRectZero];
                button.backgroundColor = [UIColor whiteColor];
                //注册button到重用池中
                [reusePool addUsingView:button];
                NSLog(@"新建了一个button");
            }else{
                NSLog(@"button 重用了");
            }
            //添加button到父视图控件
            [containerView addSubview:button];
            [button setTitle:title forState:UIControlStateNormal];
            [button setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
            //设置button的坐标
            [button setFrame:CGRectMake(0, i * buttonHeight, buttonWidth, buttonHeight)];
        }
        containerView.hidden = NO;
        containerView.frame = CGRectMake(self.frame.origin.x + self.frame.size.width - buttonWidth, self.frame.origin.y, buttonWidth, self.frame.size.height);
    }
    @end
    

    最后在viewcontroller中添加IndexedTableView并实现自定义的重用功能.

    #import "ViewController.h"
    #import "IndexedTableView.h"
    @interface ViewController ()<UITableViewDelegate,UITableViewDataSource,IndexedTableViewDataSource>
    {
        IndexedTableView *tableView;//带有索引条的tableView
        UIButton *button;
        NSMutableArray *dataSource;
    }
    @end
    
    @implementation ViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        //创建一个tableview
        tableView = [[IndexedTableView alloc]initWithFrame:CGRectMake(0, 60, self.view.frame.size.width, self.view.frame.size.height - 60) style:UITableViewStylePlain];
        tableView.delegate = self;
        tableView.dataSource = self;
        // 设置tableview的索引数据源
        tableView.indexDataSource = self;
        [self.view addSubview:tableView];
        //创建一个button
        button = [UIButton buttonWithType:UIButtonTypeSystem];
        button.frame = CGRectMake(0, 20, self.view.frame.size.width, 40);
        button.backgroundColor = [UIColor redColor];
        [button setTitle:@"reloadTable" forState:UIControlStateNormal];
        [button addTarget:self action:@selector(doAction) forControlEvents:UIControlEventTouchUpInside];
        [self.view addSubview:button];
        
        //数据源
        dataSource = [NSMutableArray array];
        for (int i = 0; i < 100; i++) {
            [dataSource addObject:@(i + 1)];
        }
    }
    
    #pragma mark IndexedTableViewDataSource
    -(NSArray<NSString *> *)indexTitlesForIndexTableView:(UITableView *)tableView
    {
        //奇数次调用返回6个字母,偶数次调用返回11个
        static BOOL change = NO;
        if (change) {
            change = NO;
            return @[@"A",@"B",@"C",@"D",@"E",@"F",@"G",@"H",@"I",@"J",@"K"];
        }else{
            change = YES;
            return @[@"A",@"B",@"C",@"D",@"E",@"F"];
        }
    }
    #pragma mark UITableViewDataSource
    
    -(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
    {
        return dataSource.count;
    }
    
    -(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
    {
        static NSString *identifier = @"reuseId";
        UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:identifier];
        // 如果重用池当中没有可重用的cell,那么创建一个cell
        if (!cell) {
            cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:identifier];
        }
        // 文案设置
        cell.textLabel.text = [[dataSource objectAtIndex:indexPath.row]stringValue];
        return cell;
    }
    -(void)doAction{
        [tableView reloadData];
    }
    @end
    

    运行一下可以看出是这样一个效果.



    点击reloadData会从重用池中取出可复用的button,没有的话就创建,看看控制台的打印.

    刚进去的时候,因为重用池中没有button所以会创建6个button,并添加到重用池中.如下图所示.
    点击reloadTable按钮会复用之前的6个button,并创建5个新的button添加到重用池中.如下图所示

    这个时候,重用池中已经有11个button了,如果当前屏幕的button数不超过这个数的话,就会一直复用重用池中的button不会再创建新的button,这样减少了内存的开销.


    相关文章

      网友评论

          本文标题:UI技术总结--UITableView

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