前言
筆者之前用UICollectionView實現了一個課程表。
分為兩個部分,左邊節數1~6為section 0,右邊課程為section1,採用垂直佈局。
但換了個螢幕就涼涼了,section0排版出了問題。
想著用#define kScaleWidth(width) (width) * (SCREEN_WIDTH / 375.f)
這個巨集處理,還是不好使。
px:畫素pt:獨立畫素 / point / 點iOS 開發中用到的單位 pt 是獨立畫素的意思,它是絕對長度,不隨螢幕畫素密度變化而變化。——iOS 中 pt 與px的區分
罪魁禍首就是畫素,手機螢幕最小的單位是畫素也就是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就應該往上偏移。
筆者針對這份程式碼做了優化,但筆者演算法差勁,依然存在不少不足。如果一個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細縫的精緻效果。改動下應該也能實現間隙的控制。