• One: listen for the time when scrolling stops
  • Listen for the time when reloadData finishes refreshing the list
  • Three, free drag
  • Waterfall flow
  • Float headers

One: listen for the time when scrolling stops

Scenario 1: Manually drag the screen to stop

Scenario 2. InvokescrollToRowstop

Scenario 3. InvocationsetContentOffsetstop

self.tableView.setContentOffset(CGPoint.init(x: 0, y: 0), animated: false)
print("When I execute this line of code, I'm done scrolling.")
Copy the code

Listen for the time when reloadData finishes refreshing the list

Listen for the time when reloadData finishes refreshing the list

  • Method 1. PasslayoutIfNeededMethod to force a redraw and wait for completion.
  • Method 2,reloadDataThe method is executed on the main thread, throughGCD, so that subsequent operations queue inreloadDataExecute later. At a timerunloopThere are two opportunities to executeGCD dispatch main queueBefore sleep and after wake up. Set up thelistViewlayoutIfNeededTo YES, perform the asynchronous task and redraw the interface when it is about to enter sleep.
  • Method 3: CustomizeUICollectionView,UITableView.layoutSubviewsAfter areloadDataDone (complex, but better understood method 1)

Three, free drag

First, iOS9 before thinking:

  • The first step:UICollectionviewCellAdd a long press gestureUILongPressGestureRecognizer, passed through the proxy toUIViewControllerIn the.
  • The second step: Start adding hours (UIGestureRecognizerStateBegan) tocellTake a screenshot and hide itcell.
  • The third step: When moving (UIGestureRecognizerStateChanged) move the screenshot, iterate to get which screenshot to move tocellThe location of the. A method is calledmoveItemAtIndexPath:toIndexPath:Change the twocellAnd update the order of the data sources.
  • The fourth step: Stop (UIGestureRecognizerStateEnded) remove the screenshot and displaycell.

Reference blog:

[iOSUI advanced drag] drop sort implementation

DraggingSort

LXReorderableCollectionViewFlowLayout

XWDragCellCollectionView

CollectionViewCellDragExchange

Two, iOS9 after thinking:

  • Step 1: HereUICollectionviewCellAdd a long press gestureUILongPressGestureRecognizer, passed through the proxy toUIViewControllerIn the.
  • Step 2: Start adding hours (UIGestureRecognizerStateBegan) Take a screenshot of the cell and hide itcell. callbeginInteractiveMovementForItem.
  • Step 3: When moving (UIGestureRecognizerStateChanged) Move screenshot. callupdateInteractiveMovementTargetPosition.
  • Step 4: Stop (UIGestureRecognizerStateEnded) remove the screenshot and displaycell. callendInteractiveMovement.
// Support forReordering @ the available (iOS 9.0. *) open func beginInteractiveMovementForItem (ats indexPath: IndexPath) -> Bool // returns NOifReordering was prevented from common-otherwise YES @available(iOS 9.0, *) open func updateInteractiveMovementTargetPosition(_ targetPosition: CGPoint) @available(iOS 9.0, *) Open func endInteractiveMovement() @available(iOS 9.0, *) *) open func cancelInteractiveMovement()Copy the code

Reference blog:

IOS UICollectionView Advanced Usage (Hold down free move cell)- New

Waterfall flow

The basic principle for implementing waterfall flow is to find the shortest column and put item under the shortest column

As you can see in the figure below, the eighth Item is added below the third column because the third column is the shortest.

Since you want to find the shortest column, you need a data store for each column’s Y value, which is recommended as an array (relatively better than a dictionary). XRWaterfallLayout uses a dictionary for storage, which is partially hashed more than arrays whose subscripts directly retrieve column heights. As a waterfall stream, the size of the image may not be fixed. The size of the image can be returned by the server in advance, and the cell can set the size directly.

