Подтвердить что ты не робот

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

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

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

Interface builder screenschot

Я обновляю субтитры следующим образом:

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

В то время как каждый из них работает под iOS7 (и 6 и 5), iOS8 не обновляет заголовок первой ячейки.

screenshot of simulator

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

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

4b9b3361

Ответ 1

Как временная работа вокруг, я просто ввел пробел в текстовую метку и программно, где я установил текст метки в nil, я установил его в пустое пространство

cell.detailTextLabel.text = @" ";

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

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

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

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

Ответ 2

Похоже, что 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 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 в нужное время, это может вынудить дополнительный макет, который исправляет все, но я не смог найти подходящее время для этого.

EDIT: комментарий ниже показал, что повторно отображаемые ячейки могут быть проблемой. Таким образом, более простым решением может быть просто swizzle 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

EDIT: Кажется, эта ошибка исправлена ​​в iOS9. Таким образом, условие можно изменить на:

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

Если приложение должно поддерживать только iOS9 и выше, категорию исправлений swizzle можно просто удалить.

Ответ 3

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

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

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

Ответ 4

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

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

Ответ 5

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

Ответ 6

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

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

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