Есть ли подходящий способ справиться с перекрывающимися братьями и сестрами NSView?

Я работаю над приложением Cocoa, и я столкнулся с ситуацией, когда мне хотелось бы, чтобы два объекта NSView перекрывались. У меня есть родительский NSView, который содержит два подзапроса (NSView A и NSView B), каждый из которых может иметь несколько собственных подсетей.

Есть ли способ справиться с подобным перекрытием? NSView B всегда будет "выше" NSView A, поэтому я хочу, чтобы перекрывающиеся части NSView A были замаскированы.

Ответ 1

Если ваше приложение имеет размер 10,5, включите слои для просмотров, и он должен просто работать.

Если вы имеете в виду поддержку 10.4 и ниже, вам нужно найти способ не перекрывать представления, потому что совпадающие представления sibling - это поведение undefined. Как говорится в руководстве по программированию:

По соображениям производительности Cocoa не обеспечивает принудительного вырезания среди представлений братьев и сестер или гарантирует правильное поведение недействительности и рисования при совпадении взглядов братьев и сестер. Если вы хотите, чтобы представление было обращено перед другим видом, вы должны сделать вид спереди субвью (или потомок) заднего вида.

Я видел некоторые хаки, которые могут иногда делать это любопытно, но на это не на что можно положиться. Вам нужно либо сделать View A подвид View B, либо сделать одно гигантское представление, которое будет выполнять обе свои обязанности.

Ответ 2

Крис, единственное решение - использовать CALayers. Это определенно единственное решение.

NSViews в OSX (сентябрь 2010) просты в использовании: siblings работают неправильно. Один или другой будет случайным образом отображаться сверху.

Чтобы повторить, проблема заключается в siblings.

Чтобы проверить это: используя NSViews и/или nsimageviews. Сделайте приложение с представлением, которое представляет собой одно большое изображение (1000x1000). В представлении разместите три или четыре маленьких изображения /NSView здесь и там. Теперь добавьте еще одно большое изображение 1000x1000. Создавайте и запускайте приложение повторно - вы увидите, что он просто сломан. Часто нижние (маленькие) слои появляются поверх большого покровного слоя. если вы включите поддержку слоев в NSViews, это не поможет, независимо от того, какую комбинацию вы попробуете. Итак, окончательный тест.

Вам нужно отказаться от NSViews и использовать CALayers и что это.

Единственное раздражение с CALayers заключается в том, что вы не можете использовать IB для настройки своего материала. Вы должны установить все позиции слоя в коде,

yy = [CALayer layer];
yy.frame = CGRectMake(300,300, 300,300);

Сделайте только один NSView, который предназначен только для того, чтобы удерживать ваш первый CALayer (возможно, называемый "сзади" ), а затем просто поместить все ваши CALayers в тыл.

rear = [CALayer layer];
rear.backgroundColor = CGColorCreateGenericRGB( 0.75, 0.75, 0.75, 1.0 );

[yourOnlyNsView setLayer:rear]; // these two lines must be in this order
[yourOnlyNsView setWantsLayer:YES]; // these two lines must be in this order

[rear addSublayer:rr];
[rear addSublayer:yy];
     [yy addSublayer:s1];
     [yy addSublayer:s2];
     [yy addSublayer:s3];
     [yy addSublayer:s4];
[rear addSublayer:tt];
[rear addSublayer:ff];

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

-(void) shuff
{
[CATransaction begin];
[CATransaction setValue:[NSNumber numberWithFloat:0.0f]
              forKey:kCATransactionAnimationDuration];
if ..
    [rear insertSublayer:ff below:yy];
else
    [rear insertSublayer:ff above:yy];
[CATransaction commit];
}

(Единственная причина для раздражающей обертки "в нулевых секундах" для всего, что вы делаете, состоит в том, чтобы предотвратить анимацию, которая предоставляется вам бесплатно - если вы не хотите анимацию!)

Кстати, в этой цитате из Apple,

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

Следующее их предложение...

Если вы хотите, чтобы перед другим видом, вы должны сделать вид спереди подвью (или потомок) заднего вида.

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

Итак, это CALayers! Наслаждайтесь!

Ответ 3

Есть способ сделать это, не используя CALayers, и приложение, над которым я работал, может это доказать. Создайте два окна и используйте это:

[mainWindow addChildWindow:otherWindow ordered:NSWindowAbove];

Чтобы удалить "otherWindow", используйте:

[mainWindow removeChildWindow:otherWindow];

[otherWindow orderOut:nil];

И вы, вероятно, захотите взять строку заголовка окна с помощью:

[otherWindow setStyleMask:NSBorderlessWindowMask];

Ответ 4

Чтобы гарантировать, что NSView B всегда перекрывается NSView A, убедитесь, что вы используете правильный NSWindowOrderingMode, когда вы добавляете subview:

[parentView addSubview:B positioned:NSWindowAbove relativeTo:A];

Вы также должны помнить, что скрытые части A не будут запрашиваться для перерисовки, если вид B на 100% непрозрачен.

Если вы перемещаете subviews, вам также необходимо убедиться, что вы вызываете -setNeedsDisplayInRect: для областей просмотра, которые вы открываете.

Ответ 5

Как писал Нейт, можно использовать:

self.addSubview(btn2, positioned: NSWindowOrderingMode.Above, relativeTo: btn1)

Однако упорядочение представлений не соблюдается, как только вы просите обоим представлениям перерисовать его через вызов "needDisplay = true"

Братья и сестры не получат вызов drawRect, только прямые иерархии представлений будут.

Обновление 1

Чтобы решить эту проблему, мне пришлось прорываться глубоко, очень глубоко. Вероятно, неделя исследований и Ive распространила мои выводы на несколько статей. Окончательный прорыв в этой статье: http://stylekit.org/blog/2015/12/24/The-odd-case-of-luck/

Обновление 2

Будьте осторожны, хотя понятие трудно понять, но оно работает, и оно отлично работает. Вот конечный результат и код для его поддержки, ссылки на github repo и т.д.: http://stylekit.org/blog/2015/12/30/Graphic-framework-for-OSX/