14

我在UICollectionView中有一个基本网格。这是一个简单的2列,使用UICollectionViewDelegateFlowLayout的多行布局。选择单元格时,我想调暗背景,将单元格浮动到屏幕中央,然后根据所选单元格创建工作流程。我对UICollectionViews相当陌生,我不确定最好的解决方法。将动画UICollectionView单元格选中

当我选择一个单元格时,我应该有一个UICollectionView的自定义布局吗?

还是有办法,我可以动画显示选中的单元格,而无需创建新的布局

如果任何人都可以只让我去上了正确的方向,我想我会是很好的研究如何执行它。

回答

12

在尝试了几个(而且很冒险的)解决方案后,我通过编写我自己的UICollectionView布局来解决这个问题。以前的解决方案我尝试:

  • 添加自定义的UIView上层建筑的UICollectionView并试图伪造原始细胞通过覆盖它,调光背景,动画,新的UIView
  • 移动动画细胞,而无需创建一个全新的布局

我试图避免它,但是在编码之后,它比我原先想象的要少得多。

这里是我的代码,有兴趣的人:

.H:

@interface FMStackedLayout : UICollectionViewLayout 

@property(nonatomic,assign) CGPoint  center; 
@property(nonatomic,assign) NSInteger cellCount; 

-(id)initWithSelectedCellIndexPath:(NSIndexPath *)indexPath; 

@end 

.M:

#define ITEM_WIDTH 128.0f 
#define ITEM_HEIGHT 180.0f 

static NSUInteger  const RotationCount   = 32; 
static NSUInteger  const RotationStride  = 3; 
static NSUInteger  const PhotoCellBaseZIndex = 100; 

@interface FMStackedLayout() 

@property(strong,nonatomic) NSArray  *rotations; 
@property(strong,nonatomic) NSIndexPath *selectedIndexPath; 

@end 

@implementation FMStackedLayout 

#pragma mark - Lifecycle 
-(id)initWithSelectedCellIndexPath:(NSIndexPath *)indexPath{ 
    self = [super init]; 
    if (self) { 
     self.selectedIndexPath = indexPath; 
     [self setup]; 
    } 
    return self; 
} 

-(id)initWithCoder:(NSCoder *)aDecoder{ 
    self = [super init]; 
    if (self){ 
     [self setup]; 
    } 
    return self; 
} 

-(void)setup{ 
    NSMutableArray *rotations = [NSMutableArray arrayWithCapacity:RotationCount]; 
    CGFloat percentage = 0.0f; 

    for (NSUInteger i = 0; i < RotationCount; i++) { 
     // Ensure that each angle is different enough to be seen 
     CGFloat newPercentage = 0.0f; 
     do { 
      newPercentage = ((CGFloat)(arc4random() % 220) - 110) * 0.0001f; 
     } while (fabsf(percentage - newPercentage) < 0.006); 

     percentage = newPercentage; 

     CGFloat angle = 2 * M_PI * (1.0f + percentage); 
     CATransform3D transform = CATransform3DMakeRotation(angle, 0.0f, 0.0f, 1.0f); 
     [rotations addObject:[NSValue valueWithCATransform3D:transform]]; 
    } 

    self.rotations = rotations; 
} 

#pragma mark - Layout 
-(void)prepareLayout{ 
    [super prepareLayout]; 

    CGSize size = self.collectionView.frame.size; 
    self.cellCount = [self.collectionView numberOfItemsInSection:0]; 
    self.center = CGPointMake(size.width/2.0, size.height/2.0); 
} 

-(CGSize)collectionViewContentSize{ 
    return self.collectionView.frame.size; 
} 

-(UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath{ 
    UICollectionViewLayoutAttributes *attributes = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath]; 

    attributes.size = CGSizeMake(ITEM_WIDTH, ITEM_HEIGHT); 
    attributes.center = self.center; 
    if (indexPath.item == self.selectedIndexPath.item) { 
     attributes.zIndex = 100; 
    }else{ 
     attributes.transform3D = [self transformForPersonViewAtIndex:indexPath]; 
    } 

    return attributes; 
} 

-(NSArray *)layoutAttributesForElementsInRect:(CGRect)rect{ 
    NSMutableArray *attributes = [NSMutableArray array]; 
    for (NSInteger i = 0; i < self.cellCount; i++) { 
     NSIndexPath *indexPath = [NSIndexPath indexPathForItem:i 
                inSection:0]; 
     [attributes addObject:[self layoutAttributesForItemAtIndexPath:indexPath]]; 
    } 
    return attributes; 
} 

#pragma mark - Private 
-(CATransform3D)transformForPersonViewAtIndex:(NSIndexPath *)indexPath{ 
    NSInteger offset = (indexPath.section * RotationStride + indexPath.item); 
    return [self.rotations[offset % RotationCount] CATransform3DValue]; 
} 

@end 

然后,在你的UICollectionView,你叫

MyLayout *stackedLayout = [[MyLayout alloc] initWithSelectedCellIndexPath:indexPath]; 
[stackedLayout invalidateLayout]; 
[self.collectionView setCollectionViewLayout:stackedLayout 
            animated:YES]; 

单元格之间的动画将由自定义布局处理。

+3

这不是一个答案。看起来你有答案,但不想分享。 – Fiveagle

+1

@Mark Struzinski你能分享一下你的解决方案吗? –

+0

这是一段相当长的代码,但我并没有因为任何特殊原因而退缩。我已经将其发布给任何感兴趣的人。不知道为什么在这里有downvotes。最终解决方案使用了Apple文档中详细记录的方法:http://developer.apple.com/library/ios/#documentation/uikit/reference/UICollectionViewLayout_class/Reference/Reference.html。我一直在试图避免它,因为它的工作量很大。 –

6

似乎是一个多部分问题,所以我会回答它的一部分。

  1. UICollectionviewCells不会自动对亮点选择调整。它只会告诉你,当细胞被突出显示或选择,但有一个例外:

在大多数情况下,集合视图修改的 细胞只有属性,以表明它已被选中或突出显示;它不会 更改您的细胞的视觉外观,只有一个例外。如果 单元的selectedBackgroundView属性包含有效视图,则 集合视图将显示单元格高亮显示时的视图或 选定的视图。

否则,您必须手动高亮显示视觉。通常通过使用以下方法,它们是<UICollectionViewDelegate>议定书以无障碍的一个或多个调整任一.alpha属性的整个小区的,或换出在细胞本身背景UIImageView.image属性:

collectionView:didDeselectItemAtIndexPath: 
collectionView:didHighlightItemAtIndexPath: 
collectionView:didSelectItemAtIndexPath: 
collectionView:didUnhighlightItemAtIndexPath: 
collectionView:shouldDeselectItemAtIndexPath: 
collectionView:shouldHighlightItemAtIndexPath: 
collectionView:shouldSelectItemAtIndexPath: 

至于你的问题的其余部分,我希望我可以有更多的帮助,但我不熟悉自定义视图动画。

+0

嗨。我已经尝试从collectionView:didSelectItemAtIndexPath:进行动画处理。但是,根据这个问题,我想实际上将单元格移动到屏幕的中心以专注于它。我想知道如果这并不意味着我应该使用模式视图控制器,并做一些事情,让它出现单元格动画。 –