Есть ли правильный способ обработки перекрывающихся братьев и сестер NSView? - PullRequest
16 голосов
/ 21 января 2009

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

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

Ответы [ 5 ]

13 голосов
/ 18 сентября 2010

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

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

Просто повторюсь, проблема в братьях и сестрах .

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

Вы должны отказаться от NSViews и использовать CALayers, и все.

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

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

Создайте только один NSView, единственной целью которого является удержание вашего первого CALayer (возможно, называемого «задним»), а затем просто поместите все ваши CALayer в тыл.

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,

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

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

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

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

Так что это CALayers! Наслаждайтесь!

6 голосов
/ 21 января 2009

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

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

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

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

3 голосов
/ 02 ноября 2011

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

[mainWindow addChildWindow:otherWindow ordered:NSWindowAbove];

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

[mainWindow removeChildWindow:otherWindow];

[otherWindow orderOut:nil];

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

[otherWindow setStyleMask:NSBorderlessWindowMask];
1 голос
/ 04 декабря 2015

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

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

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

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

Обновление 1

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

Обновление 2

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

1 голос
/ 21 января 2009

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

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

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

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

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