UICollectionViewCell 使用原型尺寸

Posted

技术标签:

【中文标题】UICollectionViewCell 使用原型尺寸【英文标题】:UICollectionViewCell using prototype sizes 【发布时间】:2014-03-17 23:00:08 【问题描述】:

我有一个 UICollectionView,其中包含三个不同的原型单元格,每个单元格都通过 Storyboard 设置不同的高度。在运行时,Collection View 使用自己的单元格大小,忽略我的 Storyboard 的。

我目前正在使用 collectionView:layout:sizeForItemAtIndexPath: 和几个条件来直接设置每个 CGSize。

有没有更好的方法来设置单元格大小?我似乎无法检索每个单元格的 Storyboard 大小,而且 CGSizeMake 似乎太硬编码并且不是很灵活。

【问题讨论】:

【参考方案1】:

目前似乎没有简单的方法:

从 Storyboard 中获取 UICollectionViewCell 原型单元大小运行时。 只需在一处管理原型单元的大小(而不必在故事板单元原型中输入它们并实施sizeForItemAtIndexPath)。

建议的方法here(用于UITableViews)不起作用,因为在sizeForItemAtIndexPath 中使用dequeueReusableCellWithReuseIdentifier 会导致无限循环。

但是,我设法通过以下方式做到了这一点

    在所有 Storyboard 中的每个 UICollectionView 原型单元格中添加一个唯一的(在每个故事板中的所有 UICollectionViewCells 中)重用标识符。

    使用从所有 Storyboard 中提取 UICollectionViewCell 帧大小的脚本,将 Run script 构建阶段添加到您的项目中。

    output=$PROJECT_DIR/StoryboardPrototypeCellSizes.h
    printf "@" > $output
    
    for storyboard in $(find $PROJECT_DIR -name "*.storyboard")
    do
        echo "Scanning storyboard $storyboard..."
        delimiter=
        for line in $(xpath $storyboard "//collectionViewCell/@reuseIdentifier[string-length()>0] | //collectionViewCell/rect" 2>&-)
        do
            case $line in
                reuseIdentifier*)
                    reuseIdentifier=$(sed 's/[^"]*"\([^"]*\)".*/\1/' <<< $line)
                    ;;
                width*)
                    if [ -n "$reuseIdentifier" ]; then
                        width=$(sed 's/[^"]*"\([^"]*\)".*/\1/' <<< $line)
                    fi
                    ;;
                height*)
                    if [ -n "$reuseIdentifier" ]; then
                        height=$(sed 's/[^"]*"\([^"]*\)".*/\1/' <<< $line)
                    fi
                    ;;
            esac
    
            if [ -n "$reuseIdentifier" ] && [ -n "$width" ] && [ -n "$height" ]; then
                printf "$delimiter@\"$reuseIdentifier\" : [NSValue valueWithCGSize:CGSizeMake($width, $height)]" >> $output
                unset reuseIdentifier
                unset width
                unset height
                delimiter=,\\n
            fi
        done
    done
    
    printf ";\n" >> $output
    

    这将创建一个名为 StoryboardPrototypeCellSizes.h 的头文件,其中包含以下示例内容:

    @@"TodayCell" : [NSValue valueWithCGSize:CGSizeMake(320, 80)],
    @"SpecialDayCell" : [NSValue valueWithCGSize:CGSizeMake(320, 42)],
    @"NameDayCell" : [NSValue valueWithCGSize:CGSizeMake(320, 30)];
    

    添加一个辅助方法以在控制您的 UICollectionView 的视图控制器中返回 UICollectionViewCell 重用标识符:

    - (NSString *)cellReuseIdentifierAtIndexPath:(NSIndexPath *)indexPath
    
        switch (indexPath.item) 
            case 0: return @"TodayCell";
            case 1: return @"SpecialDayCell";
            case 2: return @"NameDayCell";
        
        return nil;
    
    

    确保在cellForItemAtIndexPath 中使用相同的重用标识符:

    - (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
    
        UICollectionViewCell *cell =
            [collectionView dequeueReusableCellWithReuseIdentifier:
                [self cellReuseIdentifierAtIndexPath:indexPath]
                forIndexPath:indexPath];
        ...
    

    终于实现sizeForItemAtIndexPath

    - (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath
    
        NSDictionary *storyboardPrototypeCellSizes =
    #import "StoryboardPrototypeCellSizes.h"
    
         return [(NSValue *)storyboardPrototypeCellSizes[
                 [self cellReuseIdentifierAtIndexPath:indexPath]
                ] CGSizeValue];
    
    

此解决方案允许您在 Storyboard(s) 中仅定义一次 UICollectionViewCell 原型单元大小,并且在运行时也不会执行任何不符合 App Store 的操作。

****Edit:**** 您还可以通过添加另一个具有相同内容的脚本并将“collectionViewCell”替换为“collectionReusableView”并将头文件重命名为例如 StoryboardReusableViewSizes.h 来获取 UICollectionReusableView 大小

【讨论】:

海拉作弊,但是...很棒。 对于那些在 ios 8 发布后来到这里的人,解决这个问题的方法是使用自调整大小的单元格。通过自动布局单元格,使其宽度/高度根据内容大小而变化,可以让 CollectionView 调整单元格的大小以适应其内容。 '这里提出的方法(用于 UITableViews)不起作用,因为在 sizeForItemAtIndexPath 中使用 dequeueReusableCellWithReuseIdentifier 会导致无限循环。这个真的帮了我很大的忙,因为我不知道为什么我的应用程序总是崩溃。【参考方案2】:

没有更好的方法来设置单元格大小。单元格大小在 UICollectionView 中的多个位置使用 - 用于定位,用于滚动指示器。例如,如果用户滚动收集具有数千个小单元格的集合,尽快接收它们是非常重要的。因此,创建单元格并询问其大小不是一种选择。您必须实现 collectionView:layout:sizeForItemAtIndexPath: 并且它应该可以快速运行。

【讨论】:

【参考方案3】:

您是否在UICollectionView 中使用流式布局?如果是,您可以使用UICollectionViewDelegateFlowLayout 协议的sizeForItemAtIndexPath 方法来提供单元格的大小。如果您在应用程序中使用 OSS 组件没有问题,可以使用RFQuiltLayout 来实现此目的。

【讨论】:

就像我在问题本身中提到的那样,我目前正在使用 sizeForItemAtIndexPath。另外,我可能错了,但 RFQuiltLayout 不会将集合分成小块吗?我认为它不适合与几个不成比例的可变高度单元格一起使用(例如,一个是 90 点,另一个是 131 点,最后一个是 78 点高)。 您提到UICollectionView 中有三个不同的原型单元格,您是否探索过使用 Autolayout 布置它们的子视图? Obc.io 在UICollectionView 布局上有一个nice article。在 UICollectionView 中使用 Autolayout 的示例是 here。根据我的经验,最好使用UICollectionView的布局对象来控制每个单元格的大小和位置,同时使用Autolayout来布局子视图? 我们越来越接近我想要的结果了!这是一篇很棒的文章。我正在尝试完全按照这些思路进行操作,但我正在寻找一种方法来使用我在 Storyboard Collection View 中实例化的 Storyboard 单元,而不是在外部创建一个 XIB 并以编程方式注册它们。有什么想法吗?编辑:我尝试通过标签号实例化它们,但这似乎不起作用。另外,我不能在 sizeForItem 中出列。

以上是关于UICollectionViewCell 使用原型尺寸的主要内容,如果未能解决你的问题,请参考以下文章

如何正确设置 UICollectionViewCell 边距?

根据标签文本自定义 UICollectionViewCell 自身大小

UICollectionViewCell 中的 UILabel 布局怪异

Xamarin iOS:从情节提要中的 UICollectionViewCell 获取信息

UICollectionViewCell 的高度,带有 2 个多行 UILabel / 自调整大小的集合视图单元格

在UICollectionViewCell iOS Swift 4中注册标题时出错