UICollectionView間隙的坑

Hsusue發表於2019-01-14

前言

筆者之前用UICollectionView實現了一個課程表。

分為兩個部分,左邊節數1~6為section 0,右邊課程為section1,採用垂直佈局。

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時,畫素是不可再分的,就造成了上面的現象。所以用巨集處理,如果要求精確,還是不能使。(上面的課程表就因為上面部分留的位置太少,要求精確)

換言之,即使view的Size能設為小數,也可能會丟失部分小數。

由於px不可再分,,在1px=0.5pt的螢幕中,按0.5pt劃分。0.3pt也要佔1px,0.8pt也要佔2px。所以按那個巨集來處理還是不行。

解決

  • 方案一 設定最大間隙

自定義UICollectionViewFlowLayout,重寫layoutAttributesForElementsInRect方法。修改返回cell的frame。

// 實現這個方法,返回每個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就應該往上偏移。

UICollectionView間隙的坑

筆者針對這份程式碼做了優化,但筆者演算法差勁,依然存在不少不足。如果一個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;

}複製程式碼

程式碼效果如下。

UICollectionView間隙的坑
  • 還找到一種方案二

該方案用到了螢幕畫素,可以達到無縫或者1px細縫的精緻效果。改動下應該也能實現間隙的控制。

UICollectionView 縫隙修復

來源:https://juejin.im/post/5c3b60f2518825258604dafd

相關文章