It appears Apple added an optimization which removes the detailTextLabel from the view hierarchy when the value is nil, and re-adds as necessary. Unfortunately in my tests, it gets added during a layout pass, but only after it would have been resized to fit the new text by that layout pass, so the size is still zero. The next layout pass (say on a rotation) then it will display OK.
One workaround may be to forcibly add the label back into the view hierarchy in all circumstances (seems like it should be OK if zero-sized). I'd be interested to see if dropping the below category into your app fixes things.
#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
There might be other approaches -- if you can call setNeedsLayout at the right time, it may force an additional layout pass which corrects things, but I was not able to find the right time for that.
EDIT: A comment below indicated that re-displayed cells could be an issue. So, a simpler fix may be to just swizzle layoutSubviews and do the check before calling Apple's implementation. That could solve all issues since it is during the layout call that the problem happens. So, below is that version of the fix -- I would be interested to see if that works.
#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
EDIT: It appears this bug is fixed in iOS9. So, the condition can be changed to:
if ([UIDevice currentDevice].systemVersion.intValue == 8)
If an application only needs to support iOS9 and above, the swizzle fix category can just be removed.