Изменять размер UITableViewCell асинхронно и сгенерировать UIView-Encapsulated-Layout-Height

Я пытаюсь изменить размер ячейки представления таблицы "изнутри", используя iOS 8, тем самым не реализуя

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

но используя:

self.tableView.rowHeight = UITableViewAutomaticDimension;
self.tableView.estimatedRowHeight = 200;

Ячейка имеет ограничения сверху вниз.

Ячейка содержит (среди прочего) UIWebview, который знает свой размер асинхронно после загрузки, запрашивая scrollview для его высоты:

CGFloat fittingHeight = self.messageTextWebView.scrollView.contentSize.height;

(это вариант, когда веб-просмотр содержится в другом представлении и этот вид соответственно изменяется).

Поскольку весь процесс автоматического макетирования завершается, когда это происходит, я устанавливаю

[self setNeedsUpdateConstraints];

на вид в ячейке, который пузырится до ячейки и снова вниз с помощью layoutSubviews.

В конце концов, я получаю страшный Unable to simultaneously satisfy constraints.. Ограничение, которое разбивает все,

"<NSLayoutConstraint:0x7f8c29d157f0 'UIView-Encapsulated-Layout-Height' V:[UITableViewCellContentView:0x7f8c2b063eb0(270)]>"

Это ограничение создается представлением таблицы, когда ячейка вычисляется в первый раз.

Он убивает меня:

Will attempt to recover by breaking constraint
<NSLayoutConstraint:0x7fa8a8515b70 V:[UIView:0x7fa8aa0263e0(263)]>

Хотя я установил приоритеты своих ограничений на 1000, а также сопротивление сжатию до 1000, я не могу переопределить ограничение, созданное табличным представлением.

Что расстраивает: Если я заменю веб-представление простой многострочной меткой и знаю все размеры во время первой компоновки ячеек, все работает отлично. Это асинхронный характер системы загрузки веб-представлений, которая заставляет меня изменить макет после того, как ячейка отображается в первый раз.

Теперь мой вопрос:

Есть ли способ обойти это? Я действительно не хочу перезагружать камеру. Если ячейка является единственной на экране, она не используется повторно, и все начинается снова. Возможно ли это, или архитектура UITableView зависит от этого внешнего ограничения, чтобы сделать его правильно?

Просто для удовольствия, я установил

self.translatesAutoresizingMaskIntoConstraints = NO;

в самой ячейке, в результате чего ячейка имеет размер 0,0 и ограничение:

"<NSLayoutConstraint:0x7f88506663a0 'UIView-Encapsulated-Layout-Height' V:[UITableViewCellContentView:0x7f8850648400(0)]>"

но он все еще там...

спасибо за вашу помощь.

Кстати: я уже прочитал все, что касается этого вопроса, в том числе: Использование автоматической компоновки в UITableView для динамических раскладок ячеек и высоты строк в строке

Ответ 1

После еще нескольких исследований мой вывод:

Либо используют атрибутные строки, которые теперь позволяют содержимое HTML (начиная с iOS7):

[[NSAttributedString alloc] initWithData:[message dataUsingEncoding:NSUTF8StringEncoding]
options:
@{
     NSDocumentTypeDocumentAttribute : NSHTMLTextDocumentType,
     NSCharacterEncodingDocumentAttribute: [NSNumber numberWithInt:NSUTF8StringEncoding] 
} 
documentAttributes:nil error:&err];

Парсер HTML в порядке, ограничения

  • вы никогда не должны использовать теги src, ссылающиеся на контент в сети. Содержимое загружается в основной поток. Я полагаю, что они должны были сделать это для принудительного синхронного рендеринга.
  • нет взаимодействия (что на самом деле хорошо).

Или: Просмотр коллекции может работать, но я еще не преобразовал представление таблицы - в зависимости от результатов моего теста с NSAttributedString я мог бы пропустить это...

Обновление

Я попытался использовать фантастический https://github.com/TTTAttributedLabel/TTTAttributedLabel Он отображает привязанные строки с добавленным бонусом обнаружения ссылок. Это было все, что мне было нужно - почти. Он не отображает теги...

Вернемся к UITextView, который на удивление отлично работает.

Ответ 2

Здесь a UITableViewCell подкласс, который управляет UIWebView и правильно настраивает его высоту для набора UITableView для использования UITableViewAutomaticDimension.

В основном, поддерживайте NSLayoutConstraint для высоты UIWebView. Обновите его константу в updateConstraints. Скажите родительскому табличному представлению, чтобы перераспределить высоту ячеек, когда есть изменения.

@implementation WebTableViewCell
{
    IBOutlet UIWebView*             _webview; // glued to cell content view using edge constraints

    IBOutlet NSLayoutConstraint*    _heightConstraint; // height constraint for the webview itself
}

- (void) awakeFromNib
{
    _webview.scrollView.scrollEnabled = NO;

    // use this to test various document heights
    //  [_webview loadHTMLString: @"<html><body style='height:100px;'>Hello, World</body></html>" baseURL: nil];

    // or, a real web page
    [_webview loadRequest: [NSURLRequest requestWithURL: [NSURL URLWithString: @"http://stackoverflow.com/users/291788/tomswift"]]];
}

- (void) updateConstraints
{
    [super updateConstraints];

    // get the document height.
    CGFloat height = [[_webview stringByEvaluatingJavaScriptFromString: @"document.height"] floatValue];
    if ( _heightConstraint.constant != height )
    {
        // update
        _heightConstraint.constant = height;

        // ask the tableview to recalc heights
        dispatch_async(dispatch_get_main_queue(), ^{

            UITableView* tableView = (UITableView*)self.superview;
            while ( tableView != nil && ![tableView isKindOfClass: [UITableView class]] )
                tableView = (UITableView*)tableView.superview;


            [tableView beginUpdates];
            [tableView endUpdates];
        });
    }
}

- (void) webViewDidFinishLoad:(UIWebView *)webView
{
    [self setNeedsUpdateConstraints];
}

@end