123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481 |
- //
- // PSTCollectionViewLayout.m
- // PSPDFKit
- //
- // Copyright (c) 2012-2013 Peter Steinberger. All rights reserved.
- //
- #import "PSTCollectionView.h"
- #import "PSTCollectionViewItemKey.h"
- #import "PSTCollectionViewData.h"
- #import <objc/runtime.h>
- @interface PSTCollectionView ()
- - (id)currentUpdate;
- - (NSDictionary *)visibleViewsDict;
- - (PSTCollectionViewData *)collectionViewData;
- - (CGRect)visibleBoundRects; // visibleBounds is flagged as private API (wtf)
- @end
- @interface PSTCollectionReusableView ()
- - (void)setIndexPath:(NSIndexPath *)indexPath;
- @end
- @interface PSTCollectionViewUpdateItem ()
- - (BOOL)isSectionOperation;
- @end
- @interface PSTCollectionViewLayoutAttributes () {
- struct {
- unsigned int isCellKind:1;
- unsigned int isDecorationView:1;
- unsigned int isHidden:1;
- }_layoutFlags;
- char filler[20]; // [HACK] Our class needs to be larger than Apple's class for the superclass change to work.
- }
- @property (nonatomic) PSTCollectionViewItemType elementCategory;
- @property (nonatomic, copy) NSString *elementKind;
- @end
- @interface PSTCollectionViewUpdateItem ()
- - (NSIndexPath *)indexPath;
- @end
- @implementation PSTCollectionViewLayoutAttributes
- ///////////////////////////////////////////////////////////////////////////////////////////
- #pragma mark - Static
- + (instancetype)layoutAttributesForCellWithIndexPath:(NSIndexPath *)indexPath {
- PSTCollectionViewLayoutAttributes *attributes = [self new];
- attributes.elementKind = PSTCollectionElementKindCell;
- attributes.elementCategory = PSTCollectionViewItemTypeCell;
- attributes.indexPath = indexPath;
- return attributes;
- }
- + (instancetype)layoutAttributesForSupplementaryViewOfKind:(NSString *)elementKind withIndexPath:(NSIndexPath *)indexPath {
- PSTCollectionViewLayoutAttributes *attributes = [self new];
- attributes.elementCategory = PSTCollectionViewItemTypeSupplementaryView;
- attributes.elementKind = elementKind;
- attributes.indexPath = indexPath;
- return attributes;
- }
- + (instancetype)layoutAttributesForDecorationViewOfKind:(NSString *)elementKind withIndexPath:(NSIndexPath *)indexPath {
- PSTCollectionViewLayoutAttributes *attributes = [self new];
- attributes.elementCategory = PSTCollectionViewItemTypeDecorationView;
- attributes.elementKind = elementKind;
- attributes.indexPath = indexPath;
- return attributes;
- }
- ///////////////////////////////////////////////////////////////////////////////////////////
- #pragma mark - NSObject
- - (id)init {
- if ((self = [super init])) {
- _alpha = 1.f;
- _transform3D = CATransform3DIdentity;
- }
- return self;
- }
- - (NSUInteger)hash {
- return ([_elementKind hash] * 31) + [_indexPath hash];
- }
- - (BOOL)isEqual:(id)other {
- if ([other isKindOfClass:self.class]) {
- PSTCollectionViewLayoutAttributes *otherLayoutAttributes = (PSTCollectionViewLayoutAttributes *)other;
- if (_elementCategory == otherLayoutAttributes.elementCategory && [_elementKind isEqual:otherLayoutAttributes.elementKind] && [_indexPath isEqual:otherLayoutAttributes.indexPath]) {
- return YES;
- }
- }
- return NO;
- }
- - (NSString *)description {
- return [NSString stringWithFormat:@"<%@: %p frame:%@ indexPath:%@ elementKind:%@>", NSStringFromClass(self.class), self, NSStringFromCGRect(self.frame), self.indexPath, self.elementKind];
- }
- ///////////////////////////////////////////////////////////////////////////////////////////
- #pragma mark - Public
- - (PSTCollectionViewItemType)representedElementCategory {
- return _elementCategory;
- }
- ///////////////////////////////////////////////////////////////////////////////////////////
- #pragma mark - Private
- - (NSString *)representedElementKind {
- return self.elementKind;
- }
- - (BOOL)isDecorationView {
- return self.representedElementCategory == PSTCollectionViewItemTypeDecorationView;
- }
- - (BOOL)isSupplementaryView {
- return self.representedElementCategory == PSTCollectionViewItemTypeSupplementaryView;
- }
- - (BOOL)isCell {
- return self.representedElementCategory == PSTCollectionViewItemTypeCell;
- }
- - (void) updateFrame {
- _frame = (CGRect){{_center.x - _size.width / 2, _center.y - _size.height / 2}, _size};
- }
- - (void)setSize:(CGSize)size {
- _size = size;
- [self updateFrame];
- }
- - (void)setCenter:(CGPoint)center {
- _center = center;
- [self updateFrame];
- }
- - (void)setFrame:(CGRect)frame {
- _frame = frame;
- _size = _frame.size;
- _center = (CGPoint){CGRectGetMidX(_frame), CGRectGetMidY(_frame)};
- }
- ///////////////////////////////////////////////////////////////////////////////////////////
- #pragma mark - NSCopying
- - (id)copyWithZone:(NSZone *)zone {
- PSTCollectionViewLayoutAttributes *layoutAttributes = [self.class new];
- layoutAttributes.indexPath = self.indexPath;
- layoutAttributes.elementKind = self.elementKind;
- layoutAttributes.elementCategory = self.elementCategory;
- layoutAttributes.frame = self.frame;
- layoutAttributes.center = self.center;
- layoutAttributes.size = self.size;
- layoutAttributes.transform3D = self.transform3D;
- layoutAttributes.alpha = self.alpha;
- layoutAttributes.zIndex = self.zIndex;
- layoutAttributes.hidden = self.isHidden;
- return layoutAttributes;
- }
- ///////////////////////////////////////////////////////////////////////////////////////////
- #pragma mark - PSTCollection/UICollection interoperability
- - (NSMethodSignature *)methodSignatureForSelector:(SEL)selector {
- NSMethodSignature *signature = [super methodSignatureForSelector:selector];
- if (!signature) {
- NSString *selString = NSStringFromSelector(selector);
- if ([selString hasPrefix:@"_"]) {
- SEL cleanedSelector = NSSelectorFromString([selString substringFromIndex:1]);
- signature = [super methodSignatureForSelector:cleanedSelector];
- }
- }
- return signature;
- }
- - (void)forwardInvocation:(NSInvocation *)invocation {
- NSString *selString = NSStringFromSelector([invocation selector]);
- if ([selString hasPrefix:@"_"]) {
- SEL cleanedSelector = NSSelectorFromString([selString substringFromIndex:1]);
- if ([self respondsToSelector:cleanedSelector]) {
- invocation.selector = cleanedSelector;
- [invocation invokeWithTarget:self];
- }
- }else {
- [super forwardInvocation:invocation];
- }
- }
- @end
- @interface PSTCollectionViewLayout () {
- __unsafe_unretained PSTCollectionView *_collectionView;
- CGSize _collectionViewBoundsSize;
- NSMutableDictionary *_initialAnimationLayoutAttributesDict;
- NSMutableDictionary *_finalAnimationLayoutAttributesDict;
- NSMutableIndexSet *_deletedSectionsSet;
- NSMutableIndexSet *_insertedSectionsSet;
- NSMutableDictionary *_decorationViewClassDict;
- NSMutableDictionary *_decorationViewNibDict;
- NSMutableDictionary *_decorationViewExternalObjectsTables;
- char filler[200]; // [HACK] Our class needs to be larger than Apple's class for the superclass change to work.
- }
- @property (nonatomic, unsafe_unretained) PSTCollectionView *collectionView;
- @property (nonatomic, copy, readonly) NSDictionary *decorationViewClassDict;
- @property (nonatomic, copy, readonly) NSDictionary *decorationViewNibDict;
- @property (nonatomic, copy, readonly) NSDictionary *decorationViewExternalObjectsTables;
- @end
- @implementation PSTCollectionViewLayout
- ///////////////////////////////////////////////////////////////////////////////////////////
- #pragma mark - NSObject
- - (id)init {
- if ((self = [super init])) {
- _decorationViewClassDict = [NSMutableDictionary new];
- _decorationViewNibDict = [NSMutableDictionary new];
- _decorationViewExternalObjectsTables = [NSMutableDictionary new];
- _initialAnimationLayoutAttributesDict = [NSMutableDictionary new];
- _finalAnimationLayoutAttributesDict = [NSMutableDictionary new];
- _insertedSectionsSet = [NSMutableIndexSet new];
- _deletedSectionsSet = [NSMutableIndexSet new];
- }
- return self;
- }
- - (void)awakeFromNib {
- [super awakeFromNib];
- }
- - (void)setCollectionView:(PSTCollectionView *)collectionView {
- if (collectionView != _collectionView) {
- _collectionView = collectionView;
- }
- }
- ///////////////////////////////////////////////////////////////////////////////////////////
- #pragma mark - Invalidating the Layout
- - (void)invalidateLayout {
- [[_collectionView collectionViewData] invalidate];
- [_collectionView setNeedsLayout];
- }
- - (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds {
- // not sure about his..
- if ((self.collectionView.bounds.size.width != newBounds.size.width) || (self.collectionView.bounds.size.height != newBounds.size.height)) {
- return YES;
- }
- return NO;
- }
- ///////////////////////////////////////////////////////////////////////////////////////////
- #pragma mark - Providing Layout Attributes
- + (Class)layoutAttributesClass {
- return PSTCollectionViewLayoutAttributes.class;
- }
- - (void)prepareLayout {
- }
- - (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect {
- return nil;
- }
- - (PSTCollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath {
- return nil;
- }
- - (PSTCollectionViewLayoutAttributes *)layoutAttributesForSupplementaryViewOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath {
- return nil;
- }
- - (PSTCollectionViewLayoutAttributes *)layoutAttributesForDecorationViewOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath {
- return nil;
- }
- // return a point at which to rest after scrolling - for layouts that want snap-to-point scrolling behavior
- - (CGPoint)targetContentOffsetForProposedContentOffset:(CGPoint)proposedContentOffset withScrollingVelocity:(CGPoint)velocity {
- return proposedContentOffset;
- }
- - (CGSize)collectionViewContentSize {
- return CGSizeZero;
- }
- ///////////////////////////////////////////////////////////////////////////////////////////
- #pragma mark - Responding to Collection View Updates
- - (void)prepareForCollectionViewUpdates:(NSArray *)updateItems {
- NSDictionary *update = [_collectionView currentUpdate];
- for (PSTCollectionReusableView *view in [[_collectionView visibleViewsDict] objectEnumerator]) {
- PSTCollectionViewLayoutAttributes *attr = [view.layoutAttributes copy];
- if (attr) {
- if (attr.isCell) {
- NSUInteger index = [update[@"oldModel"] globalIndexForItemAtIndexPath:[attr indexPath]];
- if (index != NSNotFound) {
- [attr setIndexPath:[attr indexPath]];
- }
- }
- _initialAnimationLayoutAttributesDict[[PSTCollectionViewItemKey collectionItemKeyForLayoutAttributes:attr]] = attr;
- }
- }
- PSTCollectionViewData *collectionViewData = [_collectionView collectionViewData];
- CGRect bounds = [_collectionView visibleBoundRects];
- for (PSTCollectionViewLayoutAttributes *attr in [collectionViewData layoutAttributesForElementsInRect:bounds]) {
- if (attr.isCell) {
- NSInteger index = (NSInteger)[collectionViewData globalIndexForItemAtIndexPath:attr.indexPath];
- index = [update[@"newToOldIndexMap"][(NSUInteger)index] integerValue];
- if (index != NSNotFound) {
- PSTCollectionViewLayoutAttributes *finalAttrs = [attr copy];
- [finalAttrs setIndexPath:[update[@"oldModel"] indexPathForItemAtGlobalIndex:index]];
- [finalAttrs setAlpha:0];
- _finalAnimationLayoutAttributesDict[[PSTCollectionViewItemKey collectionItemKeyForLayoutAttributes:finalAttrs]] = finalAttrs;
- }
- }
- }
- for (PSTCollectionViewUpdateItem *updateItem in updateItems) {
- PSTCollectionUpdateAction action = updateItem.updateAction;
- if ([updateItem isSectionOperation]) {
- if (action == PSTCollectionUpdateActionReload) {
- [_deletedSectionsSet addIndex:(NSUInteger)[[updateItem indexPathBeforeUpdate] section]];
- [_insertedSectionsSet addIndex:(NSUInteger)[updateItem indexPathAfterUpdate].section];
- }
- else {
- NSMutableIndexSet *indexSet = action == PSTCollectionUpdateActionInsert ? _insertedSectionsSet : _deletedSectionsSet;
- [indexSet addIndex:(NSUInteger)[updateItem indexPath].section];
- }
- }
- else {
- if (action == PSTCollectionUpdateActionDelete) {
- PSTCollectionViewItemKey *key = [PSTCollectionViewItemKey collectionItemKeyForCellWithIndexPath:
- [updateItem indexPathBeforeUpdate]];
- PSTCollectionViewLayoutAttributes *attrs = [_finalAnimationLayoutAttributesDict[key] copy];
- if (attrs) {
- [attrs setAlpha:0];
- _finalAnimationLayoutAttributesDict[key] = attrs;
- }
- }
- else if (action == PSTCollectionUpdateActionReload || action == PSTCollectionUpdateActionInsert) {
- PSTCollectionViewItemKey *key = [PSTCollectionViewItemKey collectionItemKeyForCellWithIndexPath:
- [updateItem indexPathAfterUpdate]];
- PSTCollectionViewLayoutAttributes *attrs = [_initialAnimationLayoutAttributesDict[key] copy];
- if (attrs) {
- [attrs setAlpha:0];
- _initialAnimationLayoutAttributesDict[key] = attrs;
- }
- }
- }
- }
- }
- - (PSTCollectionViewLayoutAttributes *)initialLayoutAttributesForAppearingItemAtIndexPath:(NSIndexPath *)itemIndexPath {
- PSTCollectionViewLayoutAttributes *attrs = _initialAnimationLayoutAttributesDict[[PSTCollectionViewItemKey collectionItemKeyForCellWithIndexPath:itemIndexPath]];
- if ([_insertedSectionsSet containsIndex:(NSUInteger)[itemIndexPath section]]) {
- attrs = [attrs copy];
- [attrs setAlpha:0];
- }
- return attrs;
- }
- - (PSTCollectionViewLayoutAttributes *)finalLayoutAttributesForDisappearingItemAtIndexPath:(NSIndexPath *)itemIndexPath {
- PSTCollectionViewLayoutAttributes *attrs = _finalAnimationLayoutAttributesDict[[PSTCollectionViewItemKey collectionItemKeyForCellWithIndexPath:itemIndexPath]];
- if ([_deletedSectionsSet containsIndex:(NSUInteger)[itemIndexPath section]]) {
- attrs = [attrs copy];
- [attrs setAlpha:0];
- }
- return attrs;
- }
- - (PSTCollectionViewLayoutAttributes *)initialLayoutAttributesForInsertedSupplementaryElementOfKind:(NSString *)elementKind atIndexPath:(NSIndexPath *)elementIndexPath {
- PSTCollectionViewLayoutAttributes *attrs = _initialAnimationLayoutAttributesDict[[PSTCollectionViewItemKey collectionItemKeyForCellWithIndexPath:elementIndexPath]];
- if ([_insertedSectionsSet containsIndex:(NSUInteger)[elementIndexPath section]]) {
- attrs = [attrs copy];
- [attrs setAlpha:0];
- }
- return attrs;
- }
- - (PSTCollectionViewLayoutAttributes *)finalLayoutAttributesForDeletedSupplementaryElementOfKind:(NSString *)elementKind atIndexPath:(NSIndexPath *)elementIndexPath {
- return nil;
- }
- - (void)finalizeCollectionViewUpdates {
- [_initialAnimationLayoutAttributesDict removeAllObjects];
- [_finalAnimationLayoutAttributesDict removeAllObjects];
- [_deletedSectionsSet removeAllIndexes];
- [_insertedSectionsSet removeAllIndexes];
- }
- ///////////////////////////////////////////////////////////////////////////////////////////
- #pragma mark - Registering Decoration Views
- - (void)registerClass:(Class)viewClass forDecorationViewOfKind:(NSString *)kind {
- _decorationViewClassDict[kind] = viewClass;
- }
- - (void)registerNib:(UINib *)nib forDecorationViewOfKind:(NSString *)kind {
- _decorationViewNibDict[kind] = nib;
- }
- ///////////////////////////////////////////////////////////////////////////////////////////
- #pragma mark - Private
- - (void)setCollectionViewBoundsSize:(CGSize)size {
- _collectionViewBoundsSize = size;
- }
- ///////////////////////////////////////////////////////////////////////////////////////////
- #pragma mark - NSCoding
- - (id)initWithCoder:(NSCoder *)coder {
- if ((self = [self init])) {
- }
- return self;
- }
- - (void)encodeWithCoder:(NSCoder *)coder {}
- ///////////////////////////////////////////////////////////////////////////////////////////
- #pragma mark - PSTCollection/UICollection interoperability
- #ifdef kPSUIInteroperabilityEnabled
- #import <objc/runtime.h>
- #import <objc/message.h>
- - (NSMethodSignature *)methodSignatureForSelector:(SEL)selector {
- NSMethodSignature *sig = [super methodSignatureForSelector:selector];
- if(!sig) {
- NSString *selString = NSStringFromSelector(selector);
- if ([selString hasPrefix:@"_"]) {
- SEL cleanedSelector = NSSelectorFromString([selString substringFromIndex:1]);
- sig = [super methodSignatureForSelector:cleanedSelector];
- }
- }
- return sig;
- }
- - (void)forwardInvocation:(NSInvocation *)inv {
- NSString *selString = NSStringFromSelector([inv selector]);
- if ([selString hasPrefix:@"_"]) {
- SEL cleanedSelector = NSSelectorFromString([selString substringFromIndex:1]);
- if ([self respondsToSelector:cleanedSelector]) {
- // dynamically add method for faster resolving
- Method newMethod = class_getInstanceMethod(self.class, [inv selector]);
- IMP underscoreIMP = imp_implementationWithBlock(^(id _self) {
- return objc_msgSend(_self, cleanedSelector);
- });
- class_addMethod(self.class, [inv selector], underscoreIMP, method_getTypeEncoding(newMethod));
- // invoke now
- inv.selector = cleanedSelector;
- [inv invokeWithTarget:self];
- }
- }else {
- [super forwardInvocation:inv];
- }
- }
- #endif
- @end
|