Skip to content

Commit

Permalink
修复 bug
Browse files Browse the repository at this point in the history
修复”点击tableview的时候如果collectionview与之对应的那个 section 没有数据,会闪退“的 bug
  • Loading branch information
leejayID committed Jun 2, 2017
1 parent cb6d183 commit 912b8a0
Show file tree
Hide file tree
Showing 5 changed files with 90 additions and 126 deletions.
53 changes: 0 additions & 53 deletions .clang-format

This file was deleted.

Binary file not shown.
10 changes: 5 additions & 5 deletions Linkage/CollectionView/CollectionViewController.m
Original file line number Diff line number Diff line change
Expand Up @@ -153,11 +153,13 @@ - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath
{
_selectIndex = indexPath.row;

// http://stackoverflow.com/questions/22100227/scroll-uicollectionview-to-section-header-view
// 解决点击 TableView 后 CollectionView 的 Header 遮挡问题。
[self scrollToTopOfSection:_selectIndex animated:YES];

// [self.collectionView scrollToItemAtIndexPath:[NSIndexPath indexPathForItem:0 inSection:_selectIndex] atScrollPosition:UICollectionViewScrollPositionTop animated:YES];
[self.tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:_selectIndex inSection:0] atScrollPosition:UITableViewScrollPositionTop animated:YES];
[self.tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:_selectIndex inSection:0]
atScrollPosition:UITableViewScrollPositionTop animated:YES];
}

#pragma mark - 解决点击 TableView 后 CollectionView 的 Header 遮挡问题
Expand All @@ -172,10 +174,8 @@ - (void)scrollToTopOfSection:(NSInteger)section animated:(BOOL)animated
- (CGRect)frameForHeaderForSection:(NSInteger)section
{
NSIndexPath *indexPath = [NSIndexPath indexPathForItem:0 inSection:section];
UICollectionViewLayoutAttributes *attributes = [self.collectionView layoutAttributesForItemAtIndexPath:indexPath];
CGRect frameForFirstCell = attributes.frame;
CGFloat headerHeight = [self collectionView:_collectionView layout:self.flowLayout referenceSizeForHeaderInSection:section].height;
return CGRectOffset(frameForFirstCell, 0, -headerHeight);
UICollectionViewLayoutAttributes *attributes = [self.collectionView.collectionViewLayout layoutAttributesForSupplementaryViewOfKind:UICollectionElementKindSectionHeader atIndexPath:indexPath];
return attributes.frame;
}

#pragma mark - UICollectionView DataSource Delegate
Expand Down
141 changes: 75 additions & 66 deletions Linkage/Tools/LJCollectionViewFlowLayout.m
Original file line number Diff line number Diff line change
Expand Up @@ -10,102 +10,111 @@

@implementation LJCollectionViewFlowLayout

- (id)initWithCoder:(NSCoder *)aDecoder
{
self = [super initWithCoder:aDecoder];

self.scrollDirection = UICollectionViewScrollDirectionHorizontal;
self.headerReferenceSize = CGSizeMake(50, 50);
self.minimumInteritemSpacing = 0;
self.minimumLineSpacing = 0;
self.sectionInset = UIEdgeInsetsMake(0, 0, 0, 0);

return self;
}

- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect
{
NSMutableArray *answer = [[super layoutAttributesForElementsInRect:rect] mutableCopy];
UICollectionView * const cv = self.collectionView;
CGPoint const contentOffset = cv.contentOffset;
// UICollectionViewLayoutAttributes:我称它为collectionView中的item(包括cell和header、footer这些)的《结构信息》
// 截取到父类所返回的数组(里面放的是当前屏幕所能展示的item的结构信息),并转化成不可变数组
NSMutableArray *superArray = [[super layoutAttributesForElementsInRect:rect] mutableCopy];

// 创建存索引的数组,无符号(正整数),无序(不能通过下标取值),不可重复(重复的话会自动过滤)
NSMutableIndexSet *noneHeaderSections = [NSMutableIndexSet indexSet];

NSMutableIndexSet *missingSections = [NSMutableIndexSet indexSet];
for (UICollectionViewLayoutAttributes *layoutAttributes in answer)
// 遍历superArray,得到一个当前屏幕中所有的section数组
for (UICollectionViewLayoutAttributes *attributes in superArray)
{
if (layoutAttributes.representedElementCategory == UICollectionElementCategoryCell)
//如果当前的元素分类是一个cell,将cell所在的分区section加入数组,重复的话会自动过滤
if (attributes.representedElementCategory == UICollectionElementCategoryCell)
{
[missingSections addIndex:layoutAttributes.indexPath.section];
[noneHeaderSections addIndex:attributes.indexPath.section];
}
}
for (UICollectionViewLayoutAttributes *layoutAttributes in answer)

// 遍历superArray,将当前屏幕中拥有的header的section从数组中移除,得到一个当前屏幕中没有header的section数组
// 正常情况下,随着手指往上移,header脱离屏幕会被系统回收而cell尚在,也会触发该方法
for (UICollectionViewLayoutAttributes *attributes in superArray)
{
if ([layoutAttributes.representedElementKind isEqualToString:UICollectionElementKindSectionHeader])
// 如果当前的元素是一个header,将header所在的section从数组中移除
if ([attributes.representedElementKind isEqualToString:UICollectionElementKindSectionHeader])
{
[missingSections removeIndex:layoutAttributes.indexPath.section];
[noneHeaderSections removeIndex:attributes.indexPath.section];
}
}

[missingSections enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL *stop) {
// 遍历当前屏幕中没有header的section数组
[noneHeaderSections enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL *_Nonnull stop) {

// 取到当前section中第一个item的indexPath
NSIndexPath *indexPath = [NSIndexPath indexPathForItem:0 inSection:idx];
// 获取当前section在正常情况下已经离开屏幕的header结构信息
UICollectionViewLayoutAttributes *attributes = [self layoutAttributesForSupplementaryViewOfKind:UICollectionElementKindSectionHeader atIndexPath:indexPath];

UICollectionViewLayoutAttributes *layoutAttributes = [self layoutAttributesForSupplementaryViewOfKind:UICollectionElementKindSectionHeader atIndexPath:indexPath];

[answer addObject:layoutAttributes];

// 如果当前分区确实有因为离开屏幕而被系统回收的header
if (attributes)
{
// 将该header结构信息重新加入到superArray中去
[superArray addObject:attributes];
}
}];

for (UICollectionViewLayoutAttributes *layoutAttributes in answer)
// 遍历superArray,改变header结构信息中的参数,使它可以在当前section还没完全离开屏幕的时候一直显示
for (UICollectionViewLayoutAttributes *attributes in superArray)
{
if ([layoutAttributes.representedElementKind isEqualToString:UICollectionElementKindSectionHeader])

// 如果当前item是header
if ([attributes.representedElementKind isEqualToString:UICollectionElementKindSectionHeader])
{
NSInteger section = layoutAttributes.indexPath.section;
NSInteger numberOfItemsInSection = [cv numberOfItemsInSection:section];

NSIndexPath *firstCellIndexPath = [NSIndexPath indexPathForItem:0 inSection:section];
NSIndexPath *lastCellIndexPath = [NSIndexPath indexPathForItem:MAX(0, (numberOfItemsInSection - 1)) inSection:section];

UICollectionViewLayoutAttributes *firstCellAttrs = [self layoutAttributesForItemAtIndexPath:firstCellIndexPath];
UICollectionViewLayoutAttributes *lastCellAttrs = [self layoutAttributesForItemAtIndexPath:lastCellIndexPath];


if (self.scrollDirection == UICollectionViewScrollDirectionVertical)
// 得到当前header所在分区的cell的数量
NSInteger numberOfItemsInSection = [self.collectionView numberOfItemsInSection:attributes.indexPath.section];
// 得到第一个item的indexPath
NSIndexPath *firstItemIndexPath = [NSIndexPath indexPathForItem:0 inSection:attributes.indexPath.section];
// 得到最后一个item的indexPath
NSIndexPath *lastItemIndexPath = [NSIndexPath indexPathForItem:MAX(0, numberOfItemsInSection - 1) inSection:attributes.indexPath.section];
// 得到第一个item和最后一个item的结构信息
UICollectionViewLayoutAttributes *firstItemAttributes, *lastItemAttributes;
if (numberOfItemsInSection > 0)
{
CGFloat headerHeight = CGRectGetHeight(layoutAttributes.frame);
CGPoint origin = layoutAttributes.frame.origin;
origin.y = MIN(
MAX(contentOffset.y, (CGRectGetMinY(firstCellAttrs.frame) - headerHeight)),
(CGRectGetMaxY(lastCellAttrs.frame) - headerHeight)
);

layoutAttributes.zIndex = 1024;
layoutAttributes.frame = (CGRect){
.origin = origin,
.size = layoutAttributes.frame.size
};
// cell有值,则获取第一个cell和最后一个cell的结构信息
firstItemAttributes = [self layoutAttributesForItemAtIndexPath:firstItemIndexPath];
lastItemAttributes = [self layoutAttributesForItemAtIndexPath:lastItemIndexPath];
}
else
{
CGFloat headerWidth = CGRectGetWidth(layoutAttributes.frame);
CGPoint origin = layoutAttributes.frame.origin;
origin.x = MIN(
MAX(contentOffset.x, (CGRectGetMinX(firstCellAttrs.frame) - headerWidth)),
(CGRectGetMaxX(lastCellAttrs.frame) - headerWidth)
);

layoutAttributes.zIndex = 1024;
layoutAttributes.frame = (CGRect){
.origin = origin,
.size = layoutAttributes.frame.size
};
// cell没值,就新建一个UICollectionViewLayoutAttributes
firstItemAttributes = [UICollectionViewLayoutAttributes new];
// 然后模拟出在当前分区中的唯一一个cell,cell在header的下面,高度为0,还与header隔着可能存在的sectionInset的top
CGFloat y = CGRectGetMaxY(attributes.frame) + self.sectionInset.top;
firstItemAttributes.frame = CGRectMake(0, y, 0, 0);
// 因为只有一个cell,所以最后一个cell等于第一个cell
lastItemAttributes = firstItemAttributes;
}

// 获取当前header的frame
CGRect rect = attributes.frame;
// 当前的滑动距离 + 因为导航栏产生的偏移量,默认为64(如果app需求不同,需自己设置)
CGFloat offset = self.collectionView.contentOffset.y;
// 第一个cell的y值 - 当前header的高度 - 可能存在的sectionInset的top
CGFloat headerY = firstItemAttributes.frame.origin.y - rect.size.height - self.sectionInset.top;
// 哪个大取哪个,保证header悬停
// 针对当前header基本上都是offset更加大,针对下一个header则会是headerY大,各自处理
CGFloat maxY = MAX(offset, headerY);
// 最后一个cell的y值 + 最后一个cell的高度 + 可能存在的sectionInset的bottom - 当前header的高度
// 当当前section的footer或者下一个section的header接触到当前header的底部,计算出的headerMissingY即为有效值
CGFloat headerMissingY = CGRectGetMaxY(lastItemAttributes.frame) + self.sectionInset.bottom - rect.size.height;
// 给rect的y赋新值,因为在最后消失的临界点要跟谁消失,所以取小
rect.origin.y = MIN(maxY, headerMissingY);
// 给header的结构信息的frame重新赋值
attributes.frame = rect;
// 如果按照正常情况下,header离开屏幕被系统回收,而header的层次关系又与cell相等,如果不去理会,会出现cell在header上面的情况
// 通过打印可以知道cell的层次关系zIndex数值为0,我们可以将header的zIndex设置成1,如果不放心,也可以将它设置成非常大,这里随便填了个7
attributes.zIndex = 7;
}
}

return answer;
// 转换回不可变数组,并返回
return [superArray copy];
}

// return YES:表示一旦滑动就实时调用上面这个layoutAttributesForElementsInRect:方法
- (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBound
{
return YES;
Expand Down
12 changes: 10 additions & 2 deletions Linkage/liwushuo.json
Original file line number Diff line number Diff line change
Expand Up @@ -1549,8 +1549,16 @@
"status": 0
}
]
}
},
{
"icon_url": "http://img03.liwushuo.com/image/150615/3nc5tejwl.png-pw144",
"id": 1,
"name": "空的",
"order": 11,
"status": 0,
"subcategories": []
}
]
},
"message": "OK"
}
}

0 comments on commit 912b8a0

Please sign in to comment.