UICollectionView间隙的坑

前言

笔者以前用UICollectionView实现了一个课程表。算法

分为两个部分,左边节数1~6为section 0,右边课程为section1,采用垂直布局。bash

image.png

但换了个屏幕就凉凉了,section0排版出了问题。 布局

image.png

想着用#define kScaleWidth(width) (width) * (SCREEN_WIDTH / 375.f)这个宏处理,仍是很差使。测试

px:像素 pt:独立像素 / point / 点 iOS 开发中用到的单位 pt 是独立像素的意思,它是绝对长度,不随屏幕像素密度变化而变化。 ——iOS 中 pt 与px的区分优化

image.png

罪魁祸首就是像素,手机屏幕最小的单位是像素也就是1px,当小于1px时,像素是不可再分的,就形成了上面的现象。因此用宏处理,若是要求精确,仍是不能使。(上面的课程表就由于上面部分留的位置太少,要求精确)ui

换言之,即便view的Size能设为小数,也可能会丢失部分小数。spa

因为px不可再分,,在1px=0.5pt的屏幕中,按0.5pt划分。0.3pt也要占1px,0.8pt也要占2px。因此按那个宏来处理仍是不行。设计

解决

  • 方案一 设置最大间隙

自定义UICollectionViewFlowLayout,重写layoutAttributesForElementsInRect方法。修改返回cell的frame。code

// 实现这个方法,返回每一个cell的布局属性。
// Implement -layoutAttributesForElementsInRect: to return layout attributes for for supplementary or decoration views, or to perform layout in an as-needed-on-screen fashion.
- (nullable NSArray<__kindof UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect; // return an array layout attributes instances for all the views in the given rect
复制代码

iOS开发:UICollectionView最大间距网上看到这篇,没有给出垂直方向代码,并且有漏洞,测试以下。缘由是 section0最后一个cell恰好换到上一行时,section1就应该往上偏移。orm


笔者针对这份代码作了优化,但笔者算法差劲,依然存在很多不足。若是一个section中有多个cell时,系统计算的frame可能会换几回行。但只要UI不是设计得特别复杂,是不会发生这种状况的。

// CourseFlowLayout.h
@interface CourseFlowLayout : UICollectionViewFlowLayout

- (instancetype)initWithMaxSpacing:(NSInteger)maxSpacing;

@end
复制代码
// CourseFlowLayout.m
- (NSArray<UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect{
    NSMutableArray<UICollectionViewLayoutAttributes *> *attributes = [[super layoutAttributesForElementsInRect:rect] mutableCopy];
    
    if (self.scrollDirection == UICollectionViewScrollDirectionVertical) {// 水平布局
        float contentSizeWidth = self.collectionViewContentSize.width;
        float offset = 0;// 记录偏移量
        
        for(int i = 1; i < attributes.count; i++){
            UICollectionViewLayoutAttributes *currentLayoutAttributes = attributes[i];
            UICollectionViewLayoutAttributes *prevLayoutAttributes = attributes[i - 1];
            
            CGRect newFrame = currentLayoutAttributes.frame;
            {// 修改y
                newFrame.origin.y -= offset;
            }
            
            
            {// 修改y
                
                // 处理多section,若是是item == 0,x不作处理
                NSInteger curIndexItem = currentLayoutAttributes.indexPath.item;
                if (curIndexItem == 0) {
                    currentLayoutAttributes.frame = newFrame;
                    continue;
                }
                
                
                // 取上一个cell的底部
                NSInteger prevorigin = CGRectGetMaxY(prevLayoutAttributes.frame);
                
                // 还够位置就设置x
                if(prevorigin + _maxSpacing + WIDTH(currentLayoutAttributes) <= contentSizeWidth) {// 假设cell放进去是否会超过collectionView的右边
                    newFrame.origin.x = prevorigin + _maxSpacing;
                    
                    // 跟系统算出来的对比 是否换行了
                    // 若是section最后一item换行 意味着后面的y都要减回去
                    if (Y(currentLayoutAttributes) != Y(prevLayoutAttributes)) {
                        if (i < attributes.count - 1) {
                            if (attributes[i + 1].indexPath.section != currentLayoutAttributes.indexPath.section) {
                                offset += Y(currentLayoutAttributes) - Y(prevLayoutAttributes);
                            }
                        }
                        newFrame.origin.y = Y(prevLayoutAttributes);
                    }
                    
                } else {// 不够位置x设为0,摆在另外一行首部
                    newFrame.origin.x = 0;
                }
            }
            
            currentLayoutAttributes.frame = newFrame;
        }
    } else {// 垂直布局
        float contentSizeHeight = self.collectionViewContentSize.height;
        float offset = 0;// 记录偏移量
        
        for(int i = 1; i < attributes.count; i++){
            UICollectionViewLayoutAttributes *currentLayoutAttributes = attributes[i];
            UICollectionViewLayoutAttributes *prevLayoutAttributes = attributes[i - 1];
            
            CGRect newFrame = currentLayoutAttributes.frame;
            {// 修改x
                newFrame.origin.x -= offset;
            }
            
            
            {// 修改y
                
                // 处理多section,若是是item == 0,y不作处理
                NSInteger curIndexItem = currentLayoutAttributes.indexPath.item;
                if (curIndexItem == 0) {
                    currentLayoutAttributes.frame = newFrame;
                    continue;
                }
                
                
                // 取上一个cell的底部
                NSInteger prevorigin = CGRectGetMaxY(prevLayoutAttributes.frame);
                
                // 还够位置就设置y
                if(prevorigin + _maxSpacing + HEIGHT(currentLayoutAttributes) <= contentSizeHeight) {// 假设cell放进去是否会超过collectionView的底部
                    newFrame.origin.y = prevorigin + _maxSpacing;
                    
                    // 跟系统算出来的对比 是否换列了
                    // 若是section最后一item换列 意味着后面的x都要减回去
                    if (X(currentLayoutAttributes) != X(prevLayoutAttributes)) {
                        if (i < attributes.count - 1) {
                            if (attributes[i + 1].indexPath.section != currentLayoutAttributes.indexPath.section) {
                                offset += X(currentLayoutAttributes) - X(prevLayoutAttributes);
                            }
                        }
                        newFrame.origin.x = X(prevLayoutAttributes);
                    }
                    
                } else {// 不够位置y设为0,摆在另外一列首部
                    newFrame.origin.y = 0;
                }
            }
            
            currentLayoutAttributes.frame = newFrame;
        }
    }
    
    
    return attributes;
}
复制代码

代码效果以下。

  • 还找到一种方案二

该方案用到了屏幕像素,能够达到无缝或者1px细缝的精致效果。改动下应该也能实现间隙的控制。

UICollectionView 缝隙修复

相关文章
相关标签/搜索