使用UICollectionView创建一个书架

63
我将在我的项目中创建一个书架。基本要求如下:
  • 类似于iBooks的书架
  • 支持所有方向
  • 良好地支持所有种类的iOS设备(不同的分辨率)
  • 支持删除和插入项目
  • 支持通过长按手势重新排序项目
  • 当第一行被拉下时,显示隐藏的标志
UICollectionView是我想到的第一种选择。它很容易支持网格单元格。所以我搜索了一些非常有用的教程: Bryan Hansen的UICollectionView自定义布局教程 Mark Pospesel的如何向UICollectionView添加装饰视图 LXReorderableCollectionViewFlowLayout 这是结果:(由于我选择的图形不完美,请忽略颜色不一致的问题。) enter image description here 我做了以下事情:
  1. 通过创建从LXReorderableCollectionViewFlowLayout继承的类(用于重新排序目的),创建自定义布局,该类继承自UICollectionFlowLayout
  2. 添加一个用于显示标志的装饰视图
  3. 添加一个用于显示书架的装饰视图
但是我遇到了一些问题: 1.如果可以在一个屏幕上显示项目,则无法滚动 然后我添加了以下代码,使内容大小更大:
- (CGSize)collectionViewContentSize
{
    return CGSizeMake(self.collectionView.bounds.size.width,      self.collectionView.bounds.size.height+100);
}

现在我可以滚动了。这里我拉下第一行:

enter image description here

你可以看到徽标的装饰视图正在工作。

2. 但是当我拉起最后一行时,我遇到了第二组问题:

enter image description here

你可以看到绿色框部分没有添加装饰视图。

3. 书架的装饰视图背景越来越暗。(请参阅同样的问题

4. 当我重新排列项目时,书架栏有时会移动

enter image description here

以下是一些重要代码:

- (void)prepareLayout
{
    // call super so flow layout can do all the math for cells, headers, and footers
    [super prepareLayout];
    
    NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
    NSMutableDictionary *shelfLayoutInfo = [NSMutableDictionary dictionary];
    
    // decoration view - emblem
    NSIndexPath *indexPath = [NSIndexPath indexPathForItem:0 inSection:0];
    UICollectionViewLayoutAttributes *emblemAttributes =
        [UICollectionViewLayoutAttributes layoutAttributesForDecorationViewOfKind:[EmblemView kind]
            withIndexPath:indexPath];
    emblemAttributes.frame = [self frameForEmblem:YES];
    dictionary[[EmblemView kind]] = @{indexPath: emblemAttributes};

    // Calculate where shelves go in a vertical layout
    int sectionCount = [self.collectionView numberOfSections];
    
    CGFloat y = 0;
    CGFloat availableWidth = self.collectionViewContentSize.width - (self.sectionInset.left + self.sectionInset.right);
    int itemsAcross = floorf((availableWidth + self.minimumInteritemSpacing) / (self.itemSize.width + self.minimumInteritemSpacing));
    
    for (int section = 0; section < sectionCount; section++)
    {
        y += self.headerReferenceSize.height;
        //y += self.sectionInset.top;
        
        int itemCount = [self.collectionView numberOfItemsInSection:section];
        int rows = ceilf(itemCount/(float)itemsAcross)+1; // add 2 more empty row which doesn't have any data
        for (int row = 0; row < rows; row++)
        {
            indexPath = [NSIndexPath indexPathForItem:row inSection:section];
            shelfLayoutInfo[indexPath] = [NSValue valueWithCGRect:CGRectMake(0,y, self.collectionViewContentSize.width, self.itemSize.height + DECORATION_HEIGHT)];
            y += self.itemSize.height;
            
            if (row < rows - 1)
                y += self.minimumLineSpacing;
        }
        
        y += self.sectionInset.bottom;
        y += self.footerReferenceSize.height;
    }
    
    dictionary[[ShelfView kind]] = shelfLayoutInfo;
    
    self.shelfLayoutInfo = dictionary;
}


- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect
{
    NSArray *attributesArrayInRect = [super layoutAttributesForElementsInRect:rect];
    
    // cell layout info
    for (BookShelfLayoutAttributes *attribs in attributesArrayInRect)
    {
        
        attribs.zIndex = 1;
        CATransform3D t = CATransform3DIdentity;
        t = CATransform3DTranslate(t, 0, 0, 40);
        attribs.transform3D = CATransform3DRotate(t, 15 * M_PI / 180, 1, 0, 0);
    }
    
    // Add our decoration views (shelves)
    NSMutableDictionary* shelfDictionary = self.shelfLayoutInfo[[ShelfView kind]];
    NSMutableArray *newArray = [attributesArrayInRect mutableCopy];
    
    [shelfDictionary enumerateKeysAndObjectsUsingBlock:^(id key, NSValue* obj, BOOL *stop) {
        
        if (CGRectIntersectsRect([obj CGRectValue], rect))
        {
            UICollectionViewLayoutAttributes *attributes = [UICollectionViewLayoutAttributes layoutAttributesForDecorationViewOfKind:[ShelfView kind] withIndexPath:key];
            attributes.frame = [obj CGRectValue];
            
            NSLog(@"decorationView rect = %@",NSStringFromCGRect(attributes.frame));
            attributes.zIndex = 0;
            //attributes.alpha = 0.5; // screenshots
            [newArray addObject:attributes];
        }
    }];
    
    attributesArrayInRect = [NSArray arrayWithArray:newArray];
    
    NSMutableDictionary* emblemDictionary = self.shelfLayoutInfo[[EmblemView kind]];
    NSMutableArray *newArray2 = [attributesArrayInRect mutableCopy];
    [emblemDictionary enumerateKeysAndObjectsUsingBlock:^(NSIndexPath *indexPath, UICollectionViewLayoutAttributes *attributes, BOOL *innerStop) {
        if (CGRectIntersectsRect(rect, attributes.frame)) {
            [newArray2 addObject:attributes];
        }
    }];
    
    attributesArrayInRect = [NSArray arrayWithArray:newArray2];
    
    return attributesArrayInRect;
}

如果您有耐心阅读这篇文章并提供任何建议或建议,我会非常感激。如果我能解决所有问题,我将发布完整的源代码。提前致谢。


我在苹果论坛上发现了一篇帖子。看起来DecorationView的复制是一个bug。 - Bagusflyer
作为您的prepareLayout函数的一部分,您应该真正计算您的集合视图的内容大小。目前情况下,无论集合视图中有多少项,您只能向下滚动100像素。 - Ash
如果您在问题标题中包含“ UICollectionView”所属的语言或库,可能会有助于获得答案。这一点并不明显。 - David Spector
1个回答

1
我建议您检查最后一行并对第一行执行 [self.collectionView ScrollEnable:NO],将collectionView和collectionViewCell的背景颜色设置为透明,并将书架图像设置在背景视图上。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接