Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Fetching contributors…

Cannot retrieve contributors at this time

484 lines (397 sloc) 17.872 kb
//
// PSTCollectionViewLayout.m
// PSPDFKit
//
// Copyright (c) 2012-2013 Peter Steinberger. All rights reserved.
//
#import "PSTCollectionView.h"
#import "PSTCollectionViewLayout.h"
#import "PSTCollectionViewItemKey.h"
#import "PSTCollectionViewData.h"
#import "PSTCollectionViewUpdateItem.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;
}
@property (nonatomic, copy) NSString *elementKind;
@property (nonatomic, copy) NSString *reuseIdentifier;
@end
@interface PSTCollectionViewUpdateItem()
-(NSIndexPath*) indexPath;
@end
@implementation PSTCollectionViewLayoutAttributes
///////////////////////////////////////////////////////////////////////////////////////////
#pragma mark - Static
+ (instancetype)layoutAttributesForCellWithIndexPath:(NSIndexPath *)indexPath {
PSTCollectionViewLayoutAttributes *attributes = [self new];
attributes.elementKind = PSTCollectionElementKindCell;
attributes.indexPath = indexPath;
return attributes;
}
+ (instancetype)layoutAttributesForSupplementaryViewOfKind:(NSString *)elementKind withIndexPath:(NSIndexPath *)indexPath {
PSTCollectionViewLayoutAttributes *attributes = [self new];
attributes.elementKind = elementKind;
attributes.indexPath = indexPath;
return attributes;
}
+ (instancetype)layoutAttributesForDecorationViewOfKind:(NSString *)kind withIndexPath:(NSIndexPath *)indexPath {
PSTCollectionViewLayoutAttributes *attributes = [self new];
attributes.elementKind = PSTCollectionElementKindDecorationView;
attributes.reuseIdentifier = kind;
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 ([_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 {
if ([self.elementKind isEqualToString:PSTCollectionElementKindCell]) {
return PSTCollectionViewItemTypeCell;
}else if([self.elementKind isEqualToString:PSTCollectionElementKindDecorationView]) {
return PSTCollectionViewItemTypeDecorationView;
}else {
return PSTCollectionViewItemTypeSupplementaryView;
}
}
///////////////////////////////////////////////////////////////////////////////////////////
#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)setSize:(CGSize)size {
_size = size;
_frame = (CGRect){_frame.origin, _size};
}
- (void)setCenter:(CGPoint)center {
_center = center;
_frame = (CGRect){{_center.x - _frame.size.width / 2, _center.y - _frame.size.height / 2}, _frame.size};
}
- (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.reuseIdentifier = self.reuseIdentifier;
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
#import <objc/runtime.h>
- (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;
}
@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.isCell) {
NSInteger index = [update[@"oldModel"] globalIndexForItemAtIndexPath:[attr indexPath]];
if(index != NSNotFound) {
index = [update[@"oldToNewIndexMap"][index] intValue];
if(index != NSNotFound) {
[attr setIndexPath:[update[@"newModel"] indexPathForItemAtGlobalIndex:index]];
}
}
}
_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 = [collectionViewData globalIndexForItemAtIndexPath:attr.indexPath];
index = [update[@"newToOldIndexMap"][index] intValue];
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:[[updateItem indexPathBeforeUpdate] section]];
[_insertedSectionsSet addIndex:[updateItem indexPathAfterUpdate].section];
}
else {
NSMutableIndexSet *indexSet = action == PSTCollectionUpdateActionInsert ? _insertedSectionsSet : _deletedSectionsSet;
[indexSet addIndex:[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:[itemIndexPath section]]) {
attrs = [attrs copy];
[attrs setAlpha:0];
}
return attrs;
}
- (PSTCollectionViewLayoutAttributes *)finalLayoutAttributesForDisappearingItemAtIndexPath:(NSIndexPath *)itemIndexPath {
PSTCollectionViewLayoutAttributes* attrs = _finalAnimationLayoutAttributesDict[[PSTCollectionViewItemKey collectionItemKeyForCellWithIndexPath:itemIndexPath]];
if([_deletedSectionsSet containsIndex:[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:[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 setObject:viewClass forKey:kind];
}
- (void)registerNib:(UINib *)nib forDecorationViewOfKind:(NSString *)kind {
[_decorationViewNibDict setObject:nib forKey:kind];
}
///////////////////////////////////////////////////////////////////////////////////////////
#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(PSBlockImplCast(^(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
Jump to Line
Something went wrong with that request. Please try again.