UIKitiOS 大神之路iOS技术

UICollectionView高度宽度自适应缓存框架

2016-08-10  本文已影响9798人  js丶

前言

1、2演示内容完成,后续再更新

参考资料

UITableView

UICollectionView

UICollectionViewDataSource

UICollectionViewDelegate

UICollectionViewDelegateFlowLayout

UITableViewCell自适应高度框架

关于FDTemplateLayoutCell作者博客

设计思路

UITableView.png UICollectionView.png UICollectionViewDelegateFlowLayout.png UICollectionViewDataSource.png UICollectionViewDelegate.png LTCollectionViewLayout2.png layout cell(self-sizing cell).png 根据key(键)来区别每个UICollectionViewCell高度 +load.png UITableView/UICollectionView设置高度方法 UICollectionView+LTCollectionViewLayout(高度布局).png LTKeyedHeightCache(根据Key缓存UICollectionViewCell的高度).png

技术点

Category Use Method Swizzling

1.类中调用+load方法和-category中调用和+load方法调用顺序是怎样(类和分类同时重写load方法)?
答:+load的执行顺序是先类,后category,而category的+load执行顺序是根据编译顺序决定的。

**2.类和-category中调用和+load方法调用顺序是怎样(只有分类重写load方法)? **
答:

深入理解Category

Category AssociatedObject

1.在category里面如何添加实例变量的? 
答:在category里面是无法为category添加实例变量的。但是我们很多时候需要在category中添加和对象关联的值,这个时候可以求助关联对象来实现。

2.但是关联对象又是存在什么地方呢? 如何存储? 对象销毁时候如何处理关联对象呢?
关联对象又是存在什么地方呢:AssociationsManager里面是由一个静态AssociationsHashMap来存储所有的关联对象的。

如何存储:所有对象的关联对象都存在一个全局map里面。而map的的key是这个对象的指针地址(任意两个不同对象的指针地址一定是不同的),而这个map的value又是另外一个AssociationsHashMap,里面保存了关联对象的kv对。

对象销毁时候如何处理关联对象:runtime的销毁对象函数objc_destructInstance里面会判断这个对象有没有关联对象,如果有,会调用_object_remove_assocations做关联对象的清理工作。

systemLayoutSizeFittingSize And sizeThatFits

(1)在计算高度前向 contentView 加了一条和 webView 宽度相同的宽度约束,强行让 contentView 内部的控件知道了自己父 view 的宽度,再反算自己被外界约束的宽度(给contentView添加约束)
(2)调用systemLayoutSizeFittingSize api为contentView自适应高度
(3)移出contentView约束框架布局

为什么删除一个 indexPath 为 [0:5] 的最后一个 cell或item 时,[0:0] ~ [0:4] 的高度缓存不受影响,而 [0:5] 后面所有的缓存值都向前移动一个位置。

答:

几个问题

** 1.UITableView与UICollectionView从缓存池中取出Cell的区别? **

1.1- (nullable __kindof UITableViewCell *)dequeueReusableCellWithIdentifier:(NSString *)identifier; 

1.2- (__kindof UITableViewCell *)dequeueReusableCellWithIdentifier:(NSString *)identifier forIndexPath:(NSIndexPath *)indexPath NS_AVAILABLE_IOS(6_0);


2.1- (__kindof UICollectionViewCell *)dequeueReusableCellWithReuseIdentifier:(NSString *)identifier forIndexPath:(NSIndexPath *)indexPath;

** 问题发现和总结:**

1.3- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath;
                                            VS
2.2- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath;
1.4- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath;

2.3- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath;

** 解决办法:**

templateCell = [self dequeueReusableCellWithIdentifier:identifier];

而UICollectionView想要从重用池用获取UICollectionViewCell只能通过

- (__kindof UICollectionViewCell *)dequeueReusableCellWithReuseIdentifier:(NSString *)identifier forIndexPath:(NSIndexPath *)indexPath;

方法,而且UICollectionView不能在sizeForItemAtIndexPath方法调用dequeueReusableCellWithReuseIdentifier:forIndexPath否则会出现数组越界崩溃现象,所以不能直接在sizeForItemAtIndexPath调用dequeueReusableCellWithReuseIdentifier:forIndexPath方法,来获取高度并缓存。

针对上面提到的问题发现和总结:,需要解决通过重用标识创建一个UICollectionViewCell,并设置相应的属性附件,解决思路:

- (instancetype)initWithFrame:(CGRect)frame {
    if (self = [super initWithFrame: frame]) {
        NSString *className = NSStringFromClass([self class]);
        
        return [[[NSBundle mainBundle] loadNibNamed:className owner:nil options:nil] firstObject];;
    }
    return self;
}

** 3.使用代理方法实现布局和自定义UICollectionViewLayout方法执行顺序? **

第一点清楚方法执行顺序:

图1-1

使用过程:

