Offscreen UITableViewCells (для расчета размера), не учитывая класс размера?
Я использую классы Auto Layout и size внутри UITableView с ячейками, размер которых зависит от их содержимого. Для этого я использую метод, в котором для каждого типа ячеек вы сохраняете внеэкранный экземпляр этой ячейки и используете systemLayoutSizeFittingSize
для определения правильной высоты строки - этот метод прекрасно объясняется в qaru.site/info/1017/... и в другом месте.
Это отлично работало, пока я не начал использовать классы размера. В частности, я определил разные константы в ограничениях по границам текста в макетах с обычной шириной, поэтому на iPad на iPad больше пробелов. Это дает мне следующие результаты.
Похоже, что новый набор ограничений соблюдается (имеется больше пробелов), но вычисление высоты строки по-прежнему возвращает то же значение, что и для ячейки, которая не применяла ограничения класса для класса. Некоторая часть процесса компоновки в внеэкранной ячейке не учитывает класс размера окна.
Теперь я понял, что, вероятно, из-за того, что внеэкранное представление не имеет супервизора или окна, и, как таковое, оно не имеет признаков класса размера, которые нужно ссылаться в точке вызова systemLayoutSizeFittingSize
(хотя это и кажется используйте скорректированные ограничения для полей). Я теперь обойдусь этим, добавив ячейку выбора размера экрана как подзадачу UIWindow после ее создания, что дает желаемый результат:
Вот что я делаю в коде:
func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
let contentItem = content[indexPath.item]
if let contentType = contentItem["type"] {
// Get or create the cached layout cell for this cell type.
if layoutCellCache.indexForKey(contentType) == nil {
if let cellIdentifier = CellIdentifiers[contentType] {
if var cachedLayoutCell = dequeueReusableCellWithIdentifier(cellIdentifier) as? UITableViewCell {
UIApplication.sharedApplication().keyWindow?.addSubview(cachedLayoutCell)
cachedLayoutCell.hidden = true
layoutCellCache[contentType] = cachedLayoutCell
}
}
}
if let cachedLayoutCell = layoutCellCache[contentType] {
// Configure the layout cell with the requested cell content.
configureCell(cachedLayoutCell, withContentItem: contentItem)
// Perform layout on the cached cell and determine best fitting content height.
cachedLayoutCell.bounds = CGRectMake(0.0, 0.0, CGRectGetWidth(tableView.bounds), 0);
cachedLayoutCell.setNeedsLayout()
cachedLayoutCell.layoutIfNeeded()
return cachedLayoutCell.contentView.systemLayoutSizeFittingSize(UILayoutFittingCompressedSize).height
}
}
fatalError("not enough information to determine cell height for item \(indexPath.item).")
return 0
}
Добавление просмотров в окно, которое никогда не должно быть нарисовано, похоже на взломать меня. Есть ли способ, чтобы UIViews полностью применяли класс размера окна, даже если они не находятся в иерархии представлений? Или есть что-то еще, что мне не хватает? Спасибо.
Ответы
Ответ 1
Обновление до 2015 года:
Apple теперь отвлекает переопределение -traitCollection
. Пожалуйста, рассмотрите возможность использования других обходных решений. Из документ:
ВАЖНО
Используйте свойство traitCollection
напрямую. Не отменяйте его. Не предоставляйте собственную реализацию.
Исходный ответ:
существующий ответ. Он пояснил, что проблема в том, что:
Предлагаемое обходное решение заключается в том, чтобы временно добавить ячейку в представление таблицы. Однако это НЕ работает, если мы находимся, скажем, -viewDidLoad
, в котором traitCollection
табличного представления, или вид контроллера представления, или даже самого контроллера представления еще недействителен.
Здесь я предлагаю другое обходное решение, которое должно переопределить traitCollection
ячейки. Для этого:
-
Создайте собственный подкласс UITableViewCell
для ячейки (что вы, вероятно, уже сделали).
-
В пользовательском подклассе добавьте метод - (UITraitCollection *)traitCollection
, который переопределяет getter свойства traitCollection
. Теперь вы можете вернуть любой действительный UITraitCollection
, который вам нравится. Вот пример реализации:
// Override getter of traitCollection property
// https://stackoverflow.com/a/28514006/1402846
- (UITraitCollection *)traitCollection
{
// Return original value if valid.
UITraitCollection* originalTraitCollection = [super traitCollection];
if(originalTraitCollection && originalTraitCollection.userInterfaceIdiom != UIUserInterfaceIdiomUnspecified)
{
return originalTraitCollection;
}
// Return trait collection from UIScreen.
return [UIScreen mainScreen].traitCollection;
}
В качестве альтернативы вы можете вернуть подходящий UITraitCollection
, созданный с помощью любого из его методов создания, например:
+ (UITraitCollection *)traitCollectionWithDisplayScale:(CGFloat)scale
+ (UITraitCollection *)traitCollectionWithTraitsFromCollections:(NSArray *)traitCollections
+ (UITraitCollection *)traitCollectionWithUserInterfaceIdiom:(UIUserInterfaceIdiom)idiom
+ (UITraitCollection *)traitCollectionWithHorizontalSizeClass:(UIUserInterfaceSizeClass)horizontalSizeClass
+ (UITraitCollection *)traitCollectionWithVerticalSizeClass:(UIUserInterfaceSizeClass)verticalSizeClass
Или вы можете сделать это более гибким, сделав это:
// Override getter of traitCollection property
// https://stackoverflow.com/a/28514006/1402846
- (UITraitCollection *)traitCollection
{
// Return overridingTraitCollection if not nil,
// or [super traitCollection] otherwise.
// overridingTraitCollection is a writable property
return self.overridingTraitCollection ?: [super traitCollection];
}
Это обходное решение совместимо с iOS 7, поскольку свойство traitCollection
определено в iOS 8+, и поэтому в iOS 7 никто не будет называть его получателем и, следовательно, наш метод переопределения.
Ответ 2
Я потратил несколько дней на это, перейдя к использованию классов размера, чтобы упростить изменение размера шрифта на iPad и iPhone и т.д.
Корень проблемы кажется, что dequeueReusableCellWithIdentifier:
возвращает ячейку, у которой нет супервизора, из которого она получает свой UITraitCollection
. dequeueReusableCellWithIdentifier:forIndexPath:
, с другой стороны, возвращает ячейку, супервидом которой является UITableViewWrapperView
.
Я опубликовал отчет об ошибке с Apple, поскольку они не расширили этот метод для поддержки классов размеров; кажется, не документировано, как работать с классами размеров на iOS7. Когда вы отправляете сообщение в UITableView
, запрашивающее ячейку, оно должно возвращать тот, который отражает класс размера таблицы, в которую вы отправляете сообщение. Это относится к dequeueReusableCellWithIdentifier:forIndexPath:
.
Я также заметил, что при попытке использовать новый механизм автоматического компоновки вам часто нужно перезагрузить таблицу в viewDidAppear:
, чтобы новый механизм работал правильно. Без этого я вижу ту же проблему, что и у iOS7.
Как я могу сказать, не представляется возможным использовать автоматическую компоновку на iOS8 и старый механизм для iOS7 из того же кода.
В настоящее время мне приходилось прибегать к решению проблемы, добавляя ячейку прототипа в качестве подсмотра таблицы, делая вычисление размера, а затем удаляя ее:
UITableViewCell *prototype=nil;
CGFloat prototypeHeight=0.0;
prototype=[self.tableView dequeueReusableCellWithIdentifier:@"SideMenuCellIdentifier"];
// Check for when the prototype cell has no parent view from
// which to inherit size class related constraints.
BOOL added=FALSE;
if (prototype.superview == nil){
[self.tableView addSubview:prototype];
added=TRUE;
}
<snip ... Setup prototype cell>
[prototype setNeedsLayout];
[prototype layoutIfNeeded];
CGSize size = [prototype.contentView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize];
prototypeHeight=size.height+1; // Add one for separator
// Remove the cell if added. Leaves it when in iOS7.
if (added){
[prototype removeFromSuperview];
}
Параметры, связанные с классом размера, по-видимому, контролируются с помощью UITraitCollection
, который является только для чтения атрибутом UIViewController
. Для обратной совместимости iOS7 это, по-видимому, обрабатывается системой сборки как работа с некоторыми ограничениями. то есть на iOS7 вы не можете получить доступ к traitCollection
property, но вы можете в iOS8.
Учитывая жесткую связь с контроллером представления из раскадровки и как работает обратная совместимость, похоже, что ячейка прототипа должна быть в иерархии контроллера вида, определенного в Xcode.
Здесь обсуждается:
Как адаптивные интерфейсы Xcode 6 будут обратно совместимы с iOS 7 и iOS 6?