#import "TGCollectionMenuController.h" #import "TGInterfaceAssets.h" #import "TGImageUtils.h" #import "TGCollectionMenuView.h" #import "TGCollectionMenuLayout.h" @interface TGCollectionMenuSection () - (void)insertItem:(TGCollectionItem *)item atIndex:(NSUInteger)index; - (void)deleteItemAtIndex:(NSUInteger)index; - (void)replaceItemAtIndex:(NSUInteger)index withItem:(TGCollectionItem *)item; @end @interface TGCollectionMenuController () { bool _editingMode; NSMutableSet *_collectionRegisteredIdentifiers; CGFloat _currentLayoutWidth; UIView *_headerBackgroundView; } @end @implementation TGCollectionMenuController - (id)init { self = [super init]; if (self) { _menuSections = [[TGCollectionMenuSectionList alloc] init]; } return self; } - (void)dealloc { _collectionView.delegate = nil; _collectionView.dataSource = nil; } - (void)enterEditingMode:(bool)animated { if (!_editingMode) { _editingMode = true; [_collectionView setEditing:true animated:animated]; [self didEnterEditingMode:animated]; } } - (void)leaveEditingMode:(bool)animated { if (_editingMode) { _editingMode = false; [_collectionView setEditing:false animated:animated]; [self didLeaveEditingMode:animated]; } } - (void)didEnterEditingMode:(bool)__unused animated { } - (void)didLeaveEditingMode:(bool)__unused animated { } - (void)_resetCollectionView { if (_collectionView != nil) { _collectionView.delegate = nil; _collectionView.dataSource = nil; [_collectionView removeFromSuperview]; } _currentLayoutWidth = self.view.frame.size.width; _collectionRegisteredIdentifiers = [[NSMutableSet alloc] init]; _collectionLayout = [[TGCollectionMenuLayout alloc] init]; _collectionView = [[TGCollectionMenuView alloc] initWithFrame:self.view.bounds collectionViewLayout:_collectionLayout]; _collectionView.backgroundColor = nil; _collectionView.opaque = false; _collectionView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; _collectionView.dataSource = self; _collectionView.delegate = self; _collectionView.alwaysBounceVertical = true; [_collectionView registerClass:[TGCollectionItemView class] forCellWithReuseIdentifier:@"_empty"]; __weak TGCollectionMenuController *weakSelf = self; _collectionView.layoutForSize = ^(CGSize size) { __strong TGCollectionMenuController *strongSelf = weakSelf; if (strongSelf != nil) { strongSelf->_currentLayoutWidth = size.width; [strongSelf->_collectionLayout invalidateLayout]; [strongSelf->_collectionView setNeedsLayout]; [strongSelf->_collectionView layoutSubviews]; } }; [self.view insertSubview:_collectionView aboveSubview:_headerBackgroundView]; } - (void)collectionMenuViewDidEnterEditingMode:(TGCollectionMenuView *)__unused collectionMenuView { if (!_editingMode) { _editingMode = true; [self didEnterEditingMode:true]; } } - (void)collectionMenuViewDidLeaveEditingMode:(TGCollectionMenuView *)__unused collectionMenuView { if (_editingMode) { _editingMode = false; [self didLeaveEditingMode:true]; } } - (NSIndexPath *)indexPathForItem:(TGCollectionItem *)item { int sectionIndex = -1; for (TGCollectionMenuSection *section in _menuSections.sections) { sectionIndex++; int itemIndex = -1; for (TGCollectionItem *listItem in section.items) { itemIndex++; if (listItem == item) { return [NSIndexPath indexPathForItem:itemIndex inSection:sectionIndex]; } } } return nil; } - (NSUInteger)indexForSection:(TGCollectionMenuSection *)section { int index = -1; for (TGCollectionMenuSection *listSection in _menuSections.sections) { index++; if (listSection == section) return (NSUInteger)index; } return NSNotFound; } - (void)loadView { [super loadView]; if (TGIsPad()) { self.view.frame = self.navigationController.view.bounds; } self.view.backgroundColor = [TGInterfaceAssets listsBackgroundColor]; _headerBackgroundView = [[UIView alloc] initWithFrame:CGRectMake(0.0f, 0.0f, self.view.frame.size.width, self.controllerInset.top)]; _headerBackgroundView.backgroundColor = [UIColor whiteColor]; [self.view addSubview:_headerBackgroundView]; [self _resetCollectionView]; [self setExplicitTableInset:UIEdgeInsetsMake(-(TGIsRetina() ? 0.5f : 1.0f), 0, 0, 0)]; if (![self _updateControllerInset:false]) [self controllerInsetUpdated:UIEdgeInsetsZero]; } #pragma mark - - (void)controllerInsetUpdated:(UIEdgeInsets)previousInset { [super controllerInsetUpdated:previousInset]; if ([self isViewLoaded]) _headerBackgroundView.frame = CGRectMake(0.0f, 0.0f, self.view.frame.size.width, self.controllerInset.top); } - (void)viewWillAppear:(BOOL)animated { for (NSIndexPath *indexPath in [_collectionView indexPathsForSelectedItems]) { [_collectionView deselectItemAtIndexPath:indexPath animated:animated]; } [super viewWillAppear:animated]; } /*- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration { CGFloat currentLayoutWidth = [self inPopover] ? self.view.frame.size.width : [TGViewController screenSizeForInterfaceOrientation:toInterfaceOrientation].width; _currentLayoutWidth = currentLayoutWidth; [_collectionLayout invalidateLayout]; [super willRotateToInterfaceOrientation:toInterfaceOrientation duration:duration]; } - (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration { [super willAnimateRotationToInterfaceOrientation:toInterfaceOrientation duration:duration]; }*/ - (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView { if (collectionView == _collectionView) return _menuSections.sections.count; return 0; } - (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section { if (collectionView == _collectionView && section < (NSInteger)_menuSections.sections.count) return ((TGCollectionMenuSection *)_menuSections.sections[section]).items.count; return 0; } - (void)updateItem:(TGCollectionItem *)item itemView:(TGCollectionItemView *)itemView positionAtIndexPath:(NSIndexPath *)indexPath ignoreDragging:(bool)ignoreDragging animated:(bool)animated { TGCollectionItem *previousItem = nil; if (indexPath.item > 0) { if (ignoreDragging || _collectionLayout.selectedItemIndexPath == nil || ![_collectionLayout.selectedItemIndexPath isEqual:[NSIndexPath indexPathForItem:indexPath.item - 1 inSection:indexPath.section]]) { previousItem = ((TGCollectionMenuSection *)_menuSections.sections[indexPath.section]).items[indexPath.item - 1]; } } TGCollectionItem *nextItem = nil; if (indexPath.item < (NSInteger)((TGCollectionMenuSection *)_menuSections.sections[indexPath.section]).items.count - 1) { if (ignoreDragging || _collectionLayout.selectedItemIndexPath == nil || ![_collectionLayout.selectedItemIndexPath isEqual:[NSIndexPath indexPathForItem:indexPath.item + 1 inSection:indexPath.section]]) { nextItem = ((TGCollectionMenuSection *)_menuSections.sections[indexPath.section]).items[indexPath.item + 1]; } } int itemPositionMask = 0; if (!item.transparent) { if (previousItem == nil || previousItem.transparent) itemPositionMask |= TGCollectionItemViewPositionFirstInBlock; if (nextItem == nil || nextItem.transparent) itemPositionMask |= TGCollectionItemViewPositionLastInBlock; if (itemPositionMask == 0) itemPositionMask = TGCollectionItemViewPositionMiddleInBlock; } [itemView setItemPosition:itemPositionMask animated:animated]; } - (UICollectionViewCell *)collectionView:(TGCollectionMenuView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath { TGCollectionItem *item = indexPath.section < (NSInteger)_menuSections.sections.count && indexPath.row < (NSInteger)((TGCollectionMenuSection *)_menuSections.sections[indexPath.section]).items.count ? ((TGCollectionMenuSection *)_menuSections.sections[indexPath.section]).items[indexPath.item] : nil; if (item != nil) { TGCollectionItemView *itemView = [item dequeueItemView:collectionView registeredIdentifiers:_collectionRegisteredIdentifiers forIndexPath:indexPath]; if (itemView.boundItem != nil) [itemView.boundItem unbindView]; [self updateItem:item itemView:itemView positionAtIndexPath:indexPath ignoreDragging:false animated:false]; [item bindView:itemView]; [collectionView setupCellForEditing:itemView]; return itemView; } return [collectionView dequeueReusableCellWithReuseIdentifier:@"_empty" forIndexPath:indexPath]; } - (void)collectionView:(UICollectionView *)__unused collectionView didEndDisplayingCell:(TGCollectionItemView *)cell forItemAtIndexPath:(NSIndexPath *)__unused indexPath { [cell.boundItem unbindView]; } - (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)__unused collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath { TGCollectionItem *item = indexPath.section < (NSInteger)_menuSections.sections.count && indexPath.row < (NSInteger)((TGCollectionMenuSection *)_menuSections.sections[indexPath.section]).items.count ? ((TGCollectionMenuSection *)_menuSections.sections[indexPath.section]).items[indexPath.item] : nil; CGSize layoutSize = collectionView.frame.size; if (item != nil) return [item itemSizeForContainerSize:layoutSize]; return CGSizeZero; } - (CGFloat)collectionView:(UICollectionView *)__unused collectionView layout:(UICollectionViewLayout *)__unused collectionViewLayout minimumLineSpacingForSectionAtIndex:(NSInteger)__unused section { return 0.0f; } - (UIEdgeInsets)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)__unused collectionViewLayout insetForSectionAtIndex:(NSInteger)section { if (collectionView == _collectionView) { return ((TGCollectionMenuSection *)_menuSections.sections[section]).insets; } return UIEdgeInsetsZero; } - (BOOL)collectionView:(UICollectionView *)collectionView shouldHighlightItemAtIndexPath:(NSIndexPath *)indexPath { if (collectionView == _collectionView) { TGCollectionItem *item = indexPath.section < (NSInteger)_menuSections.sections.count && indexPath.row < (NSInteger)((TGCollectionMenuSection *)_menuSections.sections[indexPath.section]).items.count ? ((TGCollectionMenuSection *)_menuSections.sections[indexPath.section]).items[indexPath.item] : nil; if (item != nil) return item.highlightable; } return false; } - (BOOL)collectionView:(UICollectionView *)collectionView shouldSelectItemAtIndexPath:(NSIndexPath *)indexPath { if (collectionView == _collectionView) { TGCollectionItem *item = indexPath.section < (NSInteger)_menuSections.sections.count && indexPath.row < (NSInteger)((TGCollectionMenuSection *)_menuSections.sections[indexPath.section]).items.count ? ((TGCollectionMenuSection *)_menuSections.sections[indexPath.section]).items[indexPath.item] : nil; if (item != nil) return item.selectable; } return false; } - (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath { if (collectionView == _collectionView) { TGCollectionItem *item = indexPath.section < (NSInteger)_menuSections.sections.count && indexPath.row < (NSInteger)((TGCollectionMenuSection *)_menuSections.sections[indexPath.section]).items.count ? ((TGCollectionMenuSection *)_menuSections.sections[indexPath.section]).items[indexPath.item] : nil; if (item != nil) { [item itemSelected:self]; if (item.deselectAutomatically) [collectionView deselectItemAtIndexPath:indexPath animated:true]; } } } - (BOOL)collectionView:(UICollectionView *)collectionView shouldShowMenuForItemAtIndexPath:(NSIndexPath *)indexPath { if (collectionView == _collectionView) { TGCollectionItem *item = indexPath.section < (NSInteger)_menuSections.sections.count && indexPath.row < (NSInteger)((TGCollectionMenuSection *)_menuSections.sections[indexPath.section]).items.count ? ((TGCollectionMenuSection *)_menuSections.sections[indexPath.section]).items[indexPath.item] : nil; if (item != nil) { return [item itemWantsMenu]; } } return false; } - (BOOL)collectionView:(UICollectionView *)collectionView canPerformAction:(SEL)action forItemAtIndexPath:(NSIndexPath *)indexPath withSender:(id)__unused sender { if (collectionView == _collectionView) { TGCollectionItem *item = indexPath.section < (NSInteger)_menuSections.sections.count && indexPath.row < (NSInteger)((TGCollectionMenuSection *)_menuSections.sections[indexPath.section]).items.count ? ((TGCollectionMenuSection *)_menuSections.sections[indexPath.section]).items[indexPath.item] : nil; if (item != nil) { return [item itemCanPerformAction:action]; } } return false; } - (void)collectionView:(UICollectionView *)collectionView performAction:(SEL)action forItemAtIndexPath:(NSIndexPath *)indexPath withSender:(id)__unused sender { if (collectionView == _collectionView) { TGCollectionItem *item = indexPath.section < (NSInteger)_menuSections.sections.count && indexPath.row < (NSInteger)((TGCollectionMenuSection *)_menuSections.sections[indexPath.section]).items.count ? ((TGCollectionMenuSection *)_menuSections.sections[indexPath.section]).items[indexPath.item] : nil; if (item != nil) { return [item itemPerformAction:action]; } } } - (void)collectionView:(UICollectionView *)__unused collectionView itemAtIndexPath:(NSIndexPath *)fromIndexPath willMoveToIndexPath:(NSIndexPath *)toIndexPath { TGCollectionMenuSection *fromSection = _menuSections.sections[fromIndexPath.section]; TGCollectionMenuSection *toSection = _menuSections.sections[toIndexPath.section]; if (fromSection == toSection) { NSInteger fromIndex = fromIndexPath.item; id item = fromSection.items[fromIndex]; NSInteger toIndex = toIndexPath.item; [fromSection deleteItemAtIndex:fromIndex]; [fromSection insertItem:item atIndex:toIndex]; } else { id item = fromSection.items[fromIndexPath.item]; [fromSection deleteItemAtIndex:fromIndexPath.item]; [toSection insertItem:item atIndex:toIndexPath.item]; } } - (void)animateCollectionCrossfade { UIView *snapshotView = [self.collectionView snapshotViewAfterScreenUpdates:false]; [self.view insertSubview:snapshotView aboveSubview:self.collectionView]; [UIView animateWithDuration:0.3 animations:^ { snapshotView.alpha = 0.0f; } completion:^(__unused BOOL finished) { [snapshotView removeFromSuperview]; }]; } - (void)updateItemPositions { [self updateItemPositions:false]; } - (void)updateItemPositions:(bool)ignoreDragging { for (NSIndexPath *indexPath in [_collectionView indexPathsForVisibleItems]) { TGCollectionMenuSection *section = _menuSections.sections[indexPath.section]; TGCollectionItem *item = section.items[indexPath.item]; TGCollectionItemView *itemView = (TGCollectionItemView *)[_collectionView cellForItemAtIndexPath:indexPath]; [self updateItem:item itemView:itemView positionAtIndexPath:indexPath ignoreDragging:ignoreDragging animated:true]; } } - (void)collectionView:(UICollectionView *)__unused collectionView itemAtIndexPath:(NSIndexPath *)__unused fromIndexPath willBeginMovingToIndexPath:(NSIndexPath *)__unused toIndexPath { [self updateItemPositions:false]; } - (void)collectionView:(UICollectionView *)__unused collectionView layout:(UICollectionViewLayout *)__unused collectionViewLayout willEndDraggingItemAtIndexPath:(NSIndexPath *)__unused indexPath { [self updateItemPositions:true]; } - (void)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)__unused collectionViewLayout willBeginDraggingItemAtIndexPath:(NSIndexPath *)indexPath { TGCollectionItemView *itemView = (TGCollectionItemView *)[collectionView cellForItemAtIndexPath:indexPath]; [itemView setItemPosition:TGCollectionItemViewPositionFirstInBlock | TGCollectionItemViewPositionLastInBlock]; } - (void)collectionView:(UICollectionView *)__unused collectionView layout:(UICollectionViewLayout *)__unused collectionViewLayout didBeginDraggingItemAtIndexPath:(NSIndexPath *)__unused indexPath { [self updateItemPositions:false]; } - (void)collectionView:(UICollectionView *)__unused collectionView itemAtIndexPath:(NSIndexPath *)__unused fromIndexPath didMoveToIndexPath:(NSIndexPath *)__unused toIndexPath { } - (BOOL)collectionView:(UICollectionView *)__unused collectionView canMoveItemAtIndexPath:(NSIndexPath *)indexPath { TGCollectionMenuSection *section = _menuSections.sections[indexPath.section]; TGCollectionItem *item = section.items[indexPath.item]; return _editingMode && item.canBeMovedToSectionAtIndex != nil; } - (BOOL)collectionView:(UICollectionView *)__unused collectionView itemAtIndexPath:(NSIndexPath *) fromIndexPath canMoveToIndexPath:(NSIndexPath *)toIndexPath { TGCollectionMenuSection *section = _menuSections.sections[fromIndexPath.section]; TGCollectionItem *item = section.items[fromIndexPath.item]; return item.canBeMovedToSectionAtIndex != nil && item.canBeMovedToSectionAtIndex(toIndexPath.section, toIndexPath.item); } - (BOOL)collectionViewCanMoveItems:(UICollectionView *)__unused collectionView { return _enableItemReorderingGestures; } @end