Подтвердить что ты не робот

Динамическая высота ячейки UITableView на основе содержимого

У меня есть UITableView который заполнен пользовательскими ячейками (унаследованными от UITableViewCell), каждая ячейка содержит UIWebView который автоматически изменяет размер в зависимости от его содержимого. Здесь дело в том, как я могу изменить высоту ячеек UITableView зависимости от их содержимого (переменная webView).

Решение должно быть динамическим, поскольку HTML- UIWebViews используемый для заполнения UIWebViews, анализируется из постоянно меняющегося канала.

У меня есть ощущение, что мне нужно использовать метод делегата UITableView heightForRowAtIndexPath но из него определение:

    - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    ;//This needs to be variable
}

Я не могу получить доступ к ячейке или ее содержимому. Могу ли я изменить высоту ячейки в cellForRowAtIndexPath?

Любая помощь будет грандиозной. Благодарю.

Заметка

Я задал этот вопрос более 2 лет назад. С введением автоматической верстки лучшее решение для iOS7 можно найти:

Использование Auto Layout в UITableView для динамического размещения ячеек и переменной высоты строк

и на iOS8 эта функциональность встроена в SDK

4b9b3361

Ответ 1

Лучший способ, который я нашел для динамической высоты, - это предварительно рассчитать высоту и сохранить ее в некоторой коллекции (возможно, в массиве). Предполагая, что ячейка содержит в основном текст, вы можете использовать -[NSString sizeWithFont:constrainedToSize:lineBreakMode:] чтобы вычислить высоту, а затем вернуть соответствующее значение в heightForRowAtIndexPath:

Если содержимое постоянно меняется, вы можете реализовать метод, который обновляет массив высот при предоставлении новых данных.

Ответ 2

Это обычно работает очень хорошо:

Objective-C:

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
    return UITableViewAutomaticDimension;
}

Swift:

override func tableView(tableView: UITableView!, heightForRowAtIndexPath indexPath: NSIndexPath!) -> CGFloat {
    return UITableViewAutomaticDimension;
}

Ответ 3

self.tblVIew.estimatedRowHeight = 500.0; // put max you expect here.
self.tblVIew.rowHeight = UITableViewAutomaticDimension;

Ответ 4

Я перепробовал много решений, но одно из них сработало, предложенное другом:

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {

    int height = [StringUtils findHeightForText:yourLabel havingWidth:yourWidth andFont:[UIFont systemFontOfSize:17.0f]];

    height += [StringUtils findHeightForText:yourOtherLabel havingWidth:yourWidth andFont:[UIFont systemFontOfSize:14.0f]];

    return height + CELL_SIZE_WITHOUT_LABELS; //important to know the size of your custom cell without the height of the variable labels
}

Класс StringUtils.h:

#import <Foundation/Foundation.h>

@interface StringUtils : NSObject

+ (CGFloat)findHeightForText:(NSString *)text havingWidth:(CGFloat)widthValue andFont:(UIFont *)font;

@end

Класс StringUtils.m:

#import "StringUtils.h"

@implementation StringUtils

+ (CGFloat)findHeightForText:(NSString *)text havingWidth:(CGFloat)widthValue andFont:(UIFont *)font {
    CGFloat result = font.pointSize+4;
    if (text) {
        CGSize size;

        CGRect frame = [text boundingRectWithSize:CGSizeMake(widthValue, CGFLOAT_MAX)
                                          options:NSStringDrawingUsesLineFragmentOrigin
                                       attributes:@{NSFontAttributeName:font}
                                          context:nil];
        size = CGSizeMake(frame.size.width, frame.size.height+1);
        result = MAX(size.height, result); //At least one row
    }
    return result;
}

@end

Это отлично сработало для меня. У меня была пользовательская ячейка с 3 изображениями с фиксированными размерами, 2 этикетками с фиксированными размерами и 2 переменными метками.

Ответ 5

Вот код, который я использовал для динамической высоты ячейки при извлечении твитов из твиттера, а затем храня их в CoreData для автономного чтения.

Это не только показывает, как получить содержимое ячеек и данных, но также и динамически размер UILabel для контента с дополнением

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {

    Tweet *tweet = [self.fetchedResultsController objectAtIndexPath:indexPath];

    NSString* text = tweet.Text;

    TweetTableViewCell *cell = (TweetTableViewCell*)[self tableView:tableView cellForRowAtIndexPath:indexPath];

    //Set the maximum size
    CGSize maximumLabelSize = cell.tweetLabel.frame.size;
    CGPoint originalLocation = cell.tweetLabel.frame.origin;

    //Calculate the new size based on the text
    CGSize expectedLabelSize = [text sizeWithFont:cell.tweetLabel.font constrainedToSize:maximumLabelSize lineBreakMode:cell.tweetLabel.lineBreakMode];


    //Dynamically figure out the padding for the cell
    CGFloat topPadding = cell.tweetLabel.frame.origin.y - cell.frame.origin.y;


    CGFloat bottomOfLabel = cell.tweetLabel.frame.origin.y + cell.tweetLabel.frame.size.height;
    CGFloat bottomPadding = cell.frame.size.height - bottomOfLabel;


    CGFloat padding = topPadding + bottomPadding;


    CGFloat topPaddingForImage = cell.profileImage.frame.origin.y - cell.frame.origin.y;
    CGFloat minimumHeight = cell.profileImage.frame.size.height + topPaddingForImage + bottomPadding;

    //adjust to the new size
    cell.tweetLabel.frame = CGRectMake(originalLocation.x, originalLocation.y, cell.tweetLabel.frame.size.width, expectedLabelSize.height);


    CGFloat cellHeight = expectedLabelSize.height + padding;

    if (cellHeight < minimumHeight) {

        cellHeight = minimumHeight;
    }

    return cellHeight;
}

