UITableViewCell的字幕不会更新

lup*_*upz 33 ios xcode6 ios8

今天我开始为iOS8准备好我的应用程序.我发现我的UITableCells的字幕不会更新viewWillAppear.我把它归结为一个最小的例子:

我有一个带有2个单元格的静态单元格TableViewController(style = subtitle)一个副标题是空的,另一个是设置的.

界面构建器screenchot

我像这样更新字幕:

- (void) viewWillAppear:(BOOL)animated
{
  [super viewWillAppear:animated];
  [[self.without detailTextLabel] setText:@"foobar"];
  [[self.with detailTextLabel] setText:@"barfoo"];
}
Run Code Online (Sandbox Code Playgroud)

虽然Everythin在iOS7(以及6和5)下工作,但iOS8不会更新第一个单元格的标题.

模拟器的截图

但是,当我触摸单元格时,它将更新并显示文本.

这是一个模拟器问题吗?一个bug?难道我做错了什么?

Ace*_*ceN 48

作为临时工作,我简单地在文本标签中引入了一个空格,并以编程方式将标签文本设置为nil,我将其设置为空白

cell.detailTextLabel.text = @" ";
Run Code Online (Sandbox Code Playgroud)

相应地调整逻辑以处理单独的空白区域为零.

对我来说,即使在加载viewcontroller之前我已经为它准备好了一个字符串,详细文本标签也没有显示出来.每当视图控制器出现时,我的许多单元格中的一个都会将详细文本标签中的当前日期显示为默认值,但这也不起作用.

我认为这与iOS 8最初设置为nil时无法更新文本标签文本的事实有关.

因此,请在界面构建器中确保在原型单元格中引入空格.令人惊讶的是,我的自定义单元格标签中的文字正常工作.

  • @AceNeerav detailTextLabel肯定会从视图层次结构中删除.在它不工作的情况下,尝试查看cell.detailTextLabel.superview.它在layoutSubviews期间重新添加到视图层次结构中,但它最初保持为零大小(其大小为零文本),因为自动布局尚未调整大小.在*next*布局传递期间(例如通过选择单元格或旋转),它会正确调整大小并显示出来.我的雷达(18344249)有一个演示应用程序.我没有看到textLabel的问题,但它始终是可能的. (3认同)

Car*_*erg 15

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
Run Code Online (Sandbox Code Playgroud)

可能还有其他方法 - 如果你可以在正确的时间调用setNeedsLayout,它可能会强制一个额外的布局传递来纠正事情,但我无法找到合适的时间.

编辑:下面的评论表明,重新显示细胞可能是一个问题.因此,更简单的解决方法可能是调用layoutSubviews并在调用Apple的实现之前进行检查.这可以解决所有问题,因为在布局调用期间问题发生了.所以,下面是该版本的修复 - 我很想知道它是否有效.

#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
Run Code Online (Sandbox Code Playgroud)

编辑:似乎这个错误在iOS9中得到修复.因此,条件可以更改为:

if ([UIDevice currentDevice].systemVersion.intValue == 8)
Run Code Online (Sandbox Code Playgroud)

如果应用程序只需要支持iOS9及更高版本,则可以删除swizzle修复类别.