Ancho de celda dinámico de UICollectionView según el ancho de la etiqueta

94

Tengo un UICollectionView, que carga celdas de una celda reutilizable, que contiene una etiqueta. Una matriz proporciona contenido para esa etiqueta. Puedo cambiar el tamaño del ancho de la etiqueta dependiendo del ancho del contenido fácilmente con sizeToFit. Pero no puedo hacer que la celda se ajuste a la etiqueta.

Aqui esta el codigo

- (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;
}
pulpa
fuente
tipo de problema similar ... stackoverflow.com/questions/24915443/… ???
Fattie

Respuestas:

85

A sizeForItemAtIndexPathcambio del tamaño del texto

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

    return [(NSString*)[arrayOfStats objectAtIndex:indexPath.row] sizeWithAttributes:NULL];
}
Basheer_CAD
fuente
4
¡No te imaginas cómo te lo agradezco! Eso realmente funciona. Ahora solo necesito resolver el problema de [cell.myLabel sizeToFit], porque aparece en su tamaño completo solo después de desplazarme. Pero ni siquiera he estado cerca de tu solución.
pulpa
gracias por su ayuda, pero todavía tengo un problema. Cuando quito el comentario [cell.myLabel sizeToFit] tengo palabras truncadas y letras cortadas en la parte inferior, pero se vuelve correcto después de desplazarme (las palabras tienen su tamaño normal y las letras saltan un poco). Si comento y deshabilito el mensaje [cell.myLabel sizeToFit] (decidí jugar con IB y funciona bien) tengo palabras cortadas al final y al final. Hice una captura de pantalla goo.gl/HaoqQV. No es muy nítido en pantallas que no son retina, pero puede ver que las letras se cortaron. ¡Su sugerencia sobre cómo resolver será muy apreciada!
pulpa
2
en lugar de sizeToFit, use sizeWithAttributes para obtener el CGSize del texto, luego configure el marco de la etiqueta con un nuevo tamaño.
Basheer_CAD
gracias por la sugerencia, pero todavía tengo myLabel cortado en la parte inferior y al final. Tal vez me equivoque con la implementación de su sugerencia. Aquí está mi código
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; }
pulpa
50

Swift 4.2+

El principio es:

  1. Asegúrese de que la delegación esté configurada (p collectionView.delegate = self. Ej. )

  2. Implementar UICollectionViewDelegateFlowLayout(contiene la firma del método necesaria).

  3. collectionView...sizeForItemAtMétodo de llamada .

  4. No hay necesidad de puente de fundición Stringa NSStringal llamado size(withAttributes:método. Swift lo Stringtiene listo para usar.

  5. Los atributos son los mismos para los que configuró (NS)AttributedString, es decir, familia de fuentes, tamaño, peso, etc. Parámetro opcional.


Solución de muestra:

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

Pero lo más probable es que desee especificar atributos de cadena concretos respectivos a su celda, por lo que el retorno final se vería así:

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

¿Por qué NO DEBE utilizar UILabel para calcular el tamaño? Aquí está la solución sugerida:

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

Sí, obtienes el mismo resultado. Parece simplista y puede parecer una solución de referencia. Pero es incorrecto porque: 1) es caro, 2) elevado y 3) sucio.

Es caro porque UILabel es un objeto de interfaz de usuario complejo, que se crea en cada iteración cada vez que su celda está a punto de mostrarse, aunque no lo necesite aquí. Es una solución general porque solo necesita obtener el tamaño de un texto, pero llega a crear un objeto de interfaz de usuario completo. Y está sucio por esa razón.

Fuego de maleficio
fuente
1
no te olvides de configurarcollectionView.delegate == self // or whatever-object-which-do-it
Fitsyu
Gran respuesta. Aunque el tamaño que estaba obteniendo era un poco más pequeño de lo necesario, en diferentes longitudes de cuerdas, decidí modificar el tamaño yo mismo un poco. La adición de 5 puntos adicionales a la anchura hizo el truco:CGSize(width: title.size(withAttributes: [NSAttributedString.Key.font : UIFont.boldSystemFont(ofSize: 16)]).width + 5, height: 50)
Starsky
37

Encontré un pequeño truco para Swift 4.2

Para ancho dinámico y alto fijo:

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

Para altura dinámica y ancho fijo:

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
fuente
8
Tenga cuidado al usar esto. Crear y dibujar un nuevo UILabel para cada cálculo de celda es muy costoso.
AnthonyR
Necesito agregar UICollectionViewDelegateFlowLayout
cristianego
3
Para abordar el comentario acerca de que la creación de una etiqueta ficticia es costosa, tal vez pueda crear una etiqueta ficticia en lugar de varias. Todo lo que realmente quiere de él es el tamaño del texto de los atributos de la etiqueta. Sin embargo, al final del día, es esencialmente lo mismo que se calculó el tamaño del texto mediante sizeWithAttributes, por lo que tal vez esa sea la respuesta preferida.
Stephen Paul
@Hassan Gracias hermano, funcionó para mí, pero encontré un problema en el ancho, así que agregué return CGSize (ancho: label.frame.width + 50, height: 32). Entonces funcionó, creo que esta respuesta debería estar en la lista superior.
Arshad Shaik
27

Verifique el código a continuación que podría estar dando CGSize muy corto.

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

    NSString *testString = @"SOME TEXT";
    return [testString sizeWithAttributes:NULL];
}
Raza.najam
fuente
@Vineesh TP Gracias por tu respuesta, definitivamente lo revisaré y te lo haré saber.
pulpa
+1000 Este definitivamente funciona. La publicación tuya y la de Basheer deben tener una marca de verificación verde.
mapache rocoso
¿Alguna idea de cómo hacer esto con Swift 3?
UKDataGeek
1
¡Gracias, es hora de fiesta!
Ravi
18

En Swift 3

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

Rápido 4

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

// agregar en vista didload

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