iOS UICollectionView 的坑:不能重復(fù)調(diào)用 dequeueReusableSupplementaryViewOfKind

問(wèn)題描述

今天有個(gè)人把他寫的 demo 發(fā)給我調(diào) bug。他遇到的問(wèn)題是,在 UICollectionView 的 headerView 上加了一個(gè)按鈕,但有的 section 的 header 上的按鈕點(diǎn)了有反應(yīng),有的點(diǎn)了沒(méi)反應(yīng)。

我打開(kāi) Xcode 的類 reveal 工具一看,點(diǎn)了沒(méi)反應(yīng)的那幾個(gè) section header 上面都蓋著另外一個(gè)UICollectionReusableView。按鈕被蓋住了,難怪點(diǎn)了沒(méi)反應(yīng)呢。

section header 被另一個(gè) section header 蓋住

但為什么會(huì)出現(xiàn)這種情況呢?我們來(lái)看他 header 的注冊(cè)和回調(diào)方法是怎么寫的:

header 的注冊(cè)
[_collectionView registerClass:[Type1HeaderView class] forSupplementaryViewOfKind:UICollectionElementKindSectionHeader withReuseIdentifier:Type1HeaderID];
[_collectionView registerClass:[Type2HeaderView class] forSupplementaryViewOfKind:UICollectionElementKindSectionHeader withReuseIdentifier:Type2HeaderID];

分別用兩個(gè) reuseId 注冊(cè)了兩種 header(為了簡(jiǎn)化,我把命名改了改)。兩種 header 所屬的類都是UICollectionReusableView的子類,沒(méi)什么問(wèn)題。

header 的回調(diào)方法
- (UICollectionReusableView *)collectionView:(UICollectionView *)collectionView viewForSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath {
    if ([kind isEqualToString:UICollectionElementKindSectionHeader]) {
        Type1HeaderView *type1HeaderView = [collectionView dequeueReusableSupplementaryViewOfKind:kind withReuseIdentifier:Type1HeaderID forIndexPath:indexPath];
        Type2HeaderView *type2HeaderView = [collectionView dequeueReusableSupplementaryViewOfKind:kind withReuseIdentifier:Type2HeaderID forIndexPath:indexPath];
        if (indexPath.section == 0) {
            return type1HeaderView;
        } else {
            return type2HeaderView;
        }
    }
    return nil;
}

簡(jiǎn)化之后的代碼如上,省去了一些無(wú)關(guān)細(xì)節(jié)。代碼寫得有點(diǎn)隨意,但看著還挺正常的。所以我找了半天才找到問(wèn)題所在。

解決辦法

問(wèn)題就在于那兩句

Type1HeaderView* type1HeaderView = [collectionView dequeueReusableSupplementaryViewOfKind:kind withReuseIdentifier:Type1HeaderID forIndexPath:indexPath];
Type2HeaderView* type2HeaderView = [collectionView dequeueReusableSupplementaryViewOfKind:kind withReuseIdentifier:Type2HeaderID forIndexPath:indexPath];

連續(xù)調(diào)用了兩次 dequeueReusableSupplementaryViewOfKind: withReuseIdentifier: forIndexPath:方法,傳的是相同的indexPath。這就導(dǎo)致同一位置上出現(xiàn)了兩個(gè)重疊的 headerView ,一個(gè)蓋住另外一個(gè)。改成這樣:

改正后的回調(diào)方法
- (UICollectionReusableView *)collectionView:(UICollectionView *)collectionView viewForSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath {
    if ([kind isEqualToString:UICollectionElementKindSectionHeader]) {
        if (indexPath.section == 0) {
            Type1HeaderView *type1HeaderView = [collectionView dequeueReusableSupplementaryViewOfKind:kind withReuseIdentifier:Type1HeaderID forIndexPath:indexPath];
            return type1HeaderView;
        } else {
            Type2HeaderView *type2HeaderView = [collectionView dequeueReusableSupplementaryViewOfKind:kind withReuseIdentifier:Type2HeaderID forIndexPath:indexPath];
            return type2HeaderView;
        }
    }
    return nil;
}

就一點(diǎn)問(wèn)題都沒(méi)有了。

大概是每調(diào)一次那個(gè)dequeue方法,系統(tǒng)都會(huì)在那個(gè)indexPath的位置上創(chuàng)建一個(gè) header,不管最后 return 什么。我覺(jué)得這個(gè)問(wèn)題還挺奇怪的,因?yàn)椴榱艘幌绿O果官方文檔里并沒(méi)有提到這一點(diǎn),可以算是官方的實(shí)現(xiàn)產(chǎn)生的一個(gè) bug,也是 UICollectionView 的一個(gè)坑了。

結(jié)論

- (UICollectionReusableView *)collectionView: viewForSupplementaryElementOfKind: atIndexPath:這個(gè)回調(diào)方法的每次執(zhí)行中,要么返回 nil,要么調(diào)用一次且僅一次dequeueReusableSupplementaryViewOfKind: withReuseIdentifier: forIndexPath:,千萬(wàn)不能重復(fù)調(diào)用,否則會(huì)導(dǎo)致詭異的 bug,還不容易找到原因。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容