UICollectionView insert cells above maintaining position (like Messages.app)

后端 未结 20 2087
渐次进展
渐次进展 2020-12-02 07:23

By default Collection View maintains content offset while inserting cells. On the other hand I\'d like to insert cells above the currently displaying ones so that they appea

20条回答
  •  半阙折子戏
    2020-12-02 08:11

    Love James Martin’s solution. But for me it started to breakdown when inserting/deleting above/below a specific content window. I took a stab at subclassing UICollectionViewFlowLayout to get the behavior I wanted. Hope this helps someone. Any feedback appreciated :)

    @interface FixedScrollCollectionViewFlowLayout () {
    
        __block float bottomMostVisibleCell;
        __block float topMostVisibleCell;
    }
    
    @property (nonatomic, assign) BOOL isInsertingCellsToTop;
    @property (nonatomic, strong) NSArray *visableAttributes;
    @property (nonatomic, assign) float offset;;
    
    @end
    
    @implementation FixedScrollCollectionViewFlowLayout
    
    
    - (id)initWithCoder:(NSCoder *)aDecoder {
    
        self = [super initWithCoder:aDecoder];
    
        if (self) {
            _isInsertingCellsToTop = NO;
        }
        return self;
    }
    
    - (id)init {
    
        self = [super init];
    
        if (self) {
            _isInsertingCellsToTop = NO;
        }
        return self;
    }
    
    - (void)prepareLayout {
    
        NSLog(@"prepareLayout");
        [super prepareLayout];
    }
    
    - (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect {
    
        NSLog(@"layoutAttributesForElementsInRect");
        self.visableAttributes = [super layoutAttributesForElementsInRect:rect];
        self.offset = 0;
        self.isInsertingCellsToTop = NO;
        return self.visableAttributes;
    }
    
    - (void)prepareForCollectionViewUpdates:(NSArray *)updateItems {
    
        bottomMostVisibleCell = -MAXFLOAT;
        topMostVisibleCell = MAXFLOAT;
        CGRect container = CGRectMake(self.collectionView.contentOffset.x, self.collectionView.contentOffset.y, self.collectionView.frame.size.width, self.collectionView.frame.size.height);
    
        [self.visableAttributes  enumerateObjectsUsingBlock:^(UICollectionViewLayoutAttributes *attributes, NSUInteger idx, BOOL *stop) {
    
            CGRect currentCellFrame =  attributes.frame;
            CGRect containerFrame = container;
    
            if(CGRectIntersectsRect(containerFrame, currentCellFrame)) {
                float x = attributes.indexPath.row;
                if (x < topMostVisibleCell) topMostVisibleCell = x;
                if (x > bottomMostVisibleCell) bottomMostVisibleCell = x;
            }
        }];
    
        NSLog(@"prepareForCollectionViewUpdates");
        [super prepareForCollectionViewUpdates:updateItems];
        for (UICollectionViewUpdateItem *updateItem in updateItems) {
            switch (updateItem.updateAction) {
                case UICollectionUpdateActionInsert:{
                    NSLog(@"UICollectionUpdateActionInsert %ld",updateItem.indexPathAfterUpdate.row);
                    if (topMostVisibleCell>updateItem.indexPathAfterUpdate.row) {
                        UICollectionViewLayoutAttributes * newAttributes = [self layoutAttributesForItemAtIndexPath:updateItem.indexPathAfterUpdate];
                        self.offset += (newAttributes.size.height + self.minimumLineSpacing);
                        self.isInsertingCellsToTop = YES;
                    }
                    break;
                }
                case UICollectionUpdateActionDelete: {
                    NSLog(@"UICollectionUpdateActionDelete %ld",updateItem.indexPathBeforeUpdate.row);
                    if (topMostVisibleCell>updateItem.indexPathBeforeUpdate.row) {
                        UICollectionViewLayoutAttributes * newAttributes = [self layoutAttributesForItemAtIndexPath:updateItem.indexPathBeforeUpdate];
                        self.offset -= (newAttributes.size.height + self.minimumLineSpacing);
                        self.isInsertingCellsToTop = YES;
                    }
                    break;
                }
                case UICollectionUpdateActionMove:
                    NSLog(@"UICollectionUpdateActionMoveB %ld", updateItem.indexPathBeforeUpdate.row);
                    break;
                default:
                    NSLog(@"unhandled case: %ld", updateItem.indexPathBeforeUpdate.row);
                    break;
            }
        }
    
        if (self.isInsertingCellsToTop) {
            if (self.collectionView) {
                [CATransaction begin];
                [CATransaction setDisableActions:YES];
            }
        }
    }
    
    - (void)finalizeCollectionViewUpdates {
    
        CGPoint newOffset = CGPointMake(self.collectionView.contentOffset.x, self.collectionView.contentOffset.y + self.offset);
    
        if (self.isInsertingCellsToTop) {
            if (self.collectionView) {
                self.collectionView.contentOffset = newOffset;
                [CATransaction commit];
            }
        }
    }
    

提交回复
热议问题