今天下班的時候,和一個同事聊天的時候,發現在我們的項目中目前有一個小 Bug,就是并排 View的時候,在 iPhone 6 或 iPhone 6s 上會有一條縫出現,類似下圖。
原本場景:
左邊白色是一個 leftView,固定寬度 100;
右邊是一個大的 rightView,寬度不固定,跟隨屏幕變化;
右邊 rightView 上并排鋪滿三個等寬的 三個 subView;
我的第一反應就是,直接自動布局啊,右邊 rightView 以中間 的 subView 為軸,然后緊接著就沒問題啦。就算并排多個 subView直接用Masonry + NSArray (MASHelper)也可以啦。然而別告知rightView是 UICollectionView。而且此種縫的情況也只會在 iPhone 6 或 iPhone 6s 上出現。
核心代碼:
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath {
return CGSizeMake((SCREEN_WIDTH - 100)/3.0, 100);
}
- (UICollectionViewFlowLayout *)flowLayout {
if (!_flowLayout) {
_flowLayout = [[UICollectionViewFlowLayout alloc] init];
_flowLayout.minimumLineSpacing = 0;
_flowLayout.minimumInteritemSpacing = 0;
}
return _flowLayout;
}
想著,出現這種情況,肯定是和 iPhone 6 或 iPhone 6s 上的屏幕寬 375有關,375 - 100 = 275,275 除以 3 等于 91.666666
此時也許我們可以先來一個
// 魔鬼數字先不管啊
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath {
CGFloat itemWidth = (SCREEN_WIDTH - 100)/3.0;
// itemWidth == 91.666666
if (SCREEN_WIDTH == 375) {
NSInteger row = indexPath.row % 3;
switch (row) {
case 0:
itemWidth = (NSInteger)itemWidth + 0.0;
break;
case 1:
itemWidth = (NSInteger)itemWidth + 1.0;
break;
case 2:
itemWidth = (NSInteger)itemWidth + 1.0;
break;
default:
break;
}
}
return CGSizeMake(itemWidth, 100);
}
這樣看起來解決了,做一個 91,92,92的寬,但其實處理起來也顯得比較麻煩,而且怎么感覺都怪怪的,并且假如蘋果以后又出了一個這樣類似要調整的機型,不可能再寫吧,所以此處還需要調整。
后來再經過測試發現了一個奇怪的情況,在 iPhone 6p 上 那個寬度是等于itemWidth == 104.666667
,iPhone 6 上是 itemWidth == 91.666667
,按理說都不是整數,平分也會有縫隙啊,為什么 iPhone 6p 上沒有呢???
再經過測試,例如直接將 iPhone 6上的屏幕4等分,9等分都會出現很明顯空隙分割。特別是4等分的時候,width == 93.750000
,而且是兩條縫隙,十分奇怪。
直接查看 View 層次發現,這個四個 UICollectionCell 的寬都是一樣的。** width = 93.75 **。
于是為了驗證,我在 iPhone 6上,用最直接的方法這樣寫:
// 此處沒有用 for 循環 創建 和布局
UIView *oneView = [[UIView alloc] init];
oneView.backgroundColor = [UIColor redColor];
[self.view addSubview:oneView];
UIView *twoView = [[UIView alloc] init];
twoView.backgroundColor = [UIColor yellowColor];
[self.view addSubview:twoView];
UIView *threeView = [[UIView alloc] init];
threeView.backgroundColor = [UIColor blueColor];
[self.view addSubview:threeView];
UIView *fourView = [[UIView alloc] init];
fourView.backgroundColor = [UIColor lightGrayColor];
[self.view addSubview:fourView];
[oneView mas_makeConstraints:^(MASConstraintMaker *make) {
make.leading.mas_equalTo(@0);
make.centerY.equalTo(self.view.mas_centerY);
make.height.mas_equalTo(@100);
}];
[twoView mas_makeConstraints:^(MASConstraintMaker *make) {
make.height.centerY.width.equalTo(oneView);
make.leading.equalTo(oneView.mas_trailing);
}];
[threeView mas_makeConstraints:^(MASConstraintMaker *make) {
make.height.centerY.width.equalTo(twoView);
make.leading.equalTo(twoView.mas_trailing);
}];
[fourView mas_makeConstraints:^(MASConstraintMaker *make) {
make.height.centerY.width.equalTo(threeView);
make.trailing.mas_equalTo(@0);
make.leading.equalTo(threeView.mas_trailing);
}];
發現這樣,它又不會出現空隙,四個 View 緊緊靠在一起。但是我卻是發現它的寬度變成 94,93.5,94,93.5。
所以說,通過兩個寬度對比(93.75和94,93.5),在 iPhone6 上UICollectionViewFlowLayout和普通布局做的處理是有區別的。同時驚訝的事情發生了,iPhone 6plus 也出現這種情況了。
可以歸納為iPhone6 和iPhone 6plus UICollectionView四等分時總是不能充滿屏幕, 有兩根線漏出背景色,因此這個問題應該是和UICollectionViewFlowLayout 相關的。
所以我嘗試重寫UICollectionViewFlowLayout
啦。
@interface PQCollectionViewLayout : UICollectionViewLayout
@end
#import "PQCollectionViewLayout.h"
@interface PQCollectionViewLayout ()
@property(nonatomic,strong) NSMutableArray<UICollectionViewLayoutAttributes *> *attributeArr;
@end
@implementation PQCollectionViewLayout
- (NSMutableArray *)attributeArr{
if (_attributeArr == nil) {
_attributeArr = [[NSMutableArray alloc] init];
}
return _attributeArr;
}
- (void)prepareLayout{
[super prepareLayout];
[self.attributeArr removeAllObjects];
// 獲取cell的size
for (NSInteger i=0, count = [self.collectionView numberOfItemsInSection:0]; i < count; i++) {
NSIndexPath *indexPath = [NSIndexPath indexPathForItem:i inSection:0];
UICollectionViewLayoutAttributes *attributes = [self layoutAttributesForItemAtIndexPath:indexPath];
[self.attributeArr addObject:attributes];
}
}
- (CGSize)collectionViewContentSize{
UICollectionViewLayoutAttributes *attribute = self.attributeArr.lastObject;
return CGSizeMake(0, CGRectGetMaxY(attribute.frame));
}
- (NSArray<UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect {
return self.attributeArr;
}
/**
* 返回每個cell的布局屬性
*/
- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath{
// CGSize : width = (SCREEN_WIDTH - 100)/3.0; height = 100;
UICollectionViewLayoutAttributes *attributes = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];
CGFloat itemWidth = ([UIScreen mainScreen].bounds.size.width - 100)/3.0;
attributes.frame = CGRectMake(indexPath.row % 3 * itemWidth, indexPath.row / 3 * 100, itemWidth, 100);
return attributes;
}
@end
- (PQCollectionViewLayout *)flowLayout {
if (!_flowLayout) {
_flowLayout = [[PQCollectionViewLayout alloc] init];
}
return _flowLayout;
}
ps:上面重寫只針對這邊特殊需求的三等分,其他情況需要根據attributes.frame 自由設置, 當然有需要寫成一個自己UICollectionViewFlowLayout,有設置的屬性和代理,那就更完美啦
發現重寫后達成了我們的目的,雖說這樣貌似解決了那個漏出背景色的那個問題,但有點撓,暫時還不知道有沒有什么更好的方法,有的話,歡迎告知。