我在UITableView中使用自动布局和大小类,其中的单元格根据其内容自行调整大小。为此,我使用的方法对于每种类型的单元格,保留该单元格的屏幕外实例,并使用systemLayoutSizeFittingSize
来确定正确的行高度 - 此方法在{{ 3}}和this StackOverflow post。
这很有用,直到我开始使用大小类。具体来说,我在常规宽度布局中为文本的边距约束定义了不同的常量,因此iPad上的文本周围有更多的空白。这给了我以下结果。
看起来新的约束集合得到了尊重(有更多的空白),但是行高计算仍然返回与没有应用特定于大小类的单元格相同的值。限制。 屏幕外单元格中某些部分的布局过程未考虑窗口的尺寸等级。
现在我认为这可能是因为屏幕外视图 没有超视图或窗口,因此它没有任何大小类特征可供参考发生systemLayoutSizeFittingSize
调用(即使它似乎确实使用了边距的调整约束)。我现在通过在创建UIWindow之后添加屏幕外的大小调整单元作为UIWindow的子视图来解决这个问题,这会产生所需的结果:
以下是我在代码中所做的事情:
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
}
向窗口添加不应该被绘制的视图对我来说似乎是一个黑客。 有没有办法让UIViews完全采用窗口的大小类,即使它们当前不在视图层次结构中?或者还有其他我不知道的东西?感谢。
答案 0 :(得分:8)
Apple现在不鼓励覆盖-traitCollection
。请考虑使用其他解决方法。来自the doc:
重要
直接使用
traitCollection
属性。不要覆盖它。不要提供自定义实现。
existing answer很棒。它解释了问题是:
-dequeueReusableCellWithIdentifier:
返回没有有效cell.traitCollection
和cell.traitCollection
是readonly
。建议的解决方法是将单元格临时添加到表格视图中。但是,如果我们在-viewDidLoad
,其中表视图的traitCollection
或视图控制器的视图,甚至视图控制器本身无效,那么这不起作用。爱好。
在这里,我提出了另一种解决方法,即覆盖单元格的traitCollection
。为此:
为单元格(您可能已经执行过)创建UITableViewCell
的自定义子类。
在自定义子类中,添加- (UITraitCollection *)traitCollection
方法,该方法将覆盖traitCollection
属性的getter。现在,您可以返回您喜欢的任何有效UITraitCollection
。这是一个示例实现:
// Override getter of traitCollection property
// https://stackoverflow.com/a/28514006/1402846
- (UITraitCollection *)traitCollection
{
// Return original value if valid.
UITraitCollection* originalTraitCollection = [super traitCollection];
if(originalTraitCollection && originalTraitCollection.userInterfaceIdiom != UIUserInterfaceIdiomUnspecified)
{
return originalTraitCollection;
}
// Return trait collection from UIScreen.
return [UIScreen mainScreen].traitCollection;
}
或者,您可以返回使用any one of its create methods创建的合适UITraitCollection
,例如:
+ (UITraitCollection *)traitCollectionWithDisplayScale:(CGFloat)scale
+ (UITraitCollection *)traitCollectionWithTraitsFromCollections:(NSArray *)traitCollections
+ (UITraitCollection *)traitCollectionWithUserInterfaceIdiom:(UIUserInterfaceIdiom)idiom
+ (UITraitCollection *)traitCollectionWithHorizontalSizeClass:(UIUserInterfaceSizeClass)horizontalSizeClass
+ (UITraitCollection *)traitCollectionWithVerticalSizeClass:(UIUserInterfaceSizeClass)verticalSizeClass
或者,您甚至可以通过以下方式使其更加灵活:
// Override getter of traitCollection property
// https://stackoverflow.com/a/28514006/1402846
- (UITraitCollection *)traitCollection
{
// Return overridingTraitCollection if not nil,
// or [super traitCollection] otherwise.
// overridingTraitCollection is a writable property
return self.overridingTraitCollection ?: [super traitCollection];
}
此解决方法与iOS 7兼容,因为traitCollection
属性是在iOS 8+中定义的,因此,在iOS 7中,没有人会调用其getter,因此也就是我们的首要方法。
答案 1 :(得分:5)
我花了几天时间才开始使用尺寸类来改变iPad上的字体大小,而不是iPhone等。
问题的根源似乎是dequeueReusableCellWithIdentifier:
返回一个没有超视图的单元格,从中获取其UITraitCollection
。另一方面,dequeueReusableCellWithIdentifier:forIndexPath:
会返回超级视图为UITableViewWrapperView
的单元格。
我已经向Apple提出了一个错误报告,因为他们没有扩展此方法以支持大小类;似乎没有记录如何处理iOS7上的大小类。当您向UITableView
发送要求单元格的消息时,它应该返回一个反映您要发送消息的表的大小类的消息。这是dequeueReusableCellWithIdentifier:forIndexPath:
的情况。
我还注意到,在尝试使用新的自动布局机制时,您经常需要在viewDidAppear:
中重新加载表以使新机制正常工作。没有这个,我看到了使用iOS7方法的相同问题。
据我所知,似乎无法在iOS8上使用自动布局,也无法使用相同代码的iOS7旧机制。
现在,我不得不通过添加原型单元作为表的子视图,进行大小计算,然后将其删除来解决问题:
UITableViewCell *prototype=nil;
CGFloat prototypeHeight=0.0;
prototype=[self.tableView dequeueReusableCellWithIdentifier:@"SideMenuCellIdentifier"];
// Check for when the prototype cell has no parent view from
// which to inherit size class related constraints.
BOOL added=FALSE;
if (prototype.superview == nil){
[self.tableView addSubview:prototype];
added=TRUE;
}
<snip ... Setup prototype cell>
[prototype setNeedsLayout];
[prototype layoutIfNeeded];
CGSize size = [prototype.contentView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize];
prototypeHeight=size.height+1; // Add one for separator
// Remove the cell if added. Leaves it when in iOS7.
if (added){
[prototype removeFromSuperview];
}
与大小类相关的设置似乎是通过UITraitCollection
来控制的,UIViewController
是{{1}}的只读属性。对于iOS7向后兼容性,这似乎由构建系统处理,作为一些解决方案。即在iOS7上,您无法访问traitCollection
property,但您可以在iOS8中访问。
鉴于与故事板中视图控制器的紧密耦合以及向后兼容性如何工作,看起来原型单元必须位于您在Xcode中定义的视图控制器的层次结构中。
这里有一个讨论:
How can Xcode 6 adaptive UIs be backwards-compatible with iOS 7 and iOS 6?