UICollectionView的动态单元格宽度取决于标签宽度


92

我有一个UICollectionView,它从包含标签的可重用单元格加载单元格。数组为该标签提供内容。我可以使用sizeToFit轻松根据内容宽度调整标签宽度。但是我无法制作适合标签的单元格。

这是代码

- (void)viewDidLoad
{
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    arrayOfStats =  @[@"time:",@"2",@"items:",@"10",@"difficulty:",@"hard",@"category:",@"main"];
}

- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:     (NSInteger)section{
    return [arrayOfStats count];
}

- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath{

    return CGSizeMake(??????????);
}

- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView{

    return 1;
}

- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath{

    Cell *cell = (Cell *) [collectionView dequeueReusableCellWithReuseIdentifier:@"qw" forIndexPath:indexPath];
    cell.myLabel.text = [NSString stringWithFormat:@"%@",[arrayOfStats objectAtIndex:indexPath.item]];
    // make label width depend on text width
    [cell.myLabel sizeToFit];

    //get the width and height of the label (CGSize contains two parameters: width and height)
    CGSize labelSize = cell.myLbale.frame.size;

    NSLog(@"\n width  = %f height = %f", labelSize.width,labelSize.height);

    return cell;
}

Answers:


83

作为sizeForItemAtIndexPath回报,文字大小

- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath{

    return [(NSString*)[arrayOfStats objectAtIndex:indexPath.row] sizeWithAttributes:NULL];
}

4
你无法想象我会如何感谢你!那确实有效。现在,我只需要解决[cell.myLabel sizeToFit]问题,因为它只有在滚动后才显示为完整大小。但是我还没有接近您的解决方案。
纸浆

感谢您的帮助,但我仍然有一个问题。当我取消注释[cell.myLabel sizeToFit]时,我会截断单词并​​在底部切开字母,但是在我滚动后就可以了(单词的大小和字母都正常跳了一点)。如果我评论并禁用[cell.myLabel sizeToFit]消息(我决定使用IB,它工作正常),我在末尾切了一些单词。我制作了一个屏幕快照goo.gl/HaoqQV在非视网膜显示器上不是很清晰,但是您可以看到字母被切掉了。您对如何解决的建议将不胜感激!
纸浆

2
代替sizeToFit,使用sizeWithAttributes获取文本的CGSize,然后使用新的大小设置标签的框架。
Basheer_CAD 2014年

谢谢您的建议,但我的底部和底部仍然保留了myLabel。我可能对您的建议实施有误。这是我的代码
纸浆

- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath{ Cell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"qw" forIndexPath:indexPath]; cell.myLbale.text = [NSString stringWithFormat:@"%@",[arrayOfStats objectAtIndex:indexPath.item]]; CGSize textSize; textSize = [[arrayOfStats objectAtIndex:indexPath.item] sizeWithAttributes:@{NSFontAttributeName:[UIFont systemFontOfSize:12.0f]}]; [cell.myLbale sizeThatFits:textSize]; //[cell.myLbale sizeToFit]; return cell; }
纸浆

47

迅捷4.2+

原理是:

  1. 确保设置了委派(例如collectionView.delegate = self

  2. 实现UICollectionViewDelegateFlowLayout(包含必要的方法签名)。

  3. 调用collectionView...sizeForItemAt方法。

  4. 无需桥接投StringNSString调用size(withAttributes:方法。Swift String开箱即用。

  5. 属性与您设置的属性相同(NS)AttributedString,即字体系列,大小,粗细等。可选参数。


样品溶液:

extension ViewController: UICollectionViewDelegateFlowLayout {
    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
        return "String".size(withAttributes: nil)
    }
}

但是您很可能希望指定与您的单元格相对应的具体字符串属性,因此最终返回将类似于:

extension ViewController: UICollectionViewDelegateFlowLayout {
    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
        // dataArary is the managing array for your UICollectionView.
        let item = dataArray[indexPath.row]
        let itemSize = item.size(withAttributes: [
            NSAttributedString.Key.font : UIFont.boldSystemFont(ofSize: 14)
        ])
        return itemSize
    }
}

为什么不应该使用UILabel计算大小? 这是建议的解决方案:

let label = UILabel(frame: CGRect.zero)
label.text = textArray[indexPath.item]
label.sizeToFit()

是的,您得到相同的结果。它看起来很简单,可能是首选解决方案。但这是不适当的,因为:1)价格昂贵,2)开销很大,3)脏了。

这很昂贵,因为UILabel是一个复杂的UI对象,即使您在这里不需要它,只要您的单元格即将显示,它就会在每次迭代中创建。这是一个开销很大的解决方案,因为您只需要获取文本大小即可,但是您可以创建整个UI对象。因此很脏。


1
别忘了设定collectionView.delegate == self // or whatever-object-which-do-it
Fitsyu

好答案。尽管我得到的大小比所需的要小一些,但是在不同的字符串长度上,我还是决定自己修改一下大小。宽度增加了5个点就达到了目的:CGSize(width: title.size(withAttributes: [NSAttributedString.Key.font : UIFont.boldSystemFont(ofSize: 16)]).width + 5, height: 50)
Starsky

37

我发现了一个快速4.2的小技巧

对于动态宽度和固定高度:

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
        let label = UILabel(frame: CGRect.zero)
        label.text = textArray[indexPath.item]
        label.sizeToFit()
        return CGSize(width: label.frame.width, height: 32)
    }

对于动态高度和固定宽度:

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
            let label = UILabel(frame: CGRect.zero)
            label.text = textArray[indexPath.item]
            label.sizeToFit()
            return CGSize(width: 120, height: label.frame.height)
        }

8
使用时要小心。为每个单元格计算创建和绘制新的UILabel非常昂贵。
AnthonyR

需要添加UICollectionViewDelegateFlowLayout
cristianego

3
为了解决有关创建虚拟标签的代价昂贵的评论,也许您可​​以只创建一个虚拟标签,而不是多个。您真正想要的只是标签属性的文本大小。不过,归根结底,它与通过算出的文字大小基本相同sizeWithAttributes,因此也许这是首选的答案。
Stephen Paul

@哈桑谢谢兄弟,它为我工作,但我发现宽度有问题,所以添加了返回CGSize(width:label.frame.width + 50,height:32)。然后它起作用了,我认为这个答案应该放在最上面。
阿沙德·谢克

27

在下面的代码中签出您可能给出的CGSize非常短。

- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath{

    NSString *testString = @"SOME TEXT";
    return [testString sizeWithAttributes:NULL];
}

@Vineesh TP谢谢您的回复,我一定会检查出来的,让您知道!
纸浆

+1000这个绝对有效。您和Basheer的帖子应同时带有绿色的复选标记。
岩石浣熊

任何想法如何快速3做到这一点?
UKDataGeek '17

1
感谢Man,它的聚会时间!
拉维

18

在Swift 3中

let size = (arrayOfStats[indexPath.row] as NSString).size(attributes: nil)

14

斯威夫特4

let size = (arrayOfStats[indexPath.row] as NSString).size(withAttributes: nil)

0

//在视图中添加didload

UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init];
    [layout setScrollDirection:UICollectionViewScrollDirectionHorizontal];
    layout.estimatedItemSize = CGSizeMake(self.breadScrumbCollectionView.frame.size.width, 30); 
self.breadScrumbCollectionView.collectionViewLayout = layout;
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.