I am using Auto Layout and size classes inside a UITableView with cells that self-size based on their content. For this I'm using the method where for each type of cell, you keep an offscreen instance of that cell around and use systemLayoutSizeFittingSize
on that to determine the correct row height - this method is explained wonderfully in this StackOverflow post and elsewhere.
This worked great until I started using size classes. Specifically I've defined different constants on the margin constraints for text in Regular Width layouts, so there is more whitespace around the text on iPad. This gives me the following results.
It appears that the new set of constraints is being honored (there is more whitespace), but that the row height calculation still returns the same value as it would for a cell that didn't apply the size class-specific constraints. Some part of the layout process in the offscreen cell is not taking the window's size class into account.
Now I figured that that's probably because the offscreen view has no superview or window, and as such it doesn't have any size class traits to refer to at the point the systemLayoutSizeFittingSize
call occurs (even though it does seem to use the adjusted constraints for the margins). I now work around this by adding the offscreen sizing cell as a subview of the UIWindow after it's created, which gives the desired result:
Here's what I'm doing in code:
func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
let contentItem = content[indexPath.item]
if let contentType = contentItem["type"] {
// Get or create the cached layout cell for this cell type.
if layoutCellCache.indexForKey(contentType) == nil {
if let cellIdentifier = CellIdentifiers[contentType] {
if var cachedLayoutCell = dequeueReusableCellWithIdentifier(cellIdentifier) as? UITableViewCell {
UIApplication.sharedApplication().keyWindow?.addSubview(cachedLayoutCell)
cachedLayoutCell.hidden = true
layoutCellCache[contentType] = cachedLayoutCell
}
}
}
if let cachedLayoutCell = layoutCellCache[contentType] {
// Configure the layout cell with the requested cell's content.
configureCell(cachedLayoutCell, withContentItem: contentItem)
// Perform layout on the cached cell and determine best fitting content height.
cachedLayoutCell.bounds = CGRectMake(0.0, 0.0, CGRectGetWidth(tableView.bounds), 0);
cachedLayoutCell.setNeedsLayout()
cachedLayoutCell.layoutIfNeeded()
return cachedLayoutCell.contentView.systemLayoutSizeFittingSize(UILayoutFittingCompressedSize).height
}
}
fatalError("not enough information to determine cell height for item \(indexPath.item).")
return 0
}
Adding views to the window that aren't ever supposed to be drawn seems like a hack to me. Is there a way to have UIViews fully adopt the window's size class even when they're not currently in the view hierarchy? Or is there something else I'm missing? Thanks.
UITableViewController+TraitCollectionOverride.h
– Yellowthroat