Dynamische Zellenbreite von UICollectionView abhängig von der Etikettenbreite

91

Ich habe eine UICollectionView, die Zellen aus wiederverwendbaren Zellen lädt, die Label enthalten. Ein Array bietet Inhalt für dieses Label. Mit sizeToFit kann ich die Größe der Etikettenbreite je nach Inhaltsbreite problemlos ändern. Aber ich kann die Zelle nicht passend zum Etikett machen.

Hier ist der Code

- (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;
}
Fruchtfleisch
quelle
ähnliche Art von Problem ... stackoverflow.com/questions/24915443/… ???
Fattie

Antworten:

83

Im sizeForItemAtIndexPathGegenzug die Größe des Textes

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

    return [(NSString*)[arrayOfStats objectAtIndex:indexPath.row] sizeWithAttributes:NULL];
}
Basheer_CAD
quelle
4
Sie können sich nicht vorstellen, wie ich Ihnen danke! Das funktioniert wirklich. Jetzt muss ich nur noch das Problem [cell.myLabel sizeToFit] lösen, da es erst nach dem Scrollen in voller Größe angezeigt wird. Aber ich war Ihrer Lösung noch nicht einmal nahe.
Zellstoff
Vielen Dank für Ihre Hilfe, aber ich habe noch ein Problem. Wenn ich [cell.myLabel sizeToFit] auskommentiere, werden Wörter abgeschnitten und Buchstaben unten abgeschnitten, aber nach dem Scrollen wird es in Ordnung (Wörter haben die normale Größe und Buchstaben springen etwas nach oben). Wenn ich die Nachricht [cell.myLabel sizeToFit] kommentiere und deaktiviere (ich habe beschlossen, mit IB herumzuspielen, und es funktioniert einwandfrei), werden am Ende und am Ende Wörter ausgeschnitten. Ich habe einen Screenshot gemacht goo.gl/HaoqQV Es ist nicht sehr scharf auf Nicht-Retina-Displays, aber Sie können sehen, dass Buchstaben geschnitten haben. Ihr Vorschlag zur Lösung wird sehr geschätzt!
Zellstoff
2
Verwenden Sie anstelle von sizeToFit sizeWithAttributes, um die CGSize des Texts abzurufen, und legen Sie dann den Rahmen des Etiketts mit einer neuen Größe fest.
Basheer_CAD
Vielen Dank für den Vorschlag, aber ich habe immer noch myLabel unten und am Ende geschnitten. Vielleicht irre ich mich bei der Umsetzung Ihres Vorschlags. Hier ist mein Code
Zellstoff
- (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; }
Zellstoff
44

Swift 4.2+

Prinzip ist:

  1. Stellen Sie sicher , dass eine Delegation eingerichtet ist (zB collectionView.delegate = self)

  2. Implementieren UICollectionViewDelegateFlowLayout(es enthält die erforderliche Methodensignatur).

  3. collectionView...sizeForItemAtMethode aufrufen .

  4. Keine Notwendigkeit, Brücke Guss Stringauf , NSStringum die size(withAttributes:Anrufmethode. Swift Stringhat es aus der Box.

  5. Die Attribute sind die gleichen, für die Sie festgelegt haben, z (NS)AttributedString. B. Schriftfamilie, Größe, Gewicht usw. Optionaler Parameter.


Probenlösung:

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

Sie möchten jedoch höchstwahrscheinlich konkrete Zeichenfolgenattribute für Ihre Zelle angeben, sodass die endgültige Rückgabe folgendermaßen aussehen würde:

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
    }
}

Warum sollten Sie UILabel NICHT verwenden, um die Größe zu berechnen? Hier ist die vorgeschlagene Lösung:

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

Ja, Sie erhalten das gleiche Ergebnis. Es sieht simpel aus und scheint eine gute Lösung zu sein. Aber es ist unangemessen, weil: 1) es teuer ist, 2) Overhead und 3) schmutzig.

Es ist teuer, weil UILabel ein komplexes UI-Objekt ist, das bei jeder Iteration erstellt wird, wenn Ihre Zelle angezeigt wird, obwohl Sie es hier nicht benötigen. Dies ist eine Overhead-Lösung, da Sie nur die Größe eines Textes ermitteln müssen, aber sogar ein ganzes UI-Objekt erstellen müssen. Und aus diesem Grund ist es schmutzig.

Hexfire
quelle
1
Vergessen Sie nicht zu setzencollectionView.delegate == self // or whatever-object-which-do-it
Fitsyu
Gute Antwort. Obwohl die Größe, die ich bekam, etwas kleiner war als nötig, entschied ich mich bei verschiedenen Saitenlängen, die Größe selbst ein wenig zu ändern. Das Hinzufügen von zusätzlichen 5 Punkten zur Breite hat den Trick gemacht:CGSize(width: title.size(withAttributes: [NSAttributedString.Key.font : UIFont.boldSystemFont(ofSize: 16)]).width + 5, height: 50)
Starsky
36

Ich habe einen kleinen Trick für Swift 4.2 gefunden

Für dynamische Breite und feste Höhe:

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)
    }

Für dynamische Höhe und feste Breite:

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)
        }
Hassan Izhar
quelle
7
Seien Sie vorsichtig damit. Das Erstellen und Zeichnen eines neuen UILabels für jede Zellenberechnung ist sehr teuer.
AnthonyR
müssen UICollectionViewDelegateFlowLayout
cristianego
2
Um den Kommentar zu diesem teuren Erstellen eines Dummy-Labels zu beantworten, können Sie möglicherweise nur ein Dummy-Label anstelle mehrerer erstellen. Alles, was Sie wirklich davon wollen, ist die Textgröße aus Beschriftungsattributen. Am Ende des Tages entspricht dies im Wesentlichen der Berechnung der Textgröße über sizeWithAttributes. Vielleicht ist dies die bevorzugte Antwort.
Stephen Paul
@ Hassan Danke, Bruder, es hat bei mir funktioniert, aber ich habe ein Problem mit der Breite gefunden. Daher wurde die Rückgabe von CGSize hinzugefügt (Breite: label.frame.width + 50, Höhe: 32). Dann hat es funktioniert, ich denke, diese Antwort sollte auf der Topliste stehen.
Arshad Shaik
27

Kasse unter dem Code, den Sie möglicherweise sehr kurz CGSize geben.

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

    NSString *testString = @"SOME TEXT";
    return [testString sizeWithAttributes:NULL];
}
Raza.najam
quelle
@Vineesh TP Vielen Dank für Ihre Antwort, ich werde es auf jeden Fall überprüfen und Sie wissen lassen!
Zellstoff
+1000 Dieser funktioniert definitiv. Ihr und Basheers Beitrag sollten zusammen ein grünes Häkchen haben.
Rocky Raccoon
Irgendeine Idee, wie man das mit Swift 3 macht?
UKDataGeek
1
Danke Mann, es ist Partyzeit !!
Ravi
18

In Swift 3

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

Swift 4

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

// In der Ansicht Didload hinzufügen

UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init];
    [layout setScrollDirection:UICollectionViewScrollDirectionHorizontal];
    layout.estimatedItemSize = CGSizeMake(self.breadScrumbCollectionView.frame.size.width, 30); 
self.breadScrumbCollectionView.collectionViewLayout = layout;
Apurva Gaikwad
quelle