Apple 似乎添加了一项优化,当值为 nil 时从视图层次结构中删除 detailTextLabel,并根据需要重新添加。不幸的是,在我的测试中,它是在布局传递期间添加的,但只有在 之后,它才会被调整大小以适应该布局传递的新文本,因此大小仍然为零。下一个布局通道(比如旋转)然后会显示 OK。
一种解决方法可能是在所有情况下强制将标签添加回视图层次结构(如果大小为零,似乎应该没问题)。我很想知道将以下类别放入您的应用程序是否可以解决问题。
#import <objc/runtime.h>
/*
* In iOS8 GM (and beta5, unsure about earlier), detail-type UITableViewCells (the
* ones which have a detailTextLabel) will remove that label from the view hierarchy if the
* text value is nil. It will get re-added during the cell's layoutSubviews method, but...
* this happens just too late for the label itself to get laid out, and its size remains
* zero. When a subsequent layout call happens (e.g. a rotate, or scrolling the cells offscreen
* and back) then things get fixed back up. However the initial display has completely blank
* values. To fix this, we forcibly re-add it as a subview in both awakeFromNib and prepareForReuse.
* Both places are necessary; one if the xib/storyboard has a nil value to begin with, and
* the other if code explicitly sets it to nil during one layout cycle then sets it back).
* Bug filed with Apple; Radar 18344249 .
*
* This worked fine in iOS7.
*/
@implementation UITableViewCell (IOS8DetailCellFix)
+ (void)load
{
if ([UIDevice currentDevice].systemVersion.intValue >= 8)
{
/* Swizzle the prepareForReuse method */
Method original = class_getInstanceMethod(self, @selector(prepareForReuse));
Method replace = class_getInstanceMethod(self, @selector(_detailfix_prepareForReuse));
method_exchangeImplementations(original, replace);
/*
* Insert an awakeFromNib implementation which calls super. If that fails, then
* UITableViewCell already has an implementation, and we need to swizzle it instead.
* In IOS8 GM UITableViewCell does not implement the method, but they could add one
* in later releases so be defensive.
*/
Method fixawake = class_getInstanceMethod(self, @selector(_detailfix_super_awakeFromNib));
if (!class_addMethod(self, @selector(awakeFromNib), method_getImplementation(fixawake), method_getTypeEncoding(fixawake)))
{
original = class_getInstanceMethod(self, @selector(_detailfix_awakeFromNib));
replace = class_getInstanceMethod(self, @selector(awakeFromNib));
method_exchangeImplementations(original, replace);
}
}
}
- (void)__detailfix_addDetailAsSubviewIfNeeded
{
/*
* UITableViewCell seems to return nil if the cell style does not have a detail.
* If it returns non-nil, force add it as a contentView subview so that it gets
* view layout processing at the right times.
*/
UILabel *detailLabel = self.detailTextLabel;
if (detailLabel != nil && detailLabel.superview == nil)
{
[self.contentView addSubview:detailLabel];
}
}
- (void)_detailfix_super_awakeFromNib
{
[super awakeFromNib];
[self __detailfix_addDetailAsSubviewIfNeeded];
}
- (void)_detailfix_awakeFromNib
{
[self _detailfix_awakeFromNib];
[self __detailfix_addDetailAsSubviewIfNeeded];
}
- (void)_detailfix_prepareForReuse
{
[self _detailfix_prepareForReuse];
[self __detailfix_addDetailAsSubviewIfNeeded];
}
@end
可能还有其他方法 - 如果您可以在正确的时间调用 setNeedsLayout,它可能会强制执行额外的布局传递来纠正问题,但我无法找到合适的时间。
编辑:下面的评论表明重新显示的单元格可能是一个问题。因此,一个更简单的解决方法可能是在调用 Apple 的实现之前调整 layoutSubviews 并进行检查。这可以解决所有问题,因为问题发生在布局调用期间。所以,下面是那个版本的修复——我很想看看它是否有效。
#import <objc/runtime.h>
@implementation UITableViewCell (IOS8DetailCellFix)
+ (void)load
{
if ([UIDevice currentDevice].systemVersion.intValue >= 8)
{
Method original = class_getInstanceMethod(self, @selector(layoutSubviews));
Method replace = class_getInstanceMethod(self, @selector(_detailfix_layoutSubviews));
method_exchangeImplementations(original, replace);
}
}
- (void)_detailfix_layoutSubviews
{
/*
* UITableViewCell seems to return nil if the cell type does not have a detail.
* If it returns non-nil, force add it as a contentView subview so that it gets
* view layout processing at the right times.
*/
UILabel *detailLabel = self.detailTextLabel;
if (detailLabel != nil && detailLabel.superview == nil)
{
[self.contentView addSubview:detailLabel];
}
[self _detailfix_layoutSubviews];
}
@end
编辑:看来这个错误在 iOS9 中已修复。所以,条件可以改为:
if ([UIDevice currentDevice].systemVersion.intValue == 8)
如果应用只需要支持 iOS9 及以上版本,则可以去掉 swizzle fix 类别。