Larghezza cella dinamica di UICollectionView in base alla larghezza dell'etichetta


92

Ho un UICollectionView, che carica le celle dalla cella riutilizzabile, che contiene l'etichetta. Un array fornisce il contenuto per quell'etichetta. Posso ridimensionare facilmente la larghezza dell'etichetta in base alla larghezza del contenuto con sizeToFit. Ma non posso fare in modo che la cella si adatti all'etichetta.

Ecco il codice

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

tipo di problema simile ... stackoverflow.com/questions/24915443/… ???
Fattie

Risposte:


83

In sizeForItemAtIndexPathcambio la dimensione del testo

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

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

4
Non puoi immaginare come ti ringrazio! Funziona davvero. Ora devo solo risolvere il problema [cell.myLabel sizeToFit], perché appare nella sua dimensione intera solo dopo lo scorrimento. Ma non sono stato nemmeno vicino alla tua soluzione.
polpa

grazie per il tuo aiuto ma ho ancora un problema. Quando rimuovo [cell.myLabel sizeToFit] le parole vengono troncate e le lettere tagliate in basso, ma diventa ok dopo aver fatto scorrere (le parole hanno dimensioni normali e le lettere saltano un po 'in alto). Se commento e disabilito il messaggio [cell.myLabel sizeToFit] (ho deciso di giocare con IB e funziona bene) ho le parole tagliate alla fine e in fondo. Ho fatto uno screenshot goo.gl/HaoqQV Non è molto nitido sui display non retina ma puoi vedere che le lettere sono tagliate. Il tuo suggerimento su come risolvere sarà davvero apprezzato!
polpa

2
invece di sizeToFit, usa sizeWithAttributes per ottenere il CGSize del testo, quindi imposta la cornice dell'etichetta con una nuova dimensione.
Basheer_CAD

grazie per il suggerimento ma ho ancora myLabel tagliato in fondo e alla fine. Forse mi sbaglio nell'attuazione del tuo suggerimento. Ecco il mio codice
pulp

- (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; }
polpa

48

Swift 4.2+

Il principio è:

  1. Assicurati che la delega sia impostata (ad es. collectionView.delegate = self)

  2. Implementa UICollectionViewDelegateFlowLayout(contiene la firma del metodo necessaria).

  3. collectionView...sizeForItemAtMetodo di chiamata .

  4. Non c'è bisogno di ponte-fusione Stringa NSStringalla chiamata size(withAttributes:del metodo. Swift lo Stringha fuori dalla scatola.

  5. Gli attributi sono gli stessi che hai impostato (NS)AttributedString, cioè famiglia di caratteri, dimensione, peso, ecc. Parametro opzionale.


Soluzione campione:

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

Ma molto probabilmente vorresti specificare attributi di stringa concreti rispettivi alla tua cella, quindi il ritorno finale sarebbe simile a:

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

Perché NON DOVRESTI usare UILabel per calcolare la taglia? Ecco la soluzione suggerita:

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

Sì, ottieni lo stesso risultato. Sembra semplicistico e può sembrare una soluzione ideale. Ma è improprio perché: 1) è costoso, 2) sovraccarico e 3) sporco.

È costoso perché UILabel è un oggetto dell'interfaccia utente complesso, che viene creato ad ogni iterazione ogni volta che la tua cella sta per essere visualizzata anche se non ne hai bisogno qui. È una soluzione overhead perché hai solo bisogno di ottenere le dimensioni di un testo, ma arrivi al punto di creare un intero oggetto dell'interfaccia utente. Ed è sporco per questo motivo.


1
non dimenticare di impostarecollectionView.delegate == self // or whatever-object-which-do-it
Fitsyu

Bella risposta. Sebbene la dimensione che stavo ottenendo fosse un po 'più piccola del dovuto, su corde di diverse lunghezze, ho deciso di modificare un po' la dimensione da solo. L'aggiunta di ulteriori 5 punti alla larghezza ha funzionato:CGSize(width: title.size(withAttributes: [NSAttributedString.Key.font : UIFont.boldSystemFont(ofSize: 16)]).width + 5, height: 50)
Starsky il

37

Ho trovato un piccolo trucco per swift 4.2

Per larghezza dinamica e altezza fissa:

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

Per altezza dinamica e larghezza fissa:

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
Stai attento a usarlo. Creare e disegnare una nuova UILabel per ogni calcolo di cella è molto costoso.
AnthonyR

è necessario aggiungere UICollectionViewDelegateFlowLayout
cristianego

3
Per rispondere al commento sul fatto che è costoso creare un'etichetta fittizia, forse potresti creare un'etichetta fittizia invece di diverse. Tutto quello che vuoi veramente da esso è la dimensione del testo dagli attributi dell'etichetta. Alla fine della giornata, tuttavia, è essenzialmente lo stesso calcolato per la dimensione del testo tramite sizeWithAttributes, quindi forse questa è la risposta preferita.
Stephen Paul,

@ Hassan Grazie fratello ha funzionato per me, ma ho trovato un problema in larghezza, quindi ho aggiunto return CGSize (larghezza: label.frame.width + 50, altezza: 32). Poi ha funzionato, penso che questa risposta dovrebbe essere in cima alla lista.
Arshad Shaik

27

Controlla sotto il codice che potresti dare a CGSize molto breve.

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

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

@Vineesh TP Grazie per la tua risposta, controllerò sicuramente e ti farò sapere!
polpa

+1000 Questo funziona decisamente. Il tuo post e quello di Basheer dovrebbero avere un segno di spunta verde insieme.
procione roccioso

Qualche idea su come farlo con Swift 3?
UKDataGeek

1
Grazie uomo, è tempo di festa !!
Ravi

18

In Swift 3

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

14

Swift 4

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

0

// aggiungi nella vista didload

UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init];
    [layout setScrollDirection:UICollectionViewScrollDirectionHorizontal];
    layout.estimatedItemSize = CGSizeMake(self.breadScrumbCollectionView.frame.size.width, 30); 
self.breadScrumbCollectionView.collectionViewLayout = layout;
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.