Субтитры UITableViewCell не будут обновляться

Сегодня я начал готовить свои приложения для iOS8. Я обнаружил, что субтитры моих UITableCells не будут обновляться в viewWillAppear, Я привел это к минимальному примеру:

У меня есть статическая ячейка TableViewController с 2 ячейками (style = subtitle) Один субтитр пуст, другой установлен.

Интерфейсный скриншот

Я обновляю субтитры так:

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

Хотя все работает под iOS7 (и 6 и 5), iOS8 не будет обновлять название первой ячейки.

скриншот симулятора

Однако, когда я коснусь ячейки, она обновится и покажет текст.

Это проблема симулятора? Жук? Я делаю что-то неправильно?

6 ответов

Решение

В качестве временной работы я просто ввел пустое пространство в текстовую метку и программно, где я установил текст метки в ноль, я установил его в пустое пространство

cell.detailTextLabel.text = @" ";

Настройте свою логику соответственно, чтобы иметь дело с одиноким пустым пространством как ноль.

Для меня метка с подробным текстом не отображалась, хотя у меня была готовая строка для нее до загрузки viewcontroller. одна из моих многочисленных ячеек показывает текущую дату в подробном текстовом ярлыке по умолчанию всякий раз, когда появляется контроллер представления, но это тоже не сработало.

Я думаю, что это как-то связано с тем, что iOS 8 не может обновить текст текстовой метки, если она изначально установлена ​​на ноль.

Так что следите за тем, чтобы вы добавили пустое пространство и в ячейку прототипа, в конструктор интерфейса. Surprisingly text in labels of my custom cells are working fine.

Похоже, Apple добавила оптимизацию, которая удаляет detailTextLabel из иерархии представления, когда значение равно nil, и повторно добавляет при необходимости. К сожалению, в моих тестах он добавлялся во время прохода макета, но только после того, как его размер был изменен, чтобы соответствовать новому тексту на этом этапе макета, поэтому размер по-прежнему равен нулю. Следующий проход макета (скажем, на повороте) затем будет отображаться ОК.

Одним из обходных путей может быть принудительное добавление метки обратно в иерархию представления при любых обстоятельствах (кажется, что все должно быть в порядке, если нулевой размер). Мне было бы интересно посмотреть, исправит ли это изменение в вашем приложении.

#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 можно просто удалить.

Прошло несколько дней, и я думаю, что мне придется использовать обходной путь:

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

У этого есть видимая задержка, но это единственный способ, которым я мог найти, чтобы текст появился и был читабелен на iOS8.

Подтверждено. iOS8 не рисует detailText, если он инициализирован в ноль. Эта простая линия должна сделать свое дело.

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

Я видел две причины этой проблемы на iOS8.

  1. Если вы установите для текста в метке сведений значение nil или "" (чаще всего места, где это делается, находятся в prepareForReuse и / или удаляя текст по умолчанию из раскадровки) [Как указано в ответах выше, эта проблема решается в iOS9]

  2. Если вы создаете подкласс UITableViewCell типа subtitle, а затем программно регистрируете ячейку, созданную вами в раскадровке.[Это ошибка программирования, вам не нужно регистрировать ячейки-прототипы, созданные в раскадровке]

У меня та же проблема, в cellForRowAtIndexPath Я звоню cell.layoutSubviews() перед возвращением клетки. Это решает проблему.

Другие вопросы по тегам