I am trying to resize a table view cell "from the inside", using iOS 8, thus not implementing
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath;
but using:
self.tableView.rowHeight = UITableViewAutomaticDimension;
self.tableView.estimatedRowHeight = 200;
The cell has constraints top to bottom.
The cell contains (among other things) a UIWebview that knows its size asynchronously after load by asking the scrollview for its height:
CGFloat fittingHeight = self.messageTextWebView.scrollView.contentSize.height;
(this is a variant where the webview is contained within another view and that view resizes accordingly).
Because the whole auto layout process is finished when this happens, I set
[self setNeedsUpdateConstraints];
on the view in the cell which bubbles up to the cell and down again with layoutSubviews
.
In the end, I get the dreaded Unable to simultaneously satisfy constraints.
. The constraint that breaks everything is
"<NSLayoutConstraint:0x7f8c29d157f0 'UIView-Encapsulated-Layout-Height' V:[UITableViewCellContentView:0x7f8c2b063eb0(270)]>"
This constraint is created by the table view when the cell is being calculated for the first time.
It kills my own:
Will attempt to recover by breaking constraint
<NSLayoutConstraint:0x7fa8a8515b70 V:[UIView:0x7fa8aa0263e0(263)]>
Although I set my constraints' priorities to 1000 as well as compression resistance to 1000, I cannot override the constraint created by the table view.
What's frustrating: If I replace the web view with a simple multiline label and know all the sizes during the first cell layout, everything works perfectly. It's the asynchronous nature of the web view's loading system that forces me to change the layout after the cell is rendered for the first time.
Now my question:
Is there any way around this? I don't really want to reload the cell. If the cell is the only one on screen, it's not being reused and the whole thing starts over again. Is this even possible or does the UITableView architecture depend on that outer constraint to render itself correctly?
Just for the fun of it, I set
self.translatesAutoresizingMaskIntoConstraints = NO;
on the cell itself, resulting in a cell that has a size of 0,0 and the constraint:
"<NSLayoutConstraint:0x7f88506663a0 'UIView-Encapsulated-Layout-Height' V:[UITableViewCellContentView:0x7f8850648400(0)]>"
but it's still there...
thanks for your help.
BTW: I already read everything there is regarding the matter, including: Using Auto Layout in UITableView for dynamic cell layouts & variable row heights
Here's a UITableViewCell
subclass that manages a UIWebView
and adjusts its height correctly for a UITableView
set to use UITableViewAutomaticDimension
.
Mainly, maintain a NSLayoutConstraint
for the UIWebView
height. Update its constant in updateConstraints. Tell the parent tableview to recalc cell heights when there are changes.
@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
After some more research, my conclusion is:
Either use attributed strings that now allow HTML content (since iOS7):
[[NSAttributedString alloc] initWithData:[message dataUsingEncoding:NSUTF8StringEncoding]
options:
@{
NSDocumentTypeDocumentAttribute : NSHTMLTextDocumentType,
NSCharacterEncodingDocumentAttribute: [NSNumber numberWithInt:NSUTF8StringEncoding]
}
documentAttributes:nil error:&err];
The HTML parser is ok, limitations are
Or: A collection view might work but I haven't converted the table view yet - depending on my test results with NSAttributedString I might skip that...
Update
I tried using the fantastic https://github.com/TTTAttributedLabel/TTTAttributedLabel It renders attributed strings with the added bonus of detecting links. Which was everything I needed – almost. It doesn't render tags...
Got back to UITextView which surprisingly works just fine.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With