Skip to content

Instantly share code, notes, and snippets.

@wuyongrui
Last active April 9, 2018 09:06
Show Gist options
  • Select an option

  • Save wuyongrui/42e21ac286b9464dd95fc6e32cd4718d to your computer and use it in GitHub Desktop.

Select an option

Save wuyongrui/42e21ac286b9464dd95fc6e32cd4718d to your computer and use it in GitHub Desktop.
通过layout的方式实现首行首列浮效果
// 源设计: https://www.brightec.co.uk/ideas/uicollectionview-using-horizontal-and-vertical-scrolling-sticky-rows-and-columns
通过layout的方式实现首行首列浮效果
usage:
UICollectionView *collectionView = [[UICollectionView alloc] initWithFrame:frame layout:pendingLayout];
也可以通过左右两个tableView/collectionView组合的方式,通过同步contentOffset。这种方式实现多个pending的效果可能更简便一点;
//
// PendingLayout.h
// Brightec
//
// Created by JOSE MARTINEZ on 03/09/2014.
// Copyright (c) 2014 Brightec. All rights reserved.
//
#import <UIKit/UIKit.h>
@protocol PendingLayoutDelegate;
@interface PendingLayout : UICollectionViewFlowLayout
@property (nonatomic, weak) id<PendingLayoutDelegate> delegate;
/**
重置当前的布局数据
*/
- (void)clearAttributes;
@end
@protocol PendingLayoutDelegate <NSObject>
- (CGSize)selectTimeLayout:(PendingLayout *)layout sizeForCellAtSection:(NSInteger)section row:(NSInteger)row;
@end
//
// PendingLayout.m
// Brightec
//
// Created by JOSE MARTINEZ on 03/09/2014.
// Copyright (c) 2014 Brightec. All rights reserved.
//
#import "PendingLayout.h"
@interface PendingLayout ()
@property (strong, nonatomic) NSMutableArray *itemAttributes;
@property (nonatomic, assign) CGSize contentSize;
@end
@implementation PendingLayout
- (void)clearAttributes {
self.itemAttributes = @[].mutableCopy;
}
- (void)prepareLayout
{
if ([self.collectionView numberOfSections] == 0) {
return;
}
if (self.itemAttributes.count > 0) { // We don't enter in this if statement the first time, we enter the following times
for (int section = 0; section < [self.collectionView numberOfSections]; section++) {
NSUInteger numberOfItems = [self.collectionView numberOfItemsInSection:section];
for (NSUInteger index = 0; index < numberOfItems; index++) {
if (section != 0 && index != 0) { // This is a content cell that shouldn't be sticked
continue;
}
UICollectionViewLayoutAttributes *attributes = [self layoutAttributesForItemAtIndexPath:[NSIndexPath indexPathForItem:index inSection:section]];
if (section == 0) { // We stick the first row
CGRect frame = attributes.frame;
frame.origin.y = self.collectionView.contentOffset.y;
attributes.frame = frame;
}
if (index == 0) { // We stick the first column
CGRect frame = attributes.frame;
frame.origin.x = self.collectionView.contentOffset.x;
attributes.frame = frame;
}
}
}
return;
}
// The following code is only executed the first time we prepare the layout
self.itemAttributes = [@[] mutableCopy];
NSUInteger columnIndex = 0; // 当前列所以
CGFloat xOffset = 0.0;
CGFloat yOffset = 0.0;
CGFloat contentWidth = 0.0; // To determine the contentSize
CGFloat contentHeight = 0.0; // To determine the contentSize
// We loop through all items
NSInteger numberOfSection = [self.collectionView numberOfSections];
for (int section = 0; section < numberOfSection; section++) {
NSInteger numberOfColumns = [self.collectionView numberOfItemsInSection:section];
NSMutableArray *sectionAttributes = [@[] mutableCopy];
for (NSUInteger index = 0; index < numberOfColumns; index++) {
CGSize itemSize = [self.delegate selectTimeLayout:self sizeForCellAtSection:section row:index];
// We create the UICollectionViewLayoutAttributes object for each item and add it to our array.
// We will use this later in layoutAttributesForItemAtIndexPath:
NSIndexPath *indexPath = [NSIndexPath indexPathForItem:index inSection:section];
UICollectionViewLayoutAttributes *attributes = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];
attributes.frame = CGRectIntegral(CGRectMake(xOffset, yOffset, itemSize.width, itemSize.height));
if (section == 0 && index == 0) {
attributes.zIndex = 1024; // Set this value for the first item (Sec0Row0) in order to make it visible over first column and first row
} else if (section == 0 || index == 0) {
attributes.zIndex = 1023; // Set this value for the first row or section in order to set visible over the rest of the items
}
/*
第一行和第一列修正坐标,达到悬浮效果
*/
if (section == 0) {
CGRect frame = attributes.frame;
frame.origin.y = self.collectionView.contentOffset.y;
attributes.frame = frame; // Stick to the top
}
if (index == 0) {
CGRect frame = attributes.frame;
frame.origin.x = self.collectionView.contentOffset.x;
attributes.frame = frame; // Stick to the left
}
[sectionAttributes addObject:attributes];
xOffset = xOffset+itemSize.width;
columnIndex++;
// Create a new row if this was the last column
if (columnIndex == numberOfColumns) {
if (xOffset > contentWidth) {
contentWidth = xOffset;
}
// Reset values
columnIndex = 0;
xOffset = 0;
yOffset += itemSize.height;
}
}
[self.itemAttributes addObject:sectionAttributes];
}
// Get the last item to calculate the total height of the content
UICollectionViewLayoutAttributes *attributes = [[self.itemAttributes lastObject] lastObject];
contentHeight = attributes.frame.origin.y+attributes.frame.size.height;
self.contentSize = CGSizeMake(contentWidth, contentHeight);
}
- (CGSize)collectionViewContentSize
{
return self.contentSize;
}
- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath
{
return self.itemAttributes[indexPath.section][indexPath.row];
}
- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect
{
NSMutableArray *attributes = [@[] mutableCopy];
for (NSArray *section in self.itemAttributes) {
[attributes addObjectsFromArray:[section filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(UICollectionViewLayoutAttributes *evaluatedObject, NSDictionary *bindings) {
return CGRectIntersectsRect(rect, [evaluatedObject frame]);
}]]];
}
return attributes;
}
- (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds
{
return YES; // Set this to YES to call prepareLayout on every scroll
}
//- (CGSize)sizeForItemWithColumnIndex:(NSUInteger)columnIndex
//{
// NSString *text;
// switch (columnIndex) { // This only makes sense if the size of the items should be different
// case 0:
// text = @"Col 0";
// break;
// case 1:
// text = @"Col 1";
// break;
// case 2:
// text = @"Col 2";
// break;
// case 3:
// text = @"Col 3";
// break;
// case 4:
// text = @"Col 4";
// break;
// case 5:
// text = @"Col 5";
// break;
// case 6:
// text = @"Col 6";
// break;
// case 7:
// text = @"Col 7";
// break;
//
// default:
// break;
// }
// CGSize size = [text sizeWithAttributes: @{NSFontAttributeName:[UIFont fontWithName:@"HelveticaNeue" size:15]}];
// if (columnIndex == 0) {
// size.width += 12; // In our design the first column should be the widest one
// }
// return CGSizeMake([@(size.width + 9) floatValue], 30); // Extra space of 9px for all the items
//}
//- (void)calculateItemsSize
//{
// for (NSUInteger index = 0; index < NUMBEROFCOLUMNS; index++) {
// if (self.itemsSize.count <= index) {
// CGSize itemSize = [self sizeForItemWithColumnIndex:index];
// NSValue *itemSizeValue = [NSValue valueWithCGSize:itemSize];
// [self.itemsSize addObject:itemSizeValue];
// }
// }
//}
@end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment