it-swarm.com.ru

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

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

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

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

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

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

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

Заметка

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

http://t.co/PdUywcTjhu

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

21
Adam Waite

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

[РЕДАКТИРОВАТЬ] Если содержимое постоянно меняется, вы можете реализовать метод, который обновляет массив высот при предоставлении новых данных.

19
Nyth

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

Objective-C:

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

Swift:

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

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

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

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

С наилучшими пожеланиями, Александр.

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

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

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

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

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

3
Daniel Saidi

Вот код, который я использовал для динамической высоты ячейки при получении твитов из Twitter и их сохранении в 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;
}
3
Joseph DeCarlo

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

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

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

3) вызов [tableview reloadData] 

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

3
alex

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

- (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;
1
iOS

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

-(CGFloat)cellHeightWithData:(id)data{
    CGFloat height = [[self contentView] systemLayoutSizeFittingSize:UILayoutFittingCompressedSize].height;
    [self fillCellWithData:data]; //set the label's 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, используя пустую ячейку.

примечание: это работает только с autolayout, но это также работает с ios 7 и позже.

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

0
user2387149

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

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

tableView.estimatedRowHeight = 68.0
tableView.rowHeight = UITableViewAutomaticDimension

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

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

У меня был очень большой тест в 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;}
0
GSK