問題描述
今天有個人把他寫的 demo 發(fā)給我調(diào) bug贤牛。他遇到的問題是钦幔,在 UICollectionView 的 headerView 上加了一個按鈕偎捎,但有的 section 的 header 上的按鈕點(diǎn)了有反應(yīng)啤咽,有的點(diǎn)了沒反應(yīng)婴程。
我打開 Xcode 的類 reveal 工具一看廓奕,點(diǎn)了沒反應(yīng)的那幾個 section header 上面都蓋著另外一個UICollectionReusableView
。按鈕被蓋住了档叔,難怪點(diǎn)了沒反應(yīng)呢桌粉。
但為什么會出現(xiàn)這種情況呢?我們來看他 header 的注冊和回調(diào)方法是怎么寫的:
header 的注冊
[_collectionView registerClass:[Type1HeaderView class] forSupplementaryViewOfKind:UICollectionElementKindSectionHeader withReuseIdentifier:Type1HeaderID];
[_collectionView registerClass:[Type2HeaderView class] forSupplementaryViewOfKind:UICollectionElementKindSectionHeader withReuseIdentifier:Type2HeaderID];
分別用兩個 reuseId 注冊了兩種 header(為了簡化衙四,我把命名改了改)铃肯。兩種 header 所屬的類都是UICollectionReusableView
的子類,沒什么問題传蹈。
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;
}
簡化之后的代碼如上押逼,省去了一些無關(guān)細(xì)節(jié)。代碼寫得有點(diǎ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)了兩個重疊的 headerView ,一個蓋住另外一個瞬逊。改成這樣:
改正后的回調(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)問題都沒有了显歧。
大概是每調(diào)一次那個dequeue
方法,系統(tǒng)都會在那個indexPath
的位置上創(chuàng)建一個 header确镊,不管最后 return 什么士骤。我覺得這個問題還挺奇怪的,因為查了一下蘋果官方文檔里并沒有提到這一點(diǎn)蕾域,可以算是官方的實現(xiàn)產(chǎn)生的一個 bug拷肌,也是 UICollectionView 的一個坑了。
結(jié)論
在- (UICollectionReusableView *)collectionView: viewForSupplementaryElementOfKind: atIndexPath:
這個回調(diào)方法的每次執(zhí)行中旨巷,要么返回 nil巨缘,要么調(diào)用一次且僅一次dequeueReusableSupplementaryViewOfKind: withReuseIdentifier: forIndexPath:
,千萬不能重復(fù)調(diào)用采呐,否則會導(dǎo)致詭異的 bug若锁,還不容易找到原因。