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 縫隙修復

相關文章
相關標籤/搜索