Субтитры 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]()
Однако, когда я касаюсь ячейки, он обновит и покажет текст.
Это проблема симулятора? Жук? Я что-то делаю неправильно?
Ответы
Ответ 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 типа субтитров, а затем программно зарегистрируйте ячейку, созданную вами в раскадровке.
[Это ошибка программирования, вам не нужно регистрировать ячейки прототипов, созданные в раскадровке]