UICollectionView的重大bug?自定義UICollectionView樣式的你必須得知道這個(gè)

在開發(fā)中我們難免會(huì)用到UICollectionView子刮,一般常規(guī)用法是沒有任何問題的,但是诱建,比如在用UICollectionView實(shí)現(xiàn)瀑布流效果時(shí)钮蛛,自定義每個(gè)cell的frame屬性的時(shí)候就會(huì)出現(xiàn)在滑動(dòng)過程中有些cell一會(huì)顯示一會(huì)消失的奇葩問題(特別是cell較多的時(shí)候,總會(huì)滑動(dòng)到某個(gè)地方的時(shí)候出現(xiàn)cell突然消失的效果)受神。更奇葩的是抛猖,有的情況是在6s上顯示正常,在5s上會(huì)出現(xiàn)一會(huì)消失一會(huì)顯示鼻听。

比如在我的demo中是這樣子的:

一會(huì)顯示一會(huì)消失的效果圖.gif

什么Bug?

在網(wǎng)上搜索關(guān)鍵字cell disappearing in UICollection viewUICollectionView some cell not appearUICollectionView滾動(dòng)的時(shí)候cell消失财著,你會(huì)發(fā)現(xiàn)網(wǎng)上有很多人遇到過這種問題,下面附上各大論壇上的圖和鏈接:

蘋果官方開發(fā)者論壇的Problem of cell disappearing in UICollection view in ios 10 only

蘋果論壇.png

來自stackoverflow的UICollectionView's cell disappearing

stackoverflow.png

來自segmentfault的UICollectionView滾動(dòng)的時(shí)候會(huì)出現(xiàn)cell消失的情況

segmentfault.png

有人說通過將UICollectionView的bounces屬性設(shè)置為NO撑碴,有人說這是UICollectionView的bug(提到蘋果官方論壇也沒人回復(fù))撑教,有人推薦使用PSTCollectionView這個(gè)輪子(用UIScrollView的子類實(shí)現(xiàn)類似UICollectionView的效果)。

下面先來看看造成cell一會(huì)顯示一會(huì)消失的效果的主要代碼:

- (void)prepareLayout {
    [super prepareLayout];
}

#pragma mark - CollectionView的滾動(dòng)范圍
- (CGSize)collectionViewContentSize
{
    CGFloat width = self.collectionView.frame.size.width;
    CGFloat maxY = [self maxOrignYInSection:_framesArray.count - 1];
    return CGSizeMake(width, maxY + _rowHeight + self.sectionInset.bottom);
}

#pragma mark - 所有cell和view的布局屬性
//sectionheader sectionfooter decorationview collectionviewcell的屬性都會(huì)走這個(gè)方法
- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect
{
    NSArray *tmpArray = [super layoutAttributesForElementsInRect:rect];
    NSMutableArray *array = [NSMutableArray arrayWithCapacity:tmpArray.count];
    for(NSInteger i = 0; i < tmpArray.count; i++){
        UICollectionViewLayoutAttributes *attrs = [tmpArray objectAtIndex:i];
        UICollectionElementCategory category = attrs.representedElementCategory;
        if(category == UICollectionElementCategoryCell){
            [array addObject:[self layoutAttributesForItemAtIndexPath:attrs.indexPath]];
        }else if (category == UICollectionElementCategorySupplementaryView){
            UICollectionViewLayoutAttributes *theAttrs = [self layoutAttributesForSupplementaryViewOfKind:attrs.representedElementKind
                                                                                              atIndexPath:attrs.indexPath];
            [array addObject:theAttrs];
        }
    }
    return array;
}

詳細(xì)復(fù)現(xiàn)代碼在ReappearBugCode

分析代碼醉拓,尋找Bug

首先伟姐,我們這里是用UICollectionView實(shí)現(xiàn)一個(gè)高度固定,寬度不固定的瀑布流效果廉嚼,每個(gè)cell的寬度根據(jù)文字內(nèi)容計(jì)算的玫镐,每一行顯示不全的時(shí)候自動(dòng)換行,在cell展示的時(shí)候通過獲取cell對(duì)應(yīng)的布局屬性來把這個(gè)cell展示在指定的位置上怠噪。

