UITableViewCell的字幕不会更新

时间:2014-09-11 16:54:46

标签: ios ios8 xcode6

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

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

Interface builder screenschot

我像这样更新字幕:

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

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

screenshot of simulator

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

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

6 个答案:

答案 0 :(得分:47)

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

cell.detailTextLabel.text = @" ";

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

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

我认为这与iOS 8无法更新文本标签文本(如果最初设置为nil)的事实有关。

所以请注意,在界面构建器中也会在原型单元格中引入一个空格。我的自定义单元格的标签中的文字工作正常。

答案 1 :(得分: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

可能还有其他方法 - 如果你可以在合适的时间调用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
编辑:在iOS9中修复了此错误。因此,条件可以更改为:

if ([UIDevice currentDevice].systemVersion.intValue == 8)

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

答案 2 :(得分:4)

几天过去了,我想我必须使用解决方法:

- (void) viewDidAppear:(BOOL)animated
{
  [super viewDidAppear:animated];
  [self.without setNeedsLayout];
}

这有明显的延迟,但这是我能找到的唯一方法,使文字在iOS8上显示和可读。

答案 3 :(得分:3)

确认。如果将其初始化为nil,则iOS8不会绘制detailText。这条简单的线应该可以解决问题。

self.detailTextLabel.attributedText = [[NSAttributedString alloc] initWithString:@" "];

答案 4 :(得分:1)

我遇到了同样的问题,我在cellForRowAtIndexPath之前调用cell.layoutSubviews()返回单元格。它解决了这个问题。

答案 5 :(得分:1)

我在iOS8上看到了这个问题的两个原因。

  1. 如果将详细信息标签中的文本设置为nil或“”(完成此操作的最常见位置是prepareForReuse和/或从故事板中删除默认文本) [如上面的答案中所示,此问题已在iOS9中解决]

  2. 如果您子类化字幕类型的UITableViewCell,然后以编程方式注册您在故事板中创建的单元格。 [这是编程错误,您无需注册在故事板中创建的原型单元格]