/TeamTalk/Pods/PSTCollectionView/PSTCollectionView/PSTCollectionView.m
Objective C | 1121 lines | 873 code | 182 blank | 66 comment | 192 complexity | 927bd28ebf08449a91be2a0143da05bc MD5 | raw file
- //
- // PSTCollectionView.m
- // PSPDFKit
- //
- // Copyright (c) 2012-2013 Peter Steinberger. All rights reserved.
- //
- #import "PSTCollectionView.h"
- #import "PSTCollectionViewData.h"
- #import "PSTCollectionViewLayout+Internals.h"
- #import "PSTCollectionViewItemKey.h"
- #import <objc/runtime.h>
- #if TARGET_IPHONE_SIMULATOR
- #import <dlfcn.h>
- #endif
- @interface PSTCollectionViewLayout (Internal)
- @property (nonatomic, unsafe_unretained) PSTCollectionView *collectionView;
- @end
- @interface PSTCollectionViewData (Internal)
- - (void)prepareToLoadData;
- @end
- @interface PSTCollectionViewCell (Internal)
- - (void)performSelectionSegue;
- @end
- @interface PSTCollectionViewUpdateItem ()
- - (NSIndexPath *)indexPath;
- - (BOOL)isSectionOperation;
- @end
- @interface PSTCollectionViewLayoutAttributes () {
- char junk[128];
- }
- @property (nonatomic, copy) NSString *elementKind;
- @end
- CGFloat PSTSimulatorAnimationDragCoefficient(void);
- @class PSTCollectionViewExt;
- @interface PSTCollectionView () <UIScrollViewDelegate> {
- // ivar layout needs to EQUAL to UICollectionView.
- PSTCollectionViewLayout *_layout;
- __unsafe_unretained id<PSTCollectionViewDataSource> _dataSource;
- UIView *_backgroundView;
- NSMutableSet *_indexPathsForSelectedItems;
- NSMutableDictionary *_cellReuseQueues;
- NSMutableDictionary *_supplementaryViewReuseQueues;
- NSMutableDictionary *_decorationViewReuseQueues;
- NSMutableSet *_indexPathsForHighlightedItems;
- int _reloadingSuspendedCount;
- PSTCollectionReusableView *_firstResponderView;
- UIView *_newContentView;
- int _firstResponderViewType;
- NSString *_firstResponderViewKind;
- NSIndexPath *_firstResponderIndexPath;
- NSMutableDictionary *_allVisibleViewsDict;
- NSIndexPath *_pendingSelectionIndexPath;
- NSMutableSet *_pendingDeselectionIndexPaths;
- PSTCollectionViewData *_collectionViewData;
- id _update;
- CGRect _visibleBoundRects;
- CGRect _preRotationBounds;
- CGPoint _rotationBoundsOffset;
- int _rotationAnimationCount;
- int _updateCount;
- NSMutableArray *_insertItems;
- NSMutableArray *_deleteItems;
- NSMutableArray *_reloadItems;
- NSMutableArray *_moveItems;
- NSArray *_originalInsertItems;
- NSArray *_originalDeleteItems;
- UITouch *_currentTouch;
- void (^_updateCompletionHandler)(BOOL finished);
- NSMutableDictionary *_cellClassDict;
- NSMutableDictionary *_cellNibDict;
- NSMutableDictionary *_supplementaryViewClassDict;
- NSMutableDictionary *_supplementaryViewNibDict;
- NSMutableDictionary *_cellNibExternalObjectsTables;
- NSMutableDictionary *_supplementaryViewNibExternalObjectsTables;
- struct {
- unsigned int delegateShouldHighlightItemAtIndexPath : 1;
- unsigned int delegateDidHighlightItemAtIndexPath : 1;
- unsigned int delegateDidUnhighlightItemAtIndexPath : 1;
- unsigned int delegateShouldSelectItemAtIndexPath : 1;
- unsigned int delegateShouldDeselectItemAtIndexPath : 1;
- unsigned int delegateDidSelectItemAtIndexPath : 1;
- unsigned int delegateDidDeselectItemAtIndexPath : 1;
- unsigned int delegateSupportsMenus : 1;
- unsigned int delegateDidEndDisplayingCell : 1;
- unsigned int delegateDidEndDisplayingSupplementaryView : 1;
- unsigned int dataSourceNumberOfSections : 1;
- unsigned int dataSourceViewForSupplementaryElement : 1;
- unsigned int reloadSkippedDuringSuspension : 1;
- unsigned int scheduledUpdateVisibleCells : 1;
- unsigned int scheduledUpdateVisibleCellLayoutAttributes : 1;
- unsigned int allowsSelection : 1;
- unsigned int allowsMultipleSelection : 1;
- unsigned int updating : 1;
- unsigned int fadeCellsForBoundsChange : 1;
- unsigned int updatingLayout : 1;
- unsigned int needsReload : 1;
- unsigned int reloading : 1;
- unsigned int skipLayoutDuringSnapshotting : 1;
- unsigned int layoutInvalidatedSinceLastCellUpdate : 1;
- unsigned int doneFirstLayout : 1;
- }_collectionViewFlags;
- CGPoint _lastLayoutOffset;
- char filler[200]; // [HACK] Our class needs to be larger than Apple's class for the superclass change to work.
- }
- @property (nonatomic, strong) PSTCollectionViewData *collectionViewData;
- @property (nonatomic, strong, readonly) PSTCollectionViewExt *extVars;
- @property (nonatomic, readonly) id currentUpdate;
- @property (nonatomic, readonly) NSDictionary *visibleViewsDict;
- @property (nonatomic, assign) CGRect visibleBoundRects;
- @end
- // Used by PSTCollectionView for external variables.
- // (We need to keep the total class size equal to the UICollectionView variant)
- @interface PSTCollectionViewExt : NSObject
- @property (nonatomic, unsafe_unretained) id<PSTCollectionViewDelegate> collectionViewDelegate;
- @property (nonatomic, strong) PSTCollectionViewLayout *nibLayout;
- @property (nonatomic, strong) NSDictionary *nibCellsExternalObjects;
- @property (nonatomic, strong) NSDictionary *supplementaryViewsExternalObjects;
- @property (nonatomic, strong) NSIndexPath *touchingIndexPath;
- @property (nonatomic, strong) NSIndexPath *currentIndexPath;
- @end
- @implementation PSTCollectionViewExt
- @end
- const char kPSTColletionViewExt;
- @implementation PSTCollectionView
- @synthesize collectionViewLayout = _layout;
- @synthesize currentUpdate = _update;
- @synthesize visibleViewsDict = _allVisibleViewsDict;
- ///////////////////////////////////////////////////////////////////////////////////////////
- #pragma mark - NSObject
- static void PSTCollectionViewCommonSetup(PSTCollectionView *_self) {
- _self.allowsSelection = YES;
- _self->_indexPathsForSelectedItems = [NSMutableSet new];
- _self->_indexPathsForHighlightedItems = [NSMutableSet new];
- _self->_cellReuseQueues = [NSMutableDictionary new];
- _self->_supplementaryViewReuseQueues = [NSMutableDictionary new];
- _self->_decorationViewReuseQueues = [NSMutableDictionary new];
- _self->_allVisibleViewsDict = [NSMutableDictionary new];
- _self->_cellClassDict = [NSMutableDictionary new];
- _self->_cellNibDict = [NSMutableDictionary new];
- _self->_supplementaryViewClassDict = [NSMutableDictionary new];
- _self->_supplementaryViewNibDict = [NSMutableDictionary new];
- // add class that saves additional ivars
- objc_setAssociatedObject(_self, &kPSTColletionViewExt, [PSTCollectionViewExt new], OBJC_ASSOCIATION_RETAIN_NONATOMIC);
- }
- - (id)initWithFrame:(CGRect)frame {
- return [self initWithFrame:frame collectionViewLayout:nil];
- }
- - (id)initWithFrame:(CGRect)frame collectionViewLayout:(PSTCollectionViewLayout *)layout {
- if ((self = [super initWithFrame:frame])) {
- // Set self as the UIScrollView's delegate
- [super setDelegate:self];
- PSTCollectionViewCommonSetup(self);
- self.collectionViewLayout = layout;
- _collectionViewData = [[PSTCollectionViewData alloc] initWithCollectionView:self layout:layout];
- }
- return self;
- }
- - (id)initWithCoder:(NSCoder *)inCoder {
- if ((self = [super initWithCoder:inCoder])) {
- // Set self as the UIScrollView's delegate
- [super setDelegate:self];
- PSTCollectionViewCommonSetup(self);
- self.extVars.nibLayout = [inCoder decodeObjectForKey:@"UICollectionLayout"];
- NSDictionary *cellExternalObjects = [inCoder decodeObjectForKey:@"UICollectionViewCellPrototypeNibExternalObjects"];
- NSDictionary *cellNibs = [inCoder decodeObjectForKey:@"UICollectionViewCellNibDict"];
- for (NSString *identifier in cellNibs.allKeys) {
- _cellNibDict[identifier] = cellNibs[identifier];
- }
- self.extVars.nibCellsExternalObjects = cellExternalObjects;
- NSDictionary *supplementaryViewExternalObjects = [inCoder decodeObjectForKey:@"UICollectionViewSupplementaryViewPrototypeNibExternalObjects"];
- NSDictionary *supplementaryViewNibs = [inCoder decodeObjectForKey:@"UICollectionViewSupplementaryViewNibDict"];
- for (NSString *identifier in supplementaryViewNibs.allKeys) {
- _supplementaryViewNibDict[identifier] = supplementaryViewNibs[identifier];
- }
- self.extVars.supplementaryViewsExternalObjects = supplementaryViewExternalObjects;
- }
- return self;
- }
- - (void)awakeFromNib {
- [super awakeFromNib];
- PSTCollectionViewLayout *nibLayout = self.extVars.nibLayout;
- if (nibLayout) {
- self.collectionViewLayout = nibLayout;
- self.extVars.nibLayout = nil;
- }
- }
- - (NSString *)description {
- return [NSString stringWithFormat:@"%@ collection view layout: %@", [super description], self.collectionViewLayout];
- }
- ///////////////////////////////////////////////////////////////////////////////////////////
- #pragma mark - UIView
- - (void)layoutSubviews {
- [super layoutSubviews];
- // Adding alpha animation to make the relayouting smooth
- if (_collectionViewFlags.fadeCellsForBoundsChange) {
- CATransition *transition = [CATransition animation];
- transition.duration = 0.25f * PSTSimulatorAnimationDragCoefficient();
- transition.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
- transition.type = kCATransitionFade;
- [self.layer addAnimation:transition forKey:@"rotationAnimation"];
- }
- [_collectionViewData validateLayoutInRect:self.bounds];
- // update cells
- if (_collectionViewFlags.fadeCellsForBoundsChange) {
- [CATransaction begin];
- [CATransaction setDisableActions:YES];
- }
- if (!_collectionViewFlags.updatingLayout)
- [self updateVisibleCellsNow:YES];
- if (_collectionViewFlags.fadeCellsForBoundsChange) {
- [CATransaction commit];
- }
- // do we need to update contentSize?
- CGSize contentSize = [_collectionViewData collectionViewContentRect].size;
- if (!CGSizeEqualToSize(self.contentSize, contentSize)) {
- self.contentSize = contentSize;
- // if contentSize is different, we need to re-evaluate layout, bounds (contentOffset) might changed
- [_collectionViewData validateLayoutInRect:self.bounds];
- [self updateVisibleCellsNow:YES];
- }
- if (_backgroundView) {
- _backgroundView.frame = (CGRect){.origin=self.contentOffset, .size=self.bounds.size};
- }
- _collectionViewFlags.fadeCellsForBoundsChange = NO;
- _collectionViewFlags.doneFirstLayout = YES;
- }
- - (void)setFrame:(CGRect)frame {
- if (!CGRectEqualToRect(frame, self.frame)) {
- CGRect bounds = (CGRect){.origin=self.contentOffset, .size=frame.size};
- BOOL shouldInvalidate = [self.collectionViewLayout shouldInvalidateLayoutForBoundsChange:bounds];
- [super setFrame:frame];
- if (shouldInvalidate) {
- [self invalidateLayout];
- _collectionViewFlags.fadeCellsForBoundsChange = YES;
- }
- }
- }
- - (void)setBounds:(CGRect)bounds {
- if (!CGRectEqualToRect(bounds, self.bounds)) {
- BOOL shouldInvalidate = [self.collectionViewLayout shouldInvalidateLayoutForBoundsChange:bounds];
- [super setBounds:bounds];
- if (shouldInvalidate) {
- [self invalidateLayout];
- _collectionViewFlags.fadeCellsForBoundsChange = YES;
- }
- }
- }
- ///////////////////////////////////////////////////////////////////////////////////////////
- #pragma mark - UIScrollViewDelegate
- - (void)scrollViewDidScroll:(UIScrollView *)scrollView {
- id<PSTCollectionViewDelegate> delegate = self.extVars.collectionViewDelegate;
- if ((id)delegate != self && [delegate respondsToSelector:@selector(scrollViewDidScroll:)]) {
- [delegate scrollViewDidScroll:scrollView];
- }
- }
- - (void)scrollViewDidZoom:(UIScrollView *)scrollView {
- id<PSTCollectionViewDelegate> delegate = self.extVars.collectionViewDelegate;
- if ((id)delegate != self && [delegate respondsToSelector:@selector(scrollViewDidZoom:)]) {
- [delegate scrollViewDidZoom:scrollView];
- }
- }
- - (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView {
- id<PSTCollectionViewDelegate> delegate = self.extVars.collectionViewDelegate;
- if ((id)delegate != self && [delegate respondsToSelector:@selector(scrollViewWillBeginDragging:)]) {
- [delegate scrollViewWillBeginDragging:scrollView];
- }
- }
- - (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset {
- // Let collectionViewLayout decide where to stop.
- *targetContentOffset = [[self collectionViewLayout] targetContentOffsetForProposedContentOffset:*targetContentOffset withScrollingVelocity:velocity];
- id<PSTCollectionViewDelegate> delegate = self.extVars.collectionViewDelegate;
- if ((id)delegate != self && [delegate respondsToSelector:@selector(scrollViewWillEndDragging:withVelocity:targetContentOffset:)]) {
- //if collectionViewDelegate implements this method, it may modify targetContentOffset as well
- [delegate scrollViewWillEndDragging:scrollView withVelocity:velocity targetContentOffset:targetContentOffset];
- }
- }
- - (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate {
- id<PSTCollectionViewDelegate> delegate = self.extVars.collectionViewDelegate;
- if ((id)delegate != self && [delegate respondsToSelector:@selector(scrollViewDidEndDragging:willDecelerate:)]) {
- [delegate scrollViewDidEndDragging:scrollView willDecelerate:decelerate];
- }
- // if we are in the middle of a cell touch event, perform the "touchEnded" simulation
- if (self.extVars.touchingIndexPath) {
- [self cellTouchCancelled];
- }
- }
- - (void)scrollViewWillBeginDecelerating:(UIScrollView *)scrollView {
- id<PSTCollectionViewDelegate> delegate = self.extVars.collectionViewDelegate;
- if ((id)delegate != self && [delegate respondsToSelector:@selector(scrollViewWillBeginDecelerating:)]) {
- [delegate scrollViewWillBeginDecelerating:scrollView];
- }
- }
- - (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
- id<PSTCollectionViewDelegate> delegate = self.extVars.collectionViewDelegate;
- if ((id)delegate != self && [delegate respondsToSelector:@selector(scrollViewDidEndDecelerating:)]) {
- [delegate scrollViewDidEndDecelerating:scrollView];
- }
- }
- - (void)scrollViewDidEndScrollingAnimation:(UIScrollView *)scrollView {
- id<PSTCollectionViewDelegate> delegate = self.extVars.collectionViewDelegate;
- if ((id)delegate != self && [delegate respondsToSelector:@selector(scrollViewDidEndScrollingAnimation:)]) {
- [delegate scrollViewDidEndScrollingAnimation:scrollView];
- }
- }
- - (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView {
- id<PSTCollectionViewDelegate> delegate = self.extVars.collectionViewDelegate;
- if ((id)delegate != self && [delegate respondsToSelector:@selector(viewForZoomingInScrollView:)]) {
- return [delegate viewForZoomingInScrollView:scrollView];
- }
- return nil;
- }
- - (void)scrollViewWillBeginZooming:(UIScrollView *)scrollView withView:(UIView *)view {
- id<PSTCollectionViewDelegate> delegate = self.extVars.collectionViewDelegate;
- if ((id)delegate != self && [delegate respondsToSelector:@selector(scrollViewWillBeginZooming:withView:)]) {
- [delegate scrollViewWillBeginZooming:scrollView withView:view];
- }
- }
- - (void)scrollViewDidEndZooming:(UIScrollView *)scrollView withView:(UIView *)view atScale:(CGFloat)scale {
- id<PSTCollectionViewDelegate> delegate = self.extVars.collectionViewDelegate;
- if ((id)delegate != self && [delegate respondsToSelector:@selector(scrollViewDidEndZooming:withView:atScale:)]) {
- [delegate scrollViewDidEndZooming:scrollView withView:view atScale:scale];
- }
- }
- - (BOOL)scrollViewShouldScrollToTop:(UIScrollView *)scrollView {
- id<PSTCollectionViewDelegate> delegate = self.extVars.collectionViewDelegate;
- if ((id)delegate != self && [delegate respondsToSelector:@selector(scrollViewShouldScrollToTop:)]) {
- return [delegate scrollViewShouldScrollToTop:scrollView];
- }
- return YES;
- }
- - (void)scrollViewDidScrollToTop:(UIScrollView *)scrollView {
- id<PSTCollectionViewDelegate> delegate = self.extVars.collectionViewDelegate;
- if ((id)delegate != self && [delegate respondsToSelector:@selector(scrollViewDidScrollToTop:)]) {
- [delegate scrollViewDidScrollToTop:scrollView];
- }
- }
- ///////////////////////////////////////////////////////////////////////////////////////////
- #pragma mark - Public
- - (void)registerClass:(Class)cellClass forCellWithReuseIdentifier:(NSString *)identifier {
- NSParameterAssert(cellClass);
- NSParameterAssert(identifier);
- _cellClassDict[identifier] = cellClass;
- }
- - (void)registerClass:(Class)viewClass forSupplementaryViewOfKind:(NSString *)elementKind withReuseIdentifier:(NSString *)identifier {
- NSParameterAssert(viewClass);
- NSParameterAssert(elementKind);
- NSParameterAssert(identifier);
- NSString *kindAndIdentifier = [NSString stringWithFormat:@"%@/%@", elementKind, identifier];
- _supplementaryViewClassDict[kindAndIdentifier] = viewClass;
- }
- - (void)registerNib:(UINib *)nib forCellWithReuseIdentifier:(NSString *)identifier {
- NSArray *topLevelObjects = [nib instantiateWithOwner:nil options:nil];
- #pragma unused(topLevelObjects)
- NSAssert(topLevelObjects.count == 1 && [topLevelObjects[0] isKindOfClass:PSTCollectionViewCell.class], @"must contain exactly 1 top level object which is a PSTCollectionViewCell");
- _cellNibDict[identifier] = nib;
- }
- - (void)registerNib:(UINib *)nib forSupplementaryViewOfKind:(NSString *)kind withReuseIdentifier:(NSString *)identifier {
- NSArray *topLevelObjects = [nib instantiateWithOwner:nil options:nil];
- #pragma unused(topLevelObjects)
- NSAssert(topLevelObjects.count == 1 && [topLevelObjects[0] isKindOfClass:PSTCollectionReusableView.class], @"must contain exactly 1 top level object which is a PSTCollectionReusableView");
- NSString *kindAndIdentifier = [NSString stringWithFormat:@"%@/%@", kind, identifier];
- _supplementaryViewNibDict[kindAndIdentifier] = nib;
- }
- - (id)dequeueReusableCellWithReuseIdentifier:(NSString *)identifier forIndexPath:(NSIndexPath *)indexPath {
- // de-queue cell (if available)
- NSMutableArray *reusableCells = _cellReuseQueues[identifier];
- PSTCollectionViewCell *cell = [reusableCells lastObject];
- PSTCollectionViewLayoutAttributes *attributes = [self.collectionViewLayout layoutAttributesForItemAtIndexPath:indexPath];
- if (cell) {
- [reusableCells removeObjectAtIndex:reusableCells.count - 1];
- }else {
- if (_cellNibDict[identifier]) {
- // Cell was registered via registerNib:forCellWithReuseIdentifier:
- UINib *cellNib = _cellNibDict[identifier];
- NSDictionary *externalObjects = self.extVars.nibCellsExternalObjects[identifier];
- if (externalObjects) {
- cell = [cellNib instantiateWithOwner:self options:@{UINibExternalObjects : externalObjects}][0];
- }else {
- cell = [cellNib instantiateWithOwner:self options:nil][0];
- }
- }else {
- Class cellClass = _cellClassDict[identifier];
- // compatibility layer
- Class collectionViewCellClass = NSClassFromString(@"UICollectionViewCell");
- if (collectionViewCellClass && [cellClass isEqual:collectionViewCellClass]) {
- cellClass = PSTCollectionViewCell.class;
- }
- if (cellClass == nil) {
- @throw [NSException exceptionWithName:NSInvalidArgumentException reason:[NSString stringWithFormat:@"Class not registered for identifier %@", identifier] userInfo:nil];
- }
- if (attributes) {
- cell = [[cellClass alloc] initWithFrame:attributes.frame];
- }else {
- cell = [cellClass new];
- }
- }
- cell.collectionView = self;
- cell.reuseIdentifier = identifier;
- }
- [cell applyLayoutAttributes:attributes];
- return cell;
- }
- - (id)dequeueReusableSupplementaryViewOfKind:(NSString *)elementKind withReuseIdentifier:(NSString *)identifier forIndexPath:(NSIndexPath *)indexPath {
- NSString *kindAndIdentifier = [NSString stringWithFormat:@"%@/%@", elementKind, identifier];
- NSMutableArray *reusableViews = _supplementaryViewReuseQueues[kindAndIdentifier];
- PSTCollectionReusableView *view = [reusableViews lastObject];
- if (view) {
- [reusableViews removeObjectAtIndex:reusableViews.count - 1];
- }else {
- if (_supplementaryViewNibDict[kindAndIdentifier]) {
- // supplementary view was registered via registerNib:forCellWithReuseIdentifier:
- UINib *supplementaryViewNib = _supplementaryViewNibDict[kindAndIdentifier];
- NSDictionary *externalObjects = self.extVars.supplementaryViewsExternalObjects[kindAndIdentifier];
- if (externalObjects) {
- view = [supplementaryViewNib instantiateWithOwner:self options:@{UINibExternalObjects : externalObjects}][0];
- }else {
- view = [supplementaryViewNib instantiateWithOwner:self options:nil][0];
- }
- }else {
- Class viewClass = _supplementaryViewClassDict[kindAndIdentifier];
- Class reusableViewClass = NSClassFromString(@"UICollectionReusableView");
- if (reusableViewClass && [viewClass isEqual:reusableViewClass]) {
- viewClass = PSTCollectionReusableView.class;
- }
- if (viewClass == nil) {
- @throw [NSException exceptionWithName:NSInvalidArgumentException reason:[NSString stringWithFormat:@"Class not registered for kind/identifier %@", kindAndIdentifier] userInfo:nil];
- }
- if (self.collectionViewLayout) {
- PSTCollectionViewLayoutAttributes *attributes = [self.collectionViewLayout layoutAttributesForSupplementaryViewOfKind:elementKind atIndexPath:indexPath];
- if (attributes) {
- view = [[viewClass alloc] initWithFrame:attributes.frame];
- }
- }else {
- view = [viewClass new];
- }
- }
- view.collectionView = self;
- view.reuseIdentifier = identifier;
- }
- return view;
- }
- - (id)dequeueReusableOrCreateDecorationViewOfKind:(NSString *)elementKind forIndexPath:(NSIndexPath *)indexPath {
- NSMutableArray *reusableViews = _decorationViewReuseQueues[elementKind];
- PSTCollectionReusableView *view = [reusableViews lastObject];
- PSTCollectionViewLayout *collectionViewLayout = self.collectionViewLayout;
- PSTCollectionViewLayoutAttributes *attributes = [collectionViewLayout layoutAttributesForDecorationViewOfKind:elementKind atIndexPath:indexPath];
- if (view) {
- [reusableViews removeObjectAtIndex:reusableViews.count - 1];
- }else {
- NSDictionary *decorationViewNibDict = collectionViewLayout.decorationViewNibDict;
- NSDictionary *decorationViewExternalObjects = collectionViewLayout.decorationViewExternalObjectsTables;
- if (decorationViewNibDict[elementKind]) {
- // supplementary view was registered via registerNib:forCellWithReuseIdentifier:
- UINib *supplementaryViewNib = decorationViewNibDict[elementKind];
- NSDictionary *externalObjects = decorationViewExternalObjects[elementKind];
- if (externalObjects) {
- view = [supplementaryViewNib instantiateWithOwner:self options:@{UINibExternalObjects : externalObjects}][0];
- }else {
- view = [supplementaryViewNib instantiateWithOwner:self options:nil][0];
- }
- }else {
- NSDictionary *decorationViewClassDict = collectionViewLayout.decorationViewClassDict;
- Class viewClass = decorationViewClassDict[elementKind];
- Class reusableViewClass = NSClassFromString(@"UICollectionReusableView");
- if (reusableViewClass && [viewClass isEqual:reusableViewClass]) {
- viewClass = PSTCollectionReusableView.class;
- }
- if (viewClass == nil) {
- @throw [NSException exceptionWithName:NSInvalidArgumentException reason:[NSString stringWithFormat:@"Class not registered for identifier %@", elementKind] userInfo:nil];
- }
- if (attributes) {
- view = [[viewClass alloc] initWithFrame:attributes.frame];
- }else {
- view = [viewClass new];
- }
- }
- view.collectionView = self;
- view.reuseIdentifier = elementKind;
- }
- [view applyLayoutAttributes:attributes];
- return view;
- }
- - (NSArray *)allCells {
- return [[_allVisibleViewsDict allValues] filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(id evaluatedObject, NSDictionary *bindings) {
- return [evaluatedObject isKindOfClass:PSTCollectionViewCell.class];
- }]];
- }
- - (NSArray *)visibleCells {
- return [[_allVisibleViewsDict allValues] filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(id evaluatedObject, NSDictionary *bindings) {
- return [evaluatedObject isKindOfClass:PSTCollectionViewCell.class] && CGRectIntersectsRect(self.bounds, [evaluatedObject frame]);
- }]];
- }
- - (void)reloadData {
- if (_reloadingSuspendedCount != 0) return;
- [self invalidateLayout];
- [_allVisibleViewsDict enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
- if ([obj isKindOfClass:UIView.class]) {
- [obj removeFromSuperview];
- }
- }];
- [_allVisibleViewsDict removeAllObjects];
- for (NSIndexPath *indexPath in _indexPathsForSelectedItems) {
- PSTCollectionViewCell *selectedCell = [self cellForItemAtIndexPath:indexPath];
- selectedCell.selected = NO;
- selectedCell.highlighted = NO;
- }
- [_indexPathsForSelectedItems removeAllObjects];
- [_indexPathsForHighlightedItems removeAllObjects];
- [self setNeedsLayout];
- }
- ///////////////////////////////////////////////////////////////////////////////////////////
- #pragma mark - Query Grid
- - (NSInteger)numberOfSections {
- return [_collectionViewData numberOfSections];
- }
- - (NSInteger)numberOfItemsInSection:(NSInteger)section {
- return [_collectionViewData numberOfItemsInSection:section];
- }
- - (PSTCollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath {
- return [[self collectionViewLayout] layoutAttributesForItemAtIndexPath:indexPath];
- }
- - (PSTCollectionViewLayoutAttributes *)layoutAttributesForSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath {
- return [[self collectionViewLayout] layoutAttributesForSupplementaryViewOfKind:kind atIndexPath:indexPath];
- }
- - (NSIndexPath *)indexPathForItemAtPoint:(CGPoint)point {
- PSTCollectionViewLayoutAttributes *attributes = [[self.collectionViewLayout layoutAttributesForElementsInRect:CGRectMake(point.x, point.y, 1, 1)] lastObject];
- return attributes.indexPath;
- }
- - (NSIndexPath *)indexPathForCell:(PSTCollectionViewCell *)cell {
- __block NSIndexPath *indexPath = nil;
- [_allVisibleViewsDict enumerateKeysAndObjectsWithOptions:kNilOptions usingBlock:^(id key, id obj, BOOL *stop) {
- PSTCollectionViewItemKey *itemKey = (PSTCollectionViewItemKey *)key;
- if (itemKey.type == PSTCollectionViewItemTypeCell) {
- PSTCollectionViewCell *currentCell = (PSTCollectionViewCell *)obj;
- if (currentCell == cell) {
- indexPath = itemKey.indexPath;
- *stop = YES;
- }
- }
- }];
- return indexPath;
- }
- - (PSTCollectionViewCell *)cellForItemAtIndexPath:(NSIndexPath *)indexPath {
- // NSInteger index = [_collectionViewData globalIndexForItemAtIndexPath:indexPath];
- // TODO Apple uses some kind of globalIndex for this.
- __block PSTCollectionViewCell *cell = nil;
- [_allVisibleViewsDict enumerateKeysAndObjectsWithOptions:0 usingBlock:^(id key, id obj, BOOL *stop) {
- PSTCollectionViewItemKey *itemKey = (PSTCollectionViewItemKey *)key;
- if (itemKey.type == PSTCollectionViewItemTypeCell) {
- if ([itemKey.indexPath isEqual:indexPath]) {
- cell = obj;
- *stop = YES;
- }
- }
- }];
- return cell;
- }
- - (NSArray *)indexPathsForVisibleItems {
- NSArray *visibleCells = self.visibleCells;
- NSMutableArray *indexPaths = [NSMutableArray arrayWithCapacity:visibleCells.count];
- [visibleCells enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
- PSTCollectionViewCell *cell = (PSTCollectionViewCell *)obj;
- [indexPaths addObject:cell.layoutAttributes.indexPath];
- }];
- return indexPaths;
- }
- // returns nil or an array of selected index paths
- - (NSArray *)indexPathsForSelectedItems {
- return [_indexPathsForSelectedItems allObjects];
- }
- // Interacting with the collection view.
- - (void)scrollToItemAtIndexPath:(NSIndexPath *)indexPath atScrollPosition:(PSTCollectionViewScrollPosition)scrollPosition animated:(BOOL)animated {
- // Ensure grid is laid out; else we can't scroll.
- [self layoutSubviews];
- PSTCollectionViewLayoutAttributes *layoutAttributes = [self.collectionViewLayout layoutAttributesForItemAtIndexPath:indexPath];
- if (layoutAttributes) {
- CGRect targetRect = [self makeRect:layoutAttributes.frame toScrollPosition:scrollPosition];
- [self scrollRectToVisible:targetRect animated:animated];
- }
- }
- - (CGRect)makeRect:(CGRect)targetRect toScrollPosition:(PSTCollectionViewScrollPosition)scrollPosition {
- // split parameters
- NSUInteger verticalPosition = scrollPosition&0x07; // 0000 0111
- NSUInteger horizontalPosition = scrollPosition&0x38; // 0011 1000
- if (verticalPosition != PSTCollectionViewScrollPositionNone
- && verticalPosition != PSTCollectionViewScrollPositionTop
- && verticalPosition != PSTCollectionViewScrollPositionCenteredVertically
- && verticalPosition != PSTCollectionViewScrollPositionBottom) {
- @throw [NSException exceptionWithName:NSInvalidArgumentException reason:@"PSTCollectionViewScrollPosition: attempt to use a scroll position with multiple vertical positioning styles" userInfo:nil];
- }
- if (horizontalPosition != PSTCollectionViewScrollPositionNone
- && horizontalPosition != PSTCollectionViewScrollPositionLeft
- && horizontalPosition != PSTCollectionViewScrollPositionCenteredHorizontally
- && horizontalPosition != PSTCollectionViewScrollPositionRight) {
- @throw [NSException exceptionWithName:NSInvalidArgumentException reason:@"PSTCollectionViewScrollPosition: attempt to use a scroll position with multiple horizontal positioning styles" userInfo:nil];
- }
- CGRect frame = self.layer.bounds;
- CGFloat calculateX;
- CGFloat calculateY;
- switch (verticalPosition) {
- case PSTCollectionViewScrollPositionCenteredVertically:
- calculateY = fmax(targetRect.origin.y - ((frame.size.height / 2) - (targetRect.size.height / 2)), -self.contentInset.top);
- targetRect = CGRectMake(targetRect.origin.x, calculateY, targetRect.size.width, frame.size.height);
- break;
- case PSTCollectionViewScrollPositionTop:
- targetRect = CGRectMake(targetRect.origin.x, targetRect.origin.y, targetRect.size.width, frame.size.height);
- break;
- case PSTCollectionViewScrollPositionBottom:
- calculateY = fmax(targetRect.origin.y - (frame.size.height - targetRect.size.height), -self.contentInset.top);
- targetRect = CGRectMake(targetRect.origin.x, calculateY, targetRect.size.width, frame.size.height);
- break;
- }
- switch (horizontalPosition) {
- case PSTCollectionViewScrollPositionCenteredHorizontally:
- calculateX = targetRect.origin.x - ((frame.size.width / 2) - (targetRect.size.width / 2));
- targetRect = CGRectMake(calculateX, targetRect.origin.y, frame.size.width, targetRect.size.height);
- break;
- case PSTCollectionViewScrollPositionLeft:
- targetRect = CGRectMake(targetRect.origin.x, targetRect.origin.y, frame.size.width, targetRect.size.height);
- break;
- case PSTCollectionViewScrollPositionRight:
- calculateX = targetRect.origin.x - (frame.size.width - targetRect.size.width);
- targetRect = CGRectMake(calculateX, targetRect.origin.y, frame.size.width, targetRect.size.height);
- break;
- }
- return targetRect;
- }
- ///////////////////////////////////////////////////////////////////////////////////////////
- #pragma mark - Touch Handling
- - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
- [super touchesBegan:touches withEvent:event];
- // reset touching state vars
- self.extVars.touchingIndexPath = nil;
- self.extVars.currentIndexPath = nil;
- CGPoint touchPoint = [[touches anyObject] locationInView:self];
- NSIndexPath *indexPath = [self indexPathForItemAtPoint:touchPoint];
- if (indexPath && self.allowsSelection) {
- if (![self highlightItemAtIndexPath:indexPath animated:YES scrollPosition:PSTCollectionViewScrollPositionNone notifyDelegate:YES])
- return;
- self.extVars.touchingIndexPath = indexPath;
- self.extVars.currentIndexPath = indexPath;
- if (!self.allowsMultipleSelection) {
- // temporally unhighlight background on touchesBegan (keeps selected by _indexPathsForSelectedItems)
- // single-select only mode only though
- NSIndexPath *tempDeselectIndexPath = _indexPathsForSelectedItems.anyObject;
- if (tempDeselectIndexPath && ![tempDeselectIndexPath isEqual:self.extVars.touchingIndexPath]) {
- // iOS6 UICollectionView deselects cell without notification
- PSTCollectionViewCell *selectedCell = [self cellForItemAtIndexPath:tempDeselectIndexPath];
- selectedCell.selected = NO;
- }
- }
- }
- }
- - (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
- [super touchesMoved:touches withEvent:event];
- // allows moving between highlight and unhighlight state only if setHighlighted is not overwritten
- if (self.extVars.touchingIndexPath) {
- CGPoint touchPoint = [[touches anyObject] locationInView:self];
- NSIndexPath *indexPath = [self indexPathForItemAtPoint:touchPoint];
- // moving out of bounds
- if ([self.extVars.currentIndexPath isEqual:self.extVars.touchingIndexPath] &&
- ![indexPath isEqual:self.extVars.touchingIndexPath] &&
- [self unhighlightItemAtIndexPath:self.extVars.touchingIndexPath animated:YES notifyDelegate:YES shouldCheckHighlight:YES]) {
- self.extVars.currentIndexPath = indexPath;
- // moving back into the original touching cell
- }else if (![self.extVars.currentIndexPath isEqual:self.extVars.touchingIndexPath] &&
- [indexPath isEqual:self.extVars.touchingIndexPath]) {
- [self highlightItemAtIndexPath:self.extVars.touchingIndexPath animated:YES scrollPosition:PSTCollectionViewScrollPositionNone notifyDelegate:YES];
- self.extVars.currentIndexPath = self.extVars.touchingIndexPath;
- }
- }
- }
- - (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
- [super touchesEnded:touches withEvent:event];
- if (self.extVars.touchingIndexPath) {
- // first unhighlight the touch operation
- [self unhighlightItemAtIndexPath:self.extVars.touchingIndexPath animated:YES notifyDelegate:YES];
- CGPoint touchPoint = [[touches anyObject] locationInView:self];
- NSIndexPath *indexPath = [self indexPathForItemAtPoint:touchPoint];
- if ([indexPath isEqual:self.extVars.touchingIndexPath]) {
- [self userSelectedItemAtIndexPath:indexPath];
- }
- else if (!self.allowsMultipleSelection) {
- NSIndexPath *tempDeselectIndexPath = _indexPathsForSelectedItems.anyObject;
- if (tempDeselectIndexPath && ![tempDeselectIndexPath isEqual:self.extVars.touchingIndexPath]) {
- [self cellTouchCancelled];
- }
- }
- // for pedantic reasons only - always set to nil on touchesBegan
- self.extVars.touchingIndexPath = nil;
- self.extVars.currentIndexPath = nil;
- }
- }
- - (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event {
- [super touchesCancelled:touches withEvent:event];
- // do not mark touchingIndexPath as nil because whoever cancelled this touch will need to signal a touch up event later
- if (self.extVars.touchingIndexPath) {
- // first unhighlight the touch operation
- [self unhighlightItemAtIndexPath:self.extVars.touchingIndexPath animated:YES notifyDelegate:YES];
- }
- }
- - (void)cellTouchCancelled {
- // turn on ALL the *should be selected* cells (iOS6 UICollectionView does no state keeping or other fancy optimizations)
- // there should be no notifications as this is a silent "turn everything back on"
- for (NSIndexPath *tempDeselectedIndexPath in [_indexPathsForSelectedItems copy]) {
- PSTCollectionViewCell *selectedCell = [self cellForItemAtIndexPath:tempDeselectedIndexPath];
- selectedCell.selected = YES;
- }
- }
- - (void)userSelectedItemAtIndexPath:(NSIndexPath *)indexPath {
- if (self.allowsMultipleSelection && [_indexPathsForSelectedItems containsObject:indexPath]) {
- [self deselectItemAtIndexPath:indexPath animated:YES notifyDelegate:YES];
- }
- else if (self.allowsSelection) {
- [self selectItemAtIndexPath:indexPath animated:YES scrollPosition:PSTCollectionViewScrollPositionNone notifyDelegate:YES];
- }
- }
- // select item, notify delegate (internal)
- - (void)selectItemAtIndexPath:(NSIndexPath *)indexPath animated:(BOOL)animated scrollPosition:(PSTCollectionViewScrollPosition)scrollPosition notifyDelegate:(BOOL)notifyDelegate {
- if (self.allowsMultipleSelection && [_indexPathsForSelectedItems containsObject:indexPath]) {
- BOOL shouldDeselect = YES;
- if (notifyDelegate && _collectionViewFlags.delegateShouldDeselectItemAtIndexPath) {
- shouldDeselect = [self.delegate collectionView:self shouldDeselectItemAtIndexPath:indexPath];
- }
- if (shouldDeselect) {
- [self deselectItemAtIndexPath:indexPath animated:animated notifyDelegate:notifyDelegate];
- }
- }
- else {
- // either single selection, or wasn't already selected in multiple selection mode
- BOOL shouldSelect = YES;
- if (notifyDelegate && _collectionViewFlags.delegateShouldSelectItemAtIndexPath) {
- shouldSelect = [self.delegate collectionView:self shouldSelectItemAtIndexPath:indexPath];
- }
- if (!self.allowsMultipleSelection) {
- // now unselect the previously selected cell for single selection
- NSIndexPath *tempDeselectIndexPath = _indexPathsForSelectedItems.anyObject;
- if (tempDeselectIndexPath && ![tempDeselectIndexPath isEqual:indexPath]) {
- [self deselectItemAtIndexPath:tempDeselectIndexPath animated:YES notifyDelegate:YES];
- }
- }
- if (shouldSelect) {
- PSTCollectionViewCell *selectedCell = [self cellForItemAtIndexPath:indexPath];
- selectedCell.selected = YES;
- [_indexPathsForSelectedItems addObject:indexPath];
- [selectedCell performSelectionSegue];
- if (scrollPosition != PSTCollectionViewScrollPositionNone) {
- [self scrollToItemAtIndexPath:indexPath atScrollPosition:scrollPosition animated:animated];
- }
- if (notifyDelegate && _collectionViewFlags.delegateDidSelectItemAtIndexPath) {
- [self.delegate collectionView:self didSelectItemAtIndexPath:indexPath];
- }
- }
- }
- }
- - (void)selectItemAtIndexPath:(NSIndexPath *)indexPath animated:(BOOL)animated scrollPosition:(PSTCollectionViewScrollPosition)scrollPosition {
- [self selectItemAtIndexPath:indexPath animated:animated scrollPosition:scrollPosition notifyDelegate:NO];
- }
- - (void)deselectItemAtIndexPath:(NSIndexPath *)indexPath animated:(BOOL)animated {
- [self deselectItemAtIndexPath:indexPath animated:animated notifyDelegate:NO];
- }
- - (void)deselectItemAtIndexPath:(NSIndexPath *)indexPath animated:(BOOL)animated notifyDelegate:(BOOL)notifyDelegate {
- BOOL shouldDeselect = YES;
- // deselect only relevant during multi mode
- if (self.allowsMultipleSelection && notifyDelegate && _collectionViewFlags.delegateShouldDeselectItemAtIndexPath) {
- shouldDeselect = [self.delegate collectionView:self shouldDeselectItemAtIndexPath:indexPath];
- }
- if (shouldDeselect && [_indexPathsForSelectedItems containsObject:indexPath]) {
- PSTCollectionViewCell *selectedCell = [self cellForItemAtIndexPath:indexPath];
- if (selectedCell) {
- if (selectedCell.selected) {
- selectedCell.selected = NO;
- }
- }
- [_indexPathsForSelectedItems removeObject:indexPath];
- if (notifyDelegate && _collectionViewFlags.delegateDidDeselectItemAtIndexPath) {
- [self.delegate collectionView:self didDeselectItemAtIndexPath:indexPath];
- }
- }
- }
- - (BOOL)highlightItemAtIndexPath:(NSIndexPath *)indexPath animated:(BOOL)animated scrollPosition:(PSTCollectionViewScrollPosition)scrollPosition notifyDelegate:(BOOL)notifyDelegate {
- BOOL shouldHighlight = YES;
- if (notifyDelegate && _collectionViewFlags.delegateShouldHighlightItemAtIndexPath) {
- shouldHighlight = [self.delegate collectionView:self shouldHighlightItemAtIndexPath:indexPath];
- }
- if (shouldHighlight) {
- PSTCollectionViewCell *highlightedCell = [self cellForItemAtIndexPath:indexPath];
- highlightedCell.highlighted = YES;
- [_indexPathsForHighlightedItems addObject:indexPath];
- if (scrollPosition != PSTCollectionViewScrollPositionNone) {
- [self scrollToItemAtIndexPath:indexPath atScrollPosition:scrollPosition animated:animated];
- }
- if (notifyDelegate && _collectionViewFlags.delegateDidHighlightItemAtIndexPath) {
- [self.delegate collectionView:self didHighlightItemAtIndexPath:indexPath];
- }
- }
- return shouldHighlight;
- }
- - (BOOL)unhighlightItemAtIndexPath:(NSIndexPath *)indexPath animated:(BOOL)animated notifyDelegate:(BOOL)notifyDelegate {
- return [self unhighlightItemAtIndexPath:indexPath animated:animated notifyDelegate:notifyDelegate shouldCheckHighlight:NO];
- }
- - (BOOL)unhighlightItemAtIndexPath:(NSIndexPath *)indexPath animated:(BOOL)animated notifyDelegate:(BOOL)notifyDelegate shouldCheckHighlight:(BOOL)check {
- if ([_indexPathsForHighlightedItems containsObject:indexPath]) {
- PSTCollectionViewCell *highlightedCell = [self cellForItemAtIndexPath:indexPath];
- // iOS6 does not notify any delegate if the cell was never highlighted (setHighlighted overwritten) during touchMoved
- if (check && !highlightedCell.highlighted) {
- return NO;
- }
- // if multiple selection or not unhighlighting a selected item we don't perform any op
- if (highlightedCell.highlighted && [_indexPathsForSelectedItems containsObject:indexPath]) {
- highlightedCell.highlighted = YES;
- }else {
- highlightedCell.highlighted = NO;
- }
- [_indexPathsForHighlightedItems removeObject:indexPath];
- if (notifyDelegate && _collectionViewFlags.delegateDidUnhighlightItemAtIndexPath) {
- [self.delegate collectionView:self didUnhighlightItemAtIndexPath:indexPath];
- }
- return YES;
- }
- return NO;
- }
- ///////////////////////////////////////////////////////////////////////////////////////////
- #pragma mark - Update Grid
- - (void)insertSections:(NSIndexSet *)sections {
- [self updateSections:sections updateAction:PSTCollectionUpdateActionInsert];
- }
- - (void)deleteSections:(NSIndexSet *)sections {
- // First delete all items
- NSMutableArray *paths = [NSMutableArray new];
- [sections enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL *stop) {
- for (int i = 0; i < [self numberOfItemsInSection:idx]; ++i) {
- [paths addObject:[NSIndexPath indexPathForItem:i inSection:idx]];
- }
- }];
- [self deleteItemsAtIndexPaths:paths];
- // Then delete the section.
- [self updateSections:sections updateAction:PSTCollectionUpdateActionDelete];
- }
- - (void)reloadSections:(NSIndexSet *)sections {
- [self updateSections:sections updateAction:PSTCollectionUpdateActionReload];
- }
- - (void)moveSection:(NSInteger)section toSection:(NSInteger)newSection {
- NSMutableArray *moveUpdateItems = [self arrayForUpdateAction:PSTCollectionUpdateActionMove];
- [moveUpdateItems addObject:
- [[PSTCollectionViewUpdateItem alloc] initWithInitialIndexPath:[NSIndexPath indexPathForItem:NSNotFound inSection:section]
- finalIndexPath:[NSIndexPath indexPathForItem:NSNotFound inSection:newSection]
- updateAction:PSTCollectionUpdateActionMove]];
- if (!_collectionViewFlags.updating) {
- [self setupCellAnimations];
- [self endItemAnimations];
- }
- }
- - (void)insertItemsAtIndexPaths:(NSArray *)indexPaths {
- [self updateRowsAtIndexPaths:indexPaths updateAction:PSTCollectionUpdateActionInsert];
- }
- - (void)deleteItemsAtIndexPaths:(NSArray *)indexPaths {
- [self updateRowsAtIndexPaths:indexPaths updateAction:PSTCollectionUpdateActionDelete];
- }
- - (void)reloadItemsAtIndexPaths:(NSArray *)indexPaths {
- [self updateRowsAtIndexPaths:indexPaths updateAction:PSTCollectionUpdateActionReload];
- }
- - (void)moveItemAtIndexPath:(NSIndexPath *)indexPath toIndexPath:(NSIndexPath *)newIndexPath {
- NSMutableArray *moveUpdateItems = [self arrayForUpdateAction:PSTCollectionUpdateActionMove];
- [moveUpdateItems addObject:
- [[PSTCollectionViewUpdateItem alloc] initWithInitialIndexPath:indexPath
- finalIndexPath:newIndexPath
- updateAction:PSTCollectionUpdateActionMove]];
- if (!_collectionViewFlags.updating) {
- [self setupCellAnimations];
- [self endItemAnimations];
- }
- }
- - (void)performBatchUpdates:(void (^)(void))updates completion:(void (^)(BOOL finished))completion {
- [self setupCellAnimations];
- if (updates) updates();
- if (completion) _updateCompletionHandler = completion;
- [self endItemAnimations];
- }
- ///////////////////////////////////////////////////////////////////////////////////////////
- #pragma mark - Properties
- - (void)setBackgroundView:(UIView *)backgroundView {
- if (backgroundView != _backgroundView) {
- [_backgroundView removeFromSuperview];
- _backgroundView = backgroundView;
- backgroundView.frame = (CGRect){.origin=self.contentOffset, .size=self.bounds.size};
- backgroundView.autoresizingMask = UIViewAutoresizingFlexibleHeight|UIViewAutoresizingFlexibleWidth;
- [self addSubview:backgroundView];
- [self sendSubviewToBack:backgroundView];
- }
- }
- - (void)setCollectionViewLayout:(PSTCollectionViewLayout *)layout animated:(BOOL)animated {
- if (layout == _layout) return;
- // not sure it was it original code, but here this prevents crash
- // in case we switch layout before previous one was initially loaded
- if (CGRectIsEmpty(self.bounds) || !_collectionViewFlags.doneFirstLayout) {
- _layout.collectionView = nil;
- _collectionViewData = [[PSTCollectionViewData alloc] initWithCollectionView:self layout:layout];
- layout.collectionView = self;
- _layout = layout;
- // originally the use method
- // _setNeedsVisibleCellsUpdate:withLayoutAttributes:
- // here with CellsUpdate set to YES and LayoutAttributes parameter set to NO
- // inside this method probably some flags are set and finally
- // setNeedsDisplay is called
- _collectionViewFlags.scheduledUpdateVisibleCells = YES;
- _collectionViewFlags.scheduledUpdateVisibleCellLayoutAttributes = NO;
- [self setNeedsDisplay];
- }
- else {
- layout.collectionView = self;
-
- _layout.collectionView = nil;
- _layout = layout;
- _collectionViewData = [[PSTCollectionViewData alloc] initWithCollectionView:self layout:layout];
- [_collectionViewData prepareToLoadData];
- NSArray *previouslySelectedIndexPaths = [self indexPathsForSelectedItems];
- NSMutableSet *selectedCellKeys = [NSMutableSet setWithCapacity:previouslySelectedIndexPaths.count];
- for (NSIndexPath *indexPath in previouslySelectedIndexPaths) {
- [selectedCellKeys addObject:[PSTCollectionViewItemKey collectionItemKeyForCellWithIndexPath:indexPath]];
- }
- NSArray *previouslyVisibleItemsKeys = [_allVisibleViewsDict allKeys];
- NSSet *previouslyVisibleItemsKeysSet = [NSSet setWithArray:previouslyVisibleItemsKeys];
- NSMutableSet *previouslyVisibleItemsKeysSetMutable = [NSMutableSet setWithArray:previouslyVisibleItemsKeys];
- if ([selectedCellKeys intersectsSet:selectedCellKeys]) {
- [previouslyVisibleItemsKeysSetMutable intersectSet:previouslyVisibleItemsKeysSetMutable];
- }
- [self bringSubviewToFront:_allVisibleViewsDict[[previouslyVisibleItemsKeysSetMutable anyObject]]];
- CGPoint targetOffset = self.contentOffset;
- CGPoint centerPoint = CGPointMake(self.bounds.origin.x + self.bounds.size.width / 2.f,
- self.bounds.origin.y + self.bounds.size.height / 2.f);
- NSIndexPath *centerItemIndexPath = [self indexPathForItemAtPoint:centerPoint];
- if (!centerItemIndexPath) {
- NSArray *visibleItems = [self indexPathsForVisibleItems];
- if (visibleItems.count > 0) {
- centerItemIndexPath = visibleItems[visibleItems.count / 2];
- }
- }
- if (centerItemIndexPath) {
- PSTCo