Слой, содержащий NSView в NSOutlineView
Я пытаюсь создать пользовательский NSView
, на котором размещена иерархия CALayer
для эффективного отображения. Этот NSView
затем внедряется внутри NSTableCellView
, который отображается с помощью NSOutlineView
на основе View.
Проблема заключается в том, что всякий раз, когда я расширяю или сворачиваю элемент, все строки перемещаются, но содержимое слоя остается отображаемым в позиции, которая была перед изменением контура.
Прокрутка NSOutlineView
, похоже, обновляет слои, и они повторно синхронизируются со своими строками в этой точке.
Я отлаживал это поведение с помощью инструментов, и кажется, что прокрутка провоцирует операцию компоновки, которая обновляет слои вызовом setPosition:
, который должен был произойти при расширении или свертывании элементов.
Вот пример кода для простого уровня размещения подкласса NSView
.
@interface TestView : NSView
@end
@implementation TestView
- (instancetype)initWithFrame:(NSRect)frameRect
{
self = [super initWithFrame:frameRect];
CAShapeLayer* layer = [CAShapeLayer layer];
layer.bounds = self.bounds;
layer.position = CGPointMake(NSMidX(self.bounds), NSMidY(self.bounds));
layer.path = [NSBezierPath bezierPathWithOvalInRect:self.bounds].CGPath;
layer.fillColor = [NSColor redColor].CGColor;
layer.delegate = self;
self.layer = layer;
self.wantsLayer = YES;
return self;
}
@end
Я пробовал много потенциальных решений этой проблемы, но не смог найти какой-либо интересный метод, вызываемый в экземпляре NSView
, который можно было бы переопределить для вызова [self.layer setNeedsDisplay]
или [self.layer setNeedsLayout]
. Я также пробовал различные настройки для самого CALayer
, например:
layer.autoresizingMask = kCALayerWidthSizable | kCALayerHeightSizable;
layer.needsDisplayOnBoundsChange = YES;
self.layerContentsRedrawPolicy = NSViewLayerContentsRedrawOnSetNeedsDisplay;
Может ли кто-нибудь помочь мне разобраться, как правильно отобразить этот слой внутри NSOutlineView
?
Ответы
Ответ 1
В итоге я ответил на свой вопрос. Проблема была не в том, как мой TestView был реализован. Я просто пропустил один из шагов для поддержки поддержки CoreAnimation в приложении. Соответствующая ссылка находится в руководстве по программированию основной анимации.
В принципе, в iOS Core Animation и поддержка слоев всегда включена по умолчанию. В OS X он должен быть включен следующим образом:
- Ссылка на структуру QuartzCore
- Включить поддержку слоя для одного или нескольких объектов NSView, выполнив одно из следующих действий:
- В ваших файлах nib используйте инспектор View Effects, чтобы включить поддержку слоев для ваших просмотров. Инспектор отображает флажки для выбранного вида и его подпунктов. Рекомендуется, чтобы вы включили поддержку слоев в представлении содержимого вашего окна, когда это возможно.
- Для представлений, которые вы создаете программно, вызовите метод setWantsLayer: и передайте значение YES, чтобы указать, что представление должно использовать слои.
Как только я включаю поддержку слоя для любого из родителей NSOutlineView, различные глюки решаются.
Ответ 2
Трудно прочитать справочные документы NSOutlineView
и найти информацию о повторном использовании ячеек, которая, вероятно, даст вам возможность приступить.
Возможно, вы посмотрели на outlineViewItemDidCollapse:
, но это бесполезно для нашей проблемы, потому что у него нет указателя на NSView
, и это потому, что оно больше, чем представления на основе представления.
Возможно, одно полезное упоминание, закодированное в протоколе NSOutlineViewDelegate
, в разделе по методам NSOutlineView, основанным на представлении, есть одно упоминание внутри outlineView:didRemoveRowView:forRow:
, что:
Удаленный rowView может быть повторно использован таблицей, поэтому любые дополнительные вставленные представления должны быть удалены в этот момент.
Другими словами, когда вы вызываете представление схемы makeViewWithIdentifier:owner:
, для cellView или rowView с определенным ID вы часто получаете переработанное представление. Особенно часто из-за краха. Кстати, этот метод из суперкласса NSTableView
, и в этой ссылке также этот комментарий:
Этот метод также может возвращать повторно используемое представление с тем же идентификатором, который больше не доступен на экране. Если представление с указанным идентификатором невозможно создать из файла nib или найти в очереди повторного использования, этот метод возвращает nil.
Таким образом, у вас есть возможность изменить иерархию представлений или свойства niling в didRemoveRowView:forRow
. Тем не менее, похоронен в третьей ссылке cocoa, что для NSView
в комментарии к prepareForReuse
, этот комментарий:
Этот метод предлагает способ для reset просмотра некоторого начального состояния, чтобы его можно было повторно использовать. Например, класс NSTableView использует его для подготовки просмотров для повторного использования и тем самым позволяет избежать затрат на создание новых представлений при прокрутке их в виде. Если вы внедряете систему повторного использования в свой собственный код, вы можете вызвать этот метод из своего собственного кода до повторного использования.
Итак, TL; DR, вам нужно реализовать prepareForReuse
.
Уместными ссылками являются (в основном) суперклассы как NSOutlineView
, так и NSTableCellView
.
И, FWIW, здесь был подобный вопрос, где, похоже, вопросник говорит о том, что ситуация еще хуже, чем я думаю, в том, что NSOutlineView более творческий позади сцены, чем NSTableView.
В моей собственной работе с контурными представлениями и встроенными NSTextViews я видел ужасно ужасные икоты рендеринга, связанные с расширением/сбой/прокруткой, которые, как мне кажется, управляются только методами NSOutlineViewDelegate. В iOS они сделали все, чтобы переименовать makeViewWithIdentifier
в более явный dequeueReusableCellViewWithIdentifier
.
Ответ 3
Вам не нужно включать поддержку слоев для любого из представлений предков (например, в виде контура).
По моему опыту, слой, сразу назначенный для представления (в отличие от подслоев), не нуждается в настройках маски, рамки или авторезиста. Он автоматически делается для отслеживания границ представления. На самом деле, я бы не стал устанавливать эти свойства, на всякий случай, что прерывает автоматическую синхронизацию с ограничением вида rect.
Итак, возникает вопрос: как вы настраиваете представление для перемещения или изменения размера с помощью своего супервизора? Вы используете автоматическую компоновку? Если это так, отключили ли вы его translatesAutoresizingMaskIntoConstraints
? Если да для обоих, какие ограничения вы задаете в представлении? Если это не так, как вы позиционировали представление в своем супервизии? Какую рамку вы установили? Кроме того, супервизор сконфигурирован для автоматического анализа своих подзапросов (возможно, да, поскольку это значение по умолчанию)? Каково ваше мнение autoresizingMask
?
Вы также можете переопределить -setFrameOrigin:
и -setFrameSize:
в своем классе пользовательского вида и вызвать супер. Кроме того, добавьте ведение журнала, чтобы показать, когда это произойдет, и что такое новый прямоугольник. Является ли ваше представление перемещенным, как вы ожидаете, когда вы расширяете или сворачиваете строки?