mirror of
https://github.com/tappollo/IGListKit.git
synced 2026-04-29 04:15:02 +08:00
handle float number fraction part for layout.
Summary: [IGListCollectionViewLayout]: handle float number fraction part for layout. When width of item is a float number with non-zero fraction part. After integer scaled, it might add up smaller or bigger than maxWidth. Ex with 6 items with 13 padding on each side 1. iPhone 7, maxWidth = 349, scaledItemWidth = 58 -> 58 * 6 = 348, which is smaller than 349 -> this can cause issue where is extra one line at end 2. iPone 7 plus, maxWidth = 388, scaledItemWidth = 64.6667 -> 64.6667 * 6 = 388.0002, which is bigger than 388 -> this can cause issue where last item is mistakenly shifted to next row To fix it, add epsilon = 1 to allow some error range Also add stretchToEdge BOOL flag to handle case 1 to decide whether to strech the width of last item. Reviewed By: ryanolsonk, jessesquires Differential Revision: D4720156 fbshipit-source-id: 765f6b13b7d601394d65788c30ae69ac1b37c3f2
This commit is contained in:
committed by
Facebook Github Bot
parent
a80245a696
commit
24308a9881
@@ -83,10 +83,12 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
Create and return a new collection view layout.
|
||||
@param stickyHeaders Set to `YES` to stick section headers to the top of the bounds while scrolling.
|
||||
@param topContentInset The top content inset used to offset the sticky headers. Ignored if stickyHeaders is `NO`.
|
||||
@param stretchToEdge Specifies whether to stretch width of last item to right edge when distance from last item to right edge < epsilon(1)
|
||||
@return A new collection view layout.
|
||||
*/
|
||||
- (instancetype)initWithStickyHeaders:(BOOL)stickyHeaders
|
||||
topContentInset:(CGFloat)topContentInset NS_DESIGNATED_INITIALIZER;
|
||||
topContentInset:(CGFloat)topContentInset
|
||||
stretchToEdge:(BOOL)stretchToEdge NS_DESIGNATED_INITIALIZER;
|
||||
|
||||
/**
|
||||
:nodoc:
|
||||
|
||||
@@ -76,6 +76,7 @@ static void adjustZIndexForAttributes(UICollectionViewLayoutAttributes *attribut
|
||||
|
||||
@property (nonatomic, assign, readonly) BOOL stickyHeaders;
|
||||
@property (nonatomic, assign, readonly) CGFloat topContentInset;
|
||||
@property (nonatomic, assign, readonly) BOOL stretchToEdge;
|
||||
|
||||
@end
|
||||
|
||||
@@ -98,10 +99,12 @@ static void adjustZIndexForAttributes(UICollectionViewLayoutAttributes *attribut
|
||||
}
|
||||
|
||||
- (instancetype)initWithStickyHeaders:(BOOL)stickyHeaders
|
||||
topContentInset:(CGFloat)topContentInset {
|
||||
topContentInset:(CGFloat)topContentInset
|
||||
stretchToEdge:(BOOL)stretchToEdge {
|
||||
if (self = [super init]) {
|
||||
_stickyHeaders = stickyHeaders;
|
||||
_topContentInset = topContentInset;
|
||||
_stretchToEdge = stretchToEdge;
|
||||
_attributesCache = [NSMutableDictionary new];
|
||||
_headerAttributesCache = [NSMutableDictionary new];
|
||||
_cachedLayoutInvalid = YES;
|
||||
@@ -110,7 +113,7 @@ static void adjustZIndexForAttributes(UICollectionViewLayoutAttributes *attribut
|
||||
}
|
||||
|
||||
- (instancetype)initWithCoder:(NSCoder *)aDecoder {
|
||||
return [self initWithStickyHeaders:NO topContentInset:0];
|
||||
return [self initWithStickyHeaders:NO topContentInset:0 stretchToEdge:NO];
|
||||
}
|
||||
|
||||
#pragma mark - UICollectionViewLayout
|
||||
@@ -349,19 +352,21 @@ static void adjustZIndexForAttributes(UICollectionViewLayoutAttributes *attribut
|
||||
|
||||
// the farthest right the frame of an item in this section can go
|
||||
const CGFloat maxX = width - insets.right;
|
||||
|
||||
|
||||
for (NSInteger item = 0; item < itemCount; item++) {
|
||||
NSIndexPath *indexPath = [NSIndexPath indexPathForItem:item inSection:section];
|
||||
const CGSize size = [delegate collectionView:collectionView layout:self sizeForItemAtIndexPath:indexPath];
|
||||
|
||||
IGAssert(size.width <= paddedWidth, @"Width of item %zi in section %zi must be less than container %.0f accounting for section insets %@",
|
||||
item, section, width, NSStringFromUIEdgeInsets(insets));
|
||||
const CGFloat itemWidth = MIN(size.width, paddedWidth);
|
||||
CGFloat itemWidth = MIN(size.width, paddedWidth);
|
||||
|
||||
// if the x + width of the item busts the width of the container
|
||||
// or if this is the first item and the header has a non-zero size
|
||||
// newline to the next row and reset
|
||||
if (itemX + itemWidth > maxX
|
||||
// define epsilon to avoid float overflow issue
|
||||
const CGFloat epsilon = 1.0;
|
||||
if (itemX + itemWidth > maxX + epsilon
|
||||
|| (item == 0 && headerExists)) {
|
||||
itemY = nextRowY;
|
||||
itemX = insets.left;
|
||||
@@ -371,6 +376,11 @@ static void adjustZIndexForAttributes(UICollectionViewLayoutAttributes *attribut
|
||||
itemY += lineSpacing;
|
||||
}
|
||||
}
|
||||
|
||||
const CGFloat distanceToRighEdge = paddedWidth - (itemX + itemWidth);
|
||||
if (self.stretchToEdge && distanceToRighEdge > 0 && distanceToRighEdge <= epsilon) {
|
||||
itemWidth = paddedWidth - itemX;
|
||||
}
|
||||
|
||||
const CGRect frame = IGListRectIntegralScaled(CGRectMake(itemX,
|
||||
itemY + insets.top,
|
||||
|
||||
@@ -50,7 +50,11 @@ XCTAssertEqual(CGRectGetHeight(expected), CGRectGetHeight(frame)); \
|
||||
}
|
||||
|
||||
- (void)setUpWithStickyHeaders:(BOOL)sticky topInset:(CGFloat)inset {
|
||||
self.layout = [[IGListCollectionViewLayout alloc] initWithStickyHeaders:sticky topContentInset:inset];
|
||||
[self setUpWithStickyHeaders:sticky topInset:inset stretchToEdge:NO];
|
||||
}
|
||||
|
||||
- (void)setUpWithStickyHeaders:(BOOL)sticky topInset:(CGFloat)inset stretchToEdge:(BOOL)stretchToEdge {
|
||||
self.layout = [[IGListCollectionViewLayout alloc] initWithStickyHeaders:sticky topContentInset:inset stretchToEdge:stretchToEdge];
|
||||
self.dataSource = [IGLayoutTestDataSource new];
|
||||
self.collectionView = [[UICollectionView alloc] initWithFrame:kTestFrame collectionViewLayout:self.layout];
|
||||
self.collectionView.dataSource = self.dataSource;
|
||||
@@ -590,6 +594,81 @@ XCTAssertEqual(CGRectGetHeight(expected), CGRectGetHeight(frame)); \
|
||||
IGAssertEqualFrame([self cellForSection:0 item:1].frame, 0, 20, 40, 20);
|
||||
}
|
||||
|
||||
- (void)test_whenItemsAddedWidthSmallerThanWidth_DifferenceSmallerThanEpsilon {
|
||||
[self setUpWithStickyHeaders:NO topInset:0 stretchToEdge:YES];
|
||||
|
||||
const CGSize size = CGSizeMake(33, 33);
|
||||
[self prepareWithData:@[
|
||||
[[IGLayoutTestSection alloc] initWithInsets:UIEdgeInsetsZero
|
||||
lineSpacing:0
|
||||
interitemSpacing:0
|
||||
headerHeight:0
|
||||
items:@[
|
||||
[[IGLayoutTestItem alloc] initWithSize:size],
|
||||
[[IGLayoutTestItem alloc] initWithSize:size],
|
||||
[[IGLayoutTestItem alloc] initWithSize:size],
|
||||
]],
|
||||
]];
|
||||
|
||||
IGAssertEqualFrame([self cellForSection:0 item:0].frame, 0, 0, 33, 33);
|
||||
IGAssertEqualFrame([self cellForSection:0 item:1].frame, 33, 0, 33, 33);
|
||||
IGAssertEqualFrame([self cellForSection:0 item:2].frame, 66, 0, 34, 33);
|
||||
}
|
||||
|
||||
- (void)test_whenItemsAddedWidthSmallerThanWidth_DifferenceBiggerThanEpsilon {
|
||||
[self setUpWithStickyHeaders:NO topInset:0 stretchToEdge:YES];
|
||||
|
||||
[self prepareWithData:@[
|
||||
[[IGLayoutTestSection alloc] initWithInsets:UIEdgeInsetsZero
|
||||
lineSpacing:0
|
||||
interitemSpacing:0
|
||||
headerHeight:0
|
||||
items:@[
|
||||
[[IGLayoutTestItem alloc] initWithSize:CGSizeMake(33, 33)],
|
||||
[[IGLayoutTestItem alloc] initWithSize:CGSizeMake(65, 33)],
|
||||
]],
|
||||
]];
|
||||
|
||||
IGAssertEqualFrame([self cellForSection:0 item:0].frame, 0, 0, 33, 33);
|
||||
IGAssertEqualFrame([self cellForSection:0 item:1].frame, 33, 0, 65, 33);
|
||||
}
|
||||
|
||||
- (void)test_whenItemsAddedWithBiggerThanWidth_DifferenceSmallerThanEpsilon {
|
||||
[self setUpWithStickyHeaders:NO topInset:0];
|
||||
|
||||
[self prepareWithData:@[
|
||||
[[IGLayoutTestSection alloc] initWithInsets:UIEdgeInsetsZero
|
||||
lineSpacing:0
|
||||
interitemSpacing:0
|
||||
headerHeight:0
|
||||
items:@[
|
||||
[[IGLayoutTestItem alloc] initWithSize:CGSizeMake(50, 50)],
|
||||
[[IGLayoutTestItem alloc] initWithSize:CGSizeMake(51, 50)],
|
||||
]],
|
||||
]];
|
||||
|
||||
IGAssertEqualFrame([self cellForSection:0 item:0].frame, 0, 0, 50, 50);
|
||||
IGAssertEqualFrame([self cellForSection:0 item:1].frame, 50, 0, 51, 50);
|
||||
}
|
||||
|
||||
- (void)test_whenItemsAddedWithBiggerThanWidth_DifferenceBiggerThanEpsilon {
|
||||
[self setUpWithStickyHeaders:NO topInset:0];
|
||||
|
||||
[self prepareWithData:@[
|
||||
[[IGLayoutTestSection alloc] initWithInsets:UIEdgeInsetsZero
|
||||
lineSpacing:0
|
||||
interitemSpacing:0
|
||||
headerHeight:0
|
||||
items:@[
|
||||
[[IGLayoutTestItem alloc] initWithSize:CGSizeMake(50, 50)],
|
||||
[[IGLayoutTestItem alloc] initWithSize:CGSizeMake(52, 50)],
|
||||
]],
|
||||
]];
|
||||
|
||||
IGAssertEqualFrame([self cellForSection:0 item:0].frame, 0, 0, 50, 50);
|
||||
IGAssertEqualFrame([self cellForSection:0 item:1].frame, 0, 50, 52, 50);
|
||||
}
|
||||
|
||||
- (void)test_ {
|
||||
[self setUpWithStickyHeaders:NO topInset:0];
|
||||
self.collectionView.frame = CGRectMake(0, 0, 414, 736);
|
||||
|
||||
Reference in New Issue
Block a user