** 1.使用XIB需要将该Cell的ReuseIdentifier注册到UICollectionView,可以使用registerClass或registerNib **

 [self.collectionView registerClass:[WallterCollectionViewCell class] forCellWithReuseIdentifier:@"WallterCollectionViewCell"];

** 2.使用XIB用到UICollectionViewLayout自定义布局时,需要设置XIB如图1.2所示 **

图1-2

** 3.高度宽度自适应实现LTCollectionViewDynamicHeightCellLayout代理方法**

@protocol LTCollectionViewDynamicHeightCellLayoutDelegate <NSObject>

@required
- (NSInteger)numberOfColumnWithCollectionView:(UICollectionView *)collectionView
                         collectionViewLayout:( LTCollectionViewDynamicHeightCellLayout *)collectionViewLayout;
@required
- (CGFloat)marginOfCellWithCollectionView:(UICollectionView *)collectionView
                     collectionViewLayout:( LTCollectionViewDynamicHeightCellLayout *)collectionViewLayout;
@required
- (NSMutableArray <NSMutableArray *> *)indexHeightOfCellWithCollectionView:(UICollectionView *)collectionView collectionViewLayout:( LTCollectionViewDynamicHeightCellLayout *)collectionViewLayout;

GIF演示内容

两栏布局,实现LTCollectionViewDynamicHeightCellLayout代理方法

单栏布局图文布局,可选实现UICollectionView代理方法或LTCollectionViewDynamicHeightCellLayout代理方法

单栏布局图片布局,可选实现UICollectionView或LTCollectionViewDynamicHeightCellLayout代理方法

两栏图文布局,实现LTCollectionViewDynamicHeightCellLayout代理方法

实现LTCollectionViewDynamicHeightCellLayout代理方法,多栏布局只需要修改一行代码实现

演示图

使用介绍

1.单栏布局自适应高度(不使用UICollectionViewLayout)

主要代码:

#pragma mark UICollectionViewDataSource

- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView
{
    return self.feedEntitySections.count;
}

- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
{
    return [self.feedEntitySections[section] count];
}

- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
    LTFeedCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:reuseIdentitier forIndexPath:indexPath];
    [self configureCell:cell atIndexPath:indexPath];
    return cell;
}

- (void)configureCell:(LTFeedCell *)cell atIndexPath:(NSIndexPath *)indexPath {
    cell.lt_enforceFrameLayout = NO;
    
    cell.entity = self.feedEntitySections[indexPath.section][indexPath.row];
}

#define SCREEN_WIDTH [[UIScreen mainScreen] bounds].size.width

- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath
{
    CGFloat height = [collectionView lt_heightForCellWithIdentifier:reuseIdentitier cacheByIndexPath:indexPath configuration:^(LTFeedCell *cell) {
        
        [self configureCell:cell atIndexPath:indexPath];
    }];
    return CGSizeMake(SCREEN_WIDTH, height);
    
}

**2.多栏布局自适应高度(使用UICollectionViewLayout需要实现LTCollectionViewDynamicHeightCellLayout代理方法) **

主要代码

#pragma mark - LTCollectionViewDynamicHeightCellLayoutDelegate
- (NSInteger) numberOfColumnWithCollectionView:(UICollectionView *)collectionView
                          collectionViewLayout:( LTCollectionViewDynamicHeightCellLayout *)collectionViewLayout{
    return _cellColumn;
}

- (CGFloat) marginOfCellWithCollectionView:(UICollectionView *)collectionView
                      collectionViewLayout:(LTCollectionViewDynamicHeightCellLayout *)collectionViewLayout{
    return _cellMargin;
}


- (NSMutableArray<NSMutableArray *> *)indexHeightOfCellWithCollectionView:(UICollectionView *)collectionView collectionViewLayout:(LTCollectionViewDynamicHeightCellLayout *)collectionViewLayout {
    return _indexCountBySectionForHeight;
}


#pragma mark UICollectionViewDataSource
- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView
{
    
    return self.FeedEntitySections.count;
}

- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
{
    NSMutableArray<NSNumber *> *indexHeightArray = @[].mutableCopy;
    for (NSInteger i = 0; i < [self.FeedEntitySections[section] count]; i++) {
        NSIndexPath *indexPath = [NSIndexPath indexPathForRow:i inSection:section];
        CGFloat height= [collectionView lt_heightForCellWithIdentifier:reuseIdentitier cacheByIndexPath:indexPath configuration:^(LTFeedCell *cell) {
            [self configureCell:cell atIndexPath:indexPath];
        }];
        
        [indexHeightArray addObject:@(height)];
    }
    _indexCountBySectionForHeight[section] = indexHeightArray;
    return [self.FeedEntitySections[section] count];
}

最后

** 如果能帮助到您,希望能给一个小小的️Star或者点亮博文左下角的星,朋友的鼓励和支持是我继续分享的动力 **

源码下载地址

感谢:

sunnyxx
青玉伏案

上一篇下一篇

猜你喜欢

热点阅读