ラベルの幅に応じた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;
}

回答:


83

sizeForItemAtIndexPath復帰テキストのサイズ

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

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

4
あなたが私にどのように感謝するか想像できない!それは本当にうまくいきます。[cell.myLabel sizeToFit]の問題を解決する必要があるのは、スクロール後にのみフルサイズで表示されるためです。しかし、私はあなたの解決策にさえ近づいていません。
パルプ

あなたの助けをありがとう、しかし私はまだ1つの問題を抱えています。[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; }
パルプ

48

Swift 4.2以降

原則は次のとおりです。

  1. 委任が設定されていることを確認します(例collectionView.delegate = self

  2. 実装しますUICollectionViewDelegateFlowLayout(必要なメソッドシグネチャが含まれます)。

  3. collectionView...sizeForItemAtメソッドを呼び出します。

  4. メソッドを呼び出すためにブリッジキャストStringする必要はありません。Swift はそのまま使用できます。NSStringsize(withAttributes: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

Swift 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
これがダミーラベルの作成にコストがかかるというコメントに対処するために、複数のラベルではなく1つのダミーラベルを作成することもできます。本当に必要なのは、ラベル属性のテキストサイズだけです。結局のところ、これはを介してテキストサイズを計算したものと基本的に同じsizeWithAttributesであるため、おそらくそれが望ましい答えです。
Stephen Paul

@Hassan Thanks broそれは私のために働きました、しかし私は幅に問題を見つけたので戻りCGSize(width:label.frame.width + 50、height:32)を追加しました。その後、うまくいきました。この答えがトップリストにあるはずです。
Arshad Shaik

27

あなたが非常に短いCGSizeを与えているかもしれないコードの下のチェックアウト。

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

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

@Vineesh TPご回答ありがとうございます。ぜひチェックして、お知らせします!
パルプ

+1000これは間違いなく動作します。あなたとBasheerの投稿には、緑色のチェックマークが付いているはずです。
岩だらけのアライグマ

Swift 3でこれを行う方法はありますか?
UKDataGeek 2017

1
パーティータイム、マンに感謝!
ラビ


14

スウィフト4

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

0

//ビューに追加しました

UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init];
    [layout setScrollDirection:UICollectionViewScrollDirectionHorizontal];
    layout.estimatedItemSize = CGSizeMake(self.breadScrumbCollectionView.frame.size.width, 30); 
self.breadScrumbCollectionView.collectionViewLayout = layout;
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.