其次恐似,在cell全部顯示的情況下觀察,cell的frame全部是正確的傍念,這就說明我們代碼計(jì)算每一個(gè)cell的布局屬性是沒有問題的矫夷。并且UICollectionView的可滑動(dòng)范圍contentSize的計(jì)算也是沒有問題的。

最后憋槐,這些一會(huì)顯示一會(huì)消失的cell是在UICollectionView滑動(dòng)到某個(gè)區(qū)域時(shí)出現(xiàn)的双藕,這就說明在這個(gè)區(qū)域內(nèi)的cell布局獲取的有問題(計(jì)算沒問題)。

我們知道自定義的UICollectionViewLayout時(shí)必須實(shí)現(xiàn)并且會(huì)按順序執(zhí)行的方法如下:

- (void)prepareLayout;//step 1
- (CGSize)collectionViewContentSize;//step 2
- (NSArray<UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect;//step 3

由上面的分析可見阳仔,問題應(yīng)該出在layoutAttributesForElementsInRect:方法中忧陪,我們?cè)诳煲瑒?dòng)到出現(xiàn)異常的區(qū)域時(shí)在這個(gè)方法處加個(gè)斷點(diǎn)。當(dāng)滑動(dòng)到出現(xiàn)異常的區(qū)域時(shí),看到tmpArray為空了嘶摊,說明問題確實(shí)出在了這里延蟹。

cell顯示不正常的區(qū)域.png

因?yàn)槲覀円呀?jīng)對(duì)每個(gè)Cell都自定義了布局,調(diào)用[super layoutAttributesForElementsInRect:rect]返回的布局屬性的集合并不是我們想要的叶堆。所以在這里阱飘,我們需要在這里獲取UICollectionView當(dāng)前可見的返回,然后自己返回當(dāng)前處在該區(qū)域內(nèi)的cell的布局屬性集合虱颗。

修改代碼沥匈,解決Bug

解決思路和步驟:

  • prepareLayout方法中計(jì)算所有cell的frame并緩存起來,可提高UICollectionView滑動(dòng)的流暢性
  • collectionViewContentSize方法中根據(jù)上面計(jì)算出來的frame返回UICollectionView可滑動(dòng)的范圍
  • layoutAttributesForElementsInRect:方法中先拿到UICollectionView當(dāng)前可見范圍忘渔,然后遍歷上面計(jì)算的frame高帖,判斷哪些cell或header應(yīng)該展示在該區(qū)域內(nèi),把這些cell和header的布局屬性放到一個(gè)數(shù)組中返回辨萍。

修改后的主要代碼:

#pragma mark - 重寫父類的方法棋恼,實(shí)現(xiàn)瀑布流布局
//step1
- (void)prepareLayout {
    [super prepareLayout];
    [self calculateFrames];
}

#pragma mark - CollectionView的滾動(dòng)范圍
//step2
- (CGSize)collectionViewContentSize
{
    CGFloat width = self.collectionView.frame.size.width;
    return CGSizeMake(width, _contentHeight);
}

#pragma mark - 所有cell和view的布局屬性
//sectionheader sectionfooter decorationview collectionviewcell的屬性都會(huì)走這個(gè)方法
//step3
- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect
{
    NSMutableArray *attributesArray = [NSMutableArray array];
    CGPoint offset = self.collectionView.contentOffset;
    CGRect visibleRect = CGRectMake(0, offset.y, CGRectGetWidth(self.collectionView.frame), CGRectGetHeight(self.collectionView.frame));
    for(NSInteger section = 0; section < _framesArray.count; section++){
        NSArray *currentSectionFrames = _framesArray[section];
        for(NSInteger row = 0; row < currentSectionFrames.count; row++){
            CGRect currentFrame = [currentSectionFrames[row] CGRectValue];
            NSIndexPath *currentIndexPath = [NSIndexPath indexPathForRow:row inSection:section];
            if(currentFrame.origin.y + currentFrame.size.height >= visibleRect.origin.y &&
               currentFrame.origin.y <= visibleRect.origin.y + visibleRect.size.height){
                //first section header should show
                if(row == 0 && section == 0){
                    UICollectionViewLayoutAttributes *headerAttr = [[self layoutAttributesForSupplementaryViewOfKind:@"UICollectionElementKindSectionHeader"
                                                                                                         atIndexPath:currentIndexPath] copy];
                    CGRect frame = headerAttr.frame;
                    frame.origin.y = 0;
                    headerAttr.frame = frame;
                    [attributesArray addObject:headerAttr];
                }
                
                //cell should show
                UICollectionViewLayoutAttributes *cellAttrs = [[self layoutAttributesForItemAtIndexPath:currentIndexPath] copy];
                cellAttrs.frame = currentFrame;
                [attributesArray addObject:cellAttrs];
                
                //next section header should show
                if(row == currentSectionFrames.count - 1 && section + 1 < _framesArray.count &&
                   currentFrame.origin.y + currentFrame.size.height + self.sectionInset.bottom < visibleRect.origin.y + visibleRect.size.height){
                    UICollectionViewLayoutAttributes *headerAttr = [[self layoutAttributesForSupplementaryViewOfKind:@"UICollectionElementKindSectionHeader"
                                                                                                         atIndexPath:[NSIndexPath indexPathForRow:0 inSection:section + 1]] copy];
                    CGFloat y = [self contentHeightInSection:section];
                    CGRect frame = headerAttr.frame;
                    frame.origin.y = y;
                    headerAttr.frame = frame;
                    [attributesArray addObject:headerAttr];
                }
            }
        }
    }
    return attributesArray;
}

修改后的效果:

修改后的效果圖.gif

詳細(xì)代碼見:YLTagsChooser 如果大家有更好的解決辦法,歡迎反饋锈玉。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市义起,隨后出現(xiàn)的幾起案子拉背,更是在濱河造成了極大的恐慌,老刑警劉巖默终,帶你破解...
    沈念sama閱讀 206,482評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件椅棺,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡齐蔽,警方通過查閱死者的電腦和手機(jī)两疚,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,377評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來含滴,“玉大人诱渤,你說我怎么就攤上這事√缚觯” “怎么了勺美?”我有些...
    開封第一講書人閱讀 152,762評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)碑韵。 經(jīng)常有香客問我赡茸,道長(zhǎng),這世上最難降的妖魔是什么祝闻? 我笑而不...
    開封第一講書人閱讀 55,273評(píng)論 1 279
  • 正文 為了忘掉前任占卧,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘华蜒。我一直安慰自己舷蒲,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,289評(píng)論 5 373
  • 文/花漫 我一把揭開白布友多。 她就那樣靜靜地躺著牲平,像睡著了一般。 火紅的嫁衣襯著肌膚如雪域滥。 梳的紋絲不亂的頭發(fā)上纵柿,一...
    開封第一講書人閱讀 49,046評(píng)論 1 285
  • 那天,我揣著相機(jī)與錄音启绰,去河邊找鬼昂儒。 笑死,一個(gè)胖子當(dāng)著我的面吹牛委可,可吹牛的內(nèi)容都是我干的渊跋。 我是一名探鬼主播,決...
    沈念sama閱讀 38,351評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼着倾,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼拾酝!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起卡者,我...
    開封第一講書人閱讀 36,988評(píng)論 0 259
  • 序言:老撾萬榮一對(duì)情侶失蹤蒿囤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后崇决,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體材诽,經(jīng)...
    沈念sama閱讀 43,476評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,948評(píng)論 2 324
  • 正文 我和宋清朗相戀三年恒傻,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了脸侥。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,064評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡盈厘,死狀恐怖睁枕,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情扑庞,我是刑警寧澤譬重,帶...
    沈念sama閱讀 33,712評(píng)論 4 323
  • 正文 年R本政府宣布,位于F島的核電站罐氨,受9級(jí)特大地震影響臀规,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜栅隐,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,261評(píng)論 3 307
  • 文/蒙蒙 一塔嬉、第九天 我趴在偏房一處隱蔽的房頂上張望玩徊。 院中可真熱鬧,春花似錦谨究、人聲如沸恩袱。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,264評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽畔塔。三九已至,卻和暖如春鸯屿,著一層夾襖步出監(jiān)牢的瞬間澈吨,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,486評(píng)論 1 262
  • 我被黑心中介騙來泰國(guó)打工寄摆, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留谅辣,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,511評(píng)論 2 354
  • 正文 我出身青樓婶恼,卻偏偏與公主長(zhǎng)得像桑阶,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子勾邦,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,802評(píng)論 2 345