Ответ 6

Также я думаю, что такой алгоритм вам подойдет:

1) в cellForrowAtIndexPath вы активируете свои веб-просмотры для загрузки и даете им теги, равные indexPath.row

2) в webViewDidFinishLoading вы вычисляете высоту содержимого в ячейке и составляете словарь с ключами и значениями, такими как: key = indexPath.row value = height

3) вызвать [tableview reloadData]

4) в [tableview cellForRowAtIndexPath: indexPath] установите правильные высоты для соответствующих ячеек

Ответ 7

Большая проблема с ячейками с динамической высотой в iOS заключается в том, что таблица vc должна вычислять и возвращать высоту каждой ячейки до того, как клетки будут нарисованы. Тем не менее, перед тем, как нарисовать ячейку, она не имеет рамки и, следовательно, не имеет ширины. Это вызывает проблему, если ваша ячейка должна изменить ее высоту на основе, скажем, количества текста в textLabel, так как вы не знаете его ширины.

Общим решением, которое я видел, является то, что люди определяют числовое значение для ширины ячейки. Это плохой подход, поскольку таблицы могут быть простыми или сгруппированными, использовать стили для iOS 7 или iOS 6, отображаться на iPhone или iPad, в ландшафтном или портретном режиме и т.д.

Я боролся с этими проблемами в моем приложении iOS, которое поддерживает iOS5 + и iPhone и iPad с несколькими ориентациями. Мне нужен был удобный способ автоматизировать это и оставить логику из контроллера вида. Результатом стал подкласс UITableViewController (так что он может содержать состояние), который поддерживает ячейки по умолчанию (стиль Default и Subtitle), а также пользовательские ячейки.

Вы можете захватить его в GitHub (https://github.com/danielsaidi/AutoSizeTableView). Надеюсь, это поможет тем из вас, кто все еще борется с этой проблемой. Если вы это сделаете, я бы хотел услышать, что вы думаете, и если это сработает для вас.

Ответ 8

Это одно из моих хороших решений. это сработало для меня.

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    cell.textLabel.text = [_nameArray objectAtIndex:indexPath.row];
    cell.textLabel.numberOfLines = 0;
    cell.textLabel.lineBreakMode = NSLineBreakByWordWrapping;
}

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
    return UITableViewAutomaticDimension;
}

Нам нужно применить эти 2 изменения.

1)cell.textLabel.numberOfLines = 0;
  cell.textLabel.lineBreakMode = NSLineBreakByWordWrapping;

2)return UITableViewAutomaticDimension;

Ответ 9

Я всегда реализую это во всех своих ячейках в суперэлементном классе, потому что по какой-то причине UITableViewAutomaticDimension работает не так хорошо.

-(CGFloat)cellHeightWithData:(id)data{
    CGFloat height = [[self contentView] systemLayoutSizeFittingSize:UILayoutFittingCompressedSize].height;
    [self fillCellWithData:data]; //set the label text or anything that may affect the size of the cell
    [self layoutIfNeeded];
    height = [[self contentView] systemLayoutSizeFittingSize:UILayoutFittingCompressedSize].height;
    return height+1; //must add one because of the cell separator
}

просто вызовите этот метод на -(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath с помощью фиктивной ячейки.

note: это работает только с автозапуском, но также работает с ios 7 и более поздними версиями.

pd: не забудьте установить флажок на xib или раскадровке для "предпочтительной ширины" и установить статическую ширину (в меню cmd + alt + 5)

Ответ 10

Swift Используйте пользовательские ячейки и метки. Настройте ограничения для UILabel. (верхний, левый, нижний, правый) Установите строки UILabel на 0

Добавьте следующий код в метод viewDidLoad для ViewController:

tableView.estimatedRowHeight = 68.0
tableView.rowHeight = UITableViewAutomaticDimension

//Делегирование и источник данных

override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
    return UITableViewAutomaticDimension;
}

Ответ 11

В UILabel у меня был очень большой тест. Прежде всего, не удается работать, тогда я создаю категорию для строки, как показано ниже, и получаю точную высоту

- (CGFloat)heightStringWithEmojifontType:(UIFont *)uiFont ForWidth:(CGFloat)width {

// Get text
CFMutableAttributedStringRef attrString = CFAttributedStringCreateMutable(kCFAllocatorDefault, 0);
CFAttributedStringReplaceString (attrString, CFRangeMake(0, 0), (CFStringRef) self );
CFIndex stringLength = CFStringGetLength((CFStringRef) attrString);

// Change font
CTFontRef ctFont = CTFontCreateWithName((__bridge CFStringRef) uiFont.fontName, uiFont.pointSize, NULL);
CFAttributedStringSetAttribute(attrString, CFRangeMake(0, stringLength), kCTFontAttributeName, ctFont);

// Calc the size
CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString(attrString);
CFRange fitRange;
CGSize frameSize = CTFramesetterSuggestFrameSizeWithConstraints(framesetter, CFRangeMake(0, 0), NULL, CGSizeMake(width, CGFLOAT_MAX), &fitRange);

CFRelease(ctFont);
CFRelease(framesetter);
CFRelease(attrString);

return frameSize.height + 10;}

Ответ 12

В Swift 4+ вы можете установить его динамический

override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
    return UITableView.automaticDimension
}