Динамическая ширина ячейки UICollectionView в зависимости от ширины метки

94

У меня есть 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;
}
мякоть
источник
подобная проблема ... stackoverflow.com/questions/24915443/… ???
Fattie

Ответы:

85

В sizeForItemAtIndexPathвернуть размер текста

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

    return [(NSString*)[arrayOfStats objectAtIndex:indexPath.row] sizeWithAttributes:NULL];
}
Basheer_CAD
источник
4
Вы не представляете, как я вас благодарю! Это действительно работает. Теперь мне нужно только решить проблему [cell.myLabel sizeToFit], потому что он появляется в полном размере только после прокрутки. Но я не был даже близко к вашему решению.
pulp
спасибо за вашу помощь, но у меня все еще есть одна проблема. Когда я раскомментирую [cell.myLabel sizeToFit], слова у меня усекаются, а буквы обрезаются внизу, но после прокрутки все становится нормально (слова имеют нормальный размер, а буквы немного подскакивают). Если я прокомментирую и отключу сообщение [cell.myLabel sizeToFit] (я решил поиграть с IB, и он отлично работает), у меня будут вырезаны слова в конце и внизу. Я сделал снимок экрана goo.gl/HaoqQV. Он не очень резкий на дисплеях без сетчатки, но вы можете видеть, что буквы обрезаны. Мы будем очень благодарны за ваше предложение о том, как решить!
pulp
2
вместо sizeToFit используйте sizeWithAttributes, чтобы получить CGSize текста, а затем установите новый размер рамки метки.
Basheer_CAD
Спасибо за предложение, но у меня все еще есть myLabel, вырезанный внизу и в конце. Может я ошибаюсь в реализации вашего предложения. Вот мой код
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; }
pulp
50

Swift 4.2+

Принцип такой:

  1. Убедитесь, что делегирование настроено (например collectionView.delegate = self)

  2. Реализовать UICollectionViewDelegateFlowLayout(содержит необходимую сигнатуру метода).

  3. collectionView...sizeForItemAtМетод вызова .

  4. Нет необходимости использовать мост Stringдля NSStringвызова 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 - это сложный объект пользовательского интерфейса, который создается на каждой итерации, когда ваша ячейка собирается показывать, даже если она вам здесь не нужна. Это накладное решение, потому что вам нужно получить только размер текста, но вы можете создать целый объект пользовательского интерфейса. И по этой причине он грязный.

Hexfire
источник
1
не забудьте установитьcollectionView.delegate == self // or whatever-object-which-do-it
Фитсю
Отличный ответ. Хотя размер, который я получал, был немного меньше, чем должен был быть, на разных длинах строк я решил сам немного изменить размер. Добавление дополнительных 5 пунктов к ширине помогло:CGSize(width: title.size(withAttributes: [NSAttributedString.Key.font : UIFont.boldSystemFont(ofSize: 16)]).width + 5, height: 50)
Старски
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
Чтобы ответить на комментарий о том, что создание фиктивной метки дорого, возможно, вы могли бы просто создать одну фиктивную метку вместо нескольких. Все, что вам действительно нужно, это размер текста из атрибутов метки. В конце концов, это, по сути, то же самое, что и вычисленный размер текста sizeWithAttributes, так что, возможно, это предпочтительный ответ.
Стивен Пол
@Hassan Спасибо, брат, это сработало для меня, но я обнаружил проблему с шириной, поэтому добавил return 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];
}
Raza.najam
источник
@Vineesh TP Спасибо за ответ, обязательно проверю и дам знать!
pulp
+1000 Это определенно работает. У вашего сообщения и сообщения Башира должна быть зеленая галочка.
rocky
Есть идеи, как это сделать с помощью Swift 3?
UKDataGeek
1
Слава Богу, время для вечеринки !!
Рави
18

В 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

// добавляем в просмотр didload

UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init];
    [layout setScrollDirection:UICollectionViewScrollDirectionHorizontal];
    layout.estimatedItemSize = CGSizeMake(self.breadScrumbCollectionView.frame.size.width, 30); 
self.breadScrumbCollectionView.collectionViewLayout = layout;
Апурва Гайквад
источник