Existe uma maneira correta de lidar com irmãos NSView sobrepostos?

Estou trabalhando em um aplicativo Cocoa e me deparei com uma situação em que gostaria de ter dois objects NSView sobrepostos. Eu tenho um pai NSView que contém duas subvisualizações (NSView A e NSView B), cada qual pode ter várias subvisões próprias.

Existe uma maneira correta de lidar com esse tipo de sobreposição? O NSView B sempre estaria “acima” do NSView A, portanto, quero que as partes sobrepostas do NSView A sejam mascaradas.

Se o seu aplicativo for somente 10.5, ative as camadas para as visualizações e isso deve funcionar.

Se você está querendo suportar 10.4 e abaixo, você precisará encontrar uma maneira de não ter as visualizações sobrepostas, porque as visualizações de irmãos sobrepostas são um comportamento indefinido. Como o View Programming Guide diz:

Por motivos de desempenho, o Cocoa não impõe o recorte entre exibições irmãs nem garante o comportamento correto de invalidação e de desenho quando as visualizações de irmãos se sobrepõem. Se você deseja que uma vista seja desenhada na frente de outra vista, você deve tornar a vista frontal uma subview (ou descendente) da vista traseira.

Eu vi alguns hacks que podem fazer isso funcionar às vezes, mas não é nada que você possa confiar. Você precisará tornar a View A uma subview da View B ou fazer uma visão gigante que lide com ambas as suas tarefas.

Chris, a única solução é usar as CALayers. Essa é definitivamente a única solução.

Os NSViews são simplesmente quebrados no OSX (setembro de 2010): os irmãos não funcionam corretamente. Um ou outro aparecerá aleatoriamente no topo.

Apenas para repetir, o problema é com irmãos .

Para testar isso: usando NSViews e / ou nsimageviews. Crie um aplicativo com uma visualização que seja uma imagem grande (digamos 1000×1000). Na exibição, coloque três ou quatro imagens pequenas / NSViews aqui e ali. Agora coloque outra imagem grande de 1000×1000 no topo. Crie e execute o aplicativo repetidamente – você verá que ele está totalmente quebrado. Muitas vezes, as camadas inferiores (pequenas) aparecerão no topo da grande camada de cobertura. Se você ativar o suporte de camadas nos NSViews, isso não ajudará, não importa qual combinação você tente. Então esse é o teste definitivo.

Você tem que abandonar o NSViews e usar CALayers e é isso.

O único aborrecimento com as CALayers é que você não pode usar o IB para configurar suas coisas. Você tem que definir todas as posições da camada no código,

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

Faça apenas um NSView, cujo único propósito é manter seu primeiro CALayer (talvez chamado de ‘traseiro’), e depois colocar todos os seus CALayers dentro da parte traseira.

 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]; 

tudo então funciona perfeitamente, você pode aninhar e agrupar qualquer coisa que você queira e tudo funciona perfeitamente com tudo que estiver aparecendo acima / abaixo de tudo que deve aparecer acima / abaixo, não importa quão complexa seja sua estrutura. Mais tarde, você pode fazer qualquer coisa nas camadas, ou embaralhar as coisas da maneira típica,

 -(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]; } 

(A única razão para o invulgar invólucro ’em zero segundos’ para tudo o que você faz, é evitar a animação que é dada a você de graça – a menos que você queira a animação!)

A propósito, nesta citação da Apple,

Por motivos de desempenho, o Cocoa não impõe o recorte entre exibições irmãs nem garante o comportamento correto de invalidação e de desenho quando as visualizações de irmãos se sobrepõem.

Sua seguinte frase …

Se você deseja que uma vista seja desenhada na frente de outra vista, você deve tornar a vista frontal uma subview (ou descendente) da vista traseira.

É basicamente sem sentido (você não pode necessariamente replace irmãos com subs; e o bug óbvio descrito no teste acima ainda existe).

Então são CALayers! Apreciar!

Existe uma maneira de fazer isso sem usar CALayers e um aplicativo em que estou trabalhando pode provar isso. Crie duas janelas e use isto:

 [mainWindow addChildWindow:otherWindow ordered:NSWindowAbove]; 

Para remover o ” otherWindow ” use:

 [mainWindow removeChildWindow:otherWindow]; [otherWindow orderOut:nil]; 

E você provavelmente vai querer levar a barra de título da janela com:

 [otherWindow setStyleMask:NSBorderlessWindowMask]; 

Para garantir que o NSView B sempre sobreponha o NSView A , certifique-se de usar o NSWindowOrderingMode correto ao adicionar a subview:

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

Você também deve ter em mente que as partes ocultas de A não serão solicitadas para redesenhar se a visualização B for 100% opaca.

Se você estiver movendo as subvisualizações, também precisará certificar-se de chamar -setNeedsDisplayInRect: para as áreas da exibição que está descobrindo.

Como Nate escreveu, pode-se usar:

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

No entanto, a ordenação das visualizações não é respeitada assim que você solicitar que uma das visualizações seja redesenhada por meio da chamada “needDisplay = true”

Os irmãos não receberão a chamada drawRect, somente a hierarquia direta de visualizações.

Atualização 1

Para resolver esse problema, tive que cavar fundo, muito fundo. Provavelmente uma semana de pesquisa e eu espalhei minhas descobertas sobre alguns artigos. A conclusão final está neste artigo: http://eon.codes/blog/2015/12/24/The-odd-case-of-luck/

Atualização 2

Esteja avisado que o conceito é difícil de entender, mas funciona, e funciona muito bem. Aqui está o resultado final e código para apoiá-lo, links para o repository github etc: http://eon.codes/blog/2015/12/30/Graphic-framework-for-OSX/