Three system methods need to be rewritten: XRWaterfallLayout, 1 – (void) prepareLayout 2, 3 – (CGSize) collectionViewContentSize, – (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect

Float headers

One, in theiOS9after

UICollectionView’s header view can also hover like a tableView’s header.

UICollectionViewFlowLayout *flowLayout = [[UICollectionViewFlowLayout alloc] init]; 
//header 
flowLayout.sectionHeadersPinToVisibleBounds = YES; 
//footer
flowLayout.sectionFootersPinToVisibleBounds = YES;
Copy the code

Second, iniOS9.0before

Need to customize UICollectionViewFlowLayout

#import <UIKit/UIKit.h>
@protocol LZSFloatHeaderFlowLayoutDelegate <NSObject>
-(void)sectionDidFloat:(NSInteger)section;
@end

@interface LZSFloatHeaderFlowLayout : UICollectionViewFlowLayout
@property (nonatomic, weak) id<LZSFloatHeaderFlowLayoutDelegate> mDelegate;
@end
Copy the code
#import "LZSFloatHeaderFlowLayout.h"

@interface LZSFloatHeaderFlowLayout()
@property (nonatomic, strong) NSMutableDictionary<NSNumber*, NSNumber*>* mSectionOffsetYDic;
@end

@implementation LZSFloatHeaderFlowLayout
-(NSMutableDictionary<NSNumber *,NSNumber *> *)mSectionOffsetYDic {
    if ( !_mSectionOffsetYDic ) {
        _mSectionOffsetYDic = [NSMutableDictionary dictionary];
    }
    return _mSectionOffsetYDic;
}
- (NSArray *) layoutAttributesForElementsInRect:(CGRect)rect {
    NSMutableArray *answer = [[super layoutAttributesForElementsInRect:rect] mutableCopy];
    NSMutableIndexSet *missingSections = [NSMutableIndexSet indexSet];
    for (NSUInteger idx=0; idx<[answer count]; idx++) {
        UICollectionViewLayoutAttributes *layoutAttributes = answer[idx];
        if (layoutAttributes.representedElementCategory == UICollectionElementCategoryCell) {
            [missingSections addIndex:layoutAttributes.indexPath.section];  // remember that we need to layout header for this section
        }
        if ([layoutAttributes.representedElementKind isEqualToString:UICollectionElementKindSectionHeader]) {
            [answer removeObjectAtIndex:idx];  // remove layout of header done by our super, we will do it right later
            idx--;
        }
    }
    // layout all headers needed for the rect using self code
    [missingSections enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL *stop) {
        NSIndexPath *indexPath = [NSIndexPath indexPathForItem:0 inSection:idx];
        UICollectionViewLayoutAttributes *layoutAttributes = [self layoutAttributesForSupplementaryViewOfKind:UICollectionElementKindSectionHeader
                                                                                                  atIndexPath:indexPath];
        [answer addObject:layoutAttributes];
    }];
    return answer;
}
- (UICollectionViewLayoutAttributes *)layoutAttributesForSupplementaryViewOfKind:(NSString *)kind
                                                                     atIndexPath:(NSIndexPath *)indexPath {
    UICollectionViewLayoutAttributes *attributes = [super layoutAttributesForSupplementaryViewOfKind:kind
                                                                                         atIndexPath:indexPath];
    if ([kind isEqualToString:UICollectionElementKindSectionHeader]) {
        UICollectionView * const cv = self.collectionView;
        CGPoint const contentOffset = cv.contentOffset;
        CGPoint nextHeaderOrigin = CGPointMake(INFINITY, INFINITY);
        
        if (indexPath.section+1 < [cv numberOfSections]) {
            NSIndexPath* tIndexPath = [NSIndexPath indexPathForItem:0 inSection:indexPath.section+1]
            UICollectionViewLayoutAttributes *nextHeaderAttributes = [super layoutAttributesForSupplementaryViewOfKind:kind
                                                                                                           atIndexPath:tIndexPath];
            nextHeaderOrigin = nextHeaderAttributes.frame.origin;
        }
        CGRect frame = attributes.frame;
        if (self.scrollDirection == UICollectionViewScrollDirectionVertical) {
            frame.origin.y = MIN(MAX(contentOffset.y, frame.origin.y), nextHeaderOrigin.y - CGRectGetHeight(frame));
        }
        else { // UICollectionViewScrollDirectionHorizontal
            frame.origin.x = MIN(MAX(contentOffset.x, frame.origin.x), nextHeaderOrigin.x - CGRectGetWidth(frame));
        }
        attributes.zIndex = 1024;
        attributes.frame = frame;
        if( self.mSectionOffsetYDic[@(indexPath.section)] && (self.mSectionOffsetYDic[@(indexPath.section)].integerValue ! = frame.origin.y) ) {if ( [self.mDelegate respondsToSelector:@selector(sectionDidFloat:)] ) {
                [self.mDelegate sectionDidFloat:indexPath.section];
            }
        }
        self.mSectionOffsetYDic[@(indexPath.section)] = @(frame.origin.y);
    }
    return attributes;
}
- (UICollectionViewLayoutAttributes *)initialLayoutAttributesForAppearingSupplementaryElementOfKind:(NSString *)kind
                                                                                        atIndexPath:(NSIndexPath *)indexPath {
    UICollectionViewLayoutAttributes *attributes = [self layoutAttributesForSupplementaryViewOfKind:kind
                                                                                        atIndexPath:indexPath];
    return attributes;
}
- (UICollectionViewLayoutAttributes *)finalLayoutAttributesForDisappearingSupplementaryElementOfKind:(NSString *)kind
                                                                                         atIndexPath:(NSIndexPath *)indexPath {
    UICollectionViewLayoutAttributes *attributes = [self layoutAttributesForSupplementaryViewOfKind:kind
                                                                                        atIndexPath:indexPath];
    return attributes;
}
- (BOOL) shouldInvalidateLayoutForBoundsChange:(CGRect)newBound {
    return YES;
}
Copy the code