Eu tenho um UITableView com uma lista de itens. Selecionar um item empurra um viewController que então prossegue para fazer o seguinte. do método viewDidLoad eu disparo um URLRequest para dados que são exigidos por uma das minhas subvisualizações - uma subclasse UIView com drawRect substituído. Quando os dados chegam da nuvem, começo a construir minha hierarquia de visualizações. a subclasse em questão passa os dados e seu método drawRect agora tem tudo o que precisa para renderizar.
Mas.
Porque eu não chamo drawRect explicitamente - Cocoa-Touch lida com isso - não tenho como informar ao Cocoa-Touch que eu realmente quero que esta subclasse UIView renderize. Quando? Agora seria bom!
Eu tentei [myView setNeedsDisplay]. Isso meio que funciona às vezes. Muito irregular.
Estou lutando com isso por horas e horas. Alguém poderia me fornecer uma abordagem sólida e garantida para forçar uma re-renderização do UIView.
Aqui está o snippet de código que alimenta os dados da visualização:
// Create the subview
self.chromosomeBlockView = [[[ChromosomeBlockView alloc] initWithFrame:frame] autorelease];
// Set some properties
self.chromosomeBlockView.sequenceString = self.sequenceString;
self.chromosomeBlockView.nucleotideBases = self.nucleotideLettersDictionary;
// Insert the view in the view hierarchy
[self.containerView addSubview:self.chromosomeBlockView];
[self.containerView bringSubviewToFront:self.chromosomeBlockView];
// A vain attempt to convince Cocoa-Touch that this view is worthy of being displayed ;-)
[self.chromosomeBlockView setNeedsDisplay];
Saúde, Doug
fonte
setNeedsDisplay
chamada real dedrawRect:
. Embora a confiabilidade da chamada esteja aqui, eu não chamaria isso de solução “mais robusta” - uma solução de desenho robusta deve, em teoria, fazer todo o desenho imediatamente antes de retornar ao código do cliente que está exigindo o desenho.Eu tive um problema com um grande atraso entre chamar setNeedsDisplay e drawRect: (5 segundos). Acontece que eu chamei setNeedsDisplay em um thread diferente do thread principal. Depois de mover esta chamada para o thread principal, o atraso foi embora.
Espero que isso ajude.
fonte
A maneira garantida de devolução do dinheiro e sólido de concreto armado de forçar uma visualização a desenhar de forma síncrona (antes de retornar ao código de chamada) é configurar as
CALayer
interações de com suaUIView
subclasse.Em sua subclasse UIView, crie um
displayNow()
método que diga à camada para " definir o curso para exibição " e " torná-lo assim ":Rápido
Objective-C
Implemente também um
draw(_: CALayer, in: CGContext)
método que chamará seu método de desenho privado / interno (que funciona já que todoUIView
é umCALayerDelegate
) :Rápido
Objective-C
E crie seu
internalDraw(_: CGRect)
método personalizado , junto com a prova de falhasdraw(_: CGRect)
:Rápido
Objective-C
E agora apenas chame
myView.displayNow()
sempre que você realmente precisar para desenhar (como em umCADisplayLink
retorno de chamada) . NossodisplayNow()
método dirá oCALayer
paradisplayIfNeeded()
, que irá chamar de volta sincronizadamente o nossodraw(_:,in:)
e desenharinternalDraw(_:)
, atualizando o visual com o que é desenhado no contexto antes de prosseguir.Essa abordagem é semelhante à de @RobNapier acima, mas tem a vantagem de chamar
displayIfNeeded()
além desetNeedsDisplay()
, o que a torna síncrona.Isso é possível porque
CALayer
s expõe mais funcionalidade de desenho do queUIView
s - as camadas são de nível inferior do que as vistas e são projetadas explicitamente para o propósito de desenho altamente configurável dentro do layout e (como muitas coisas no Cocoa) são projetadas para serem usadas de forma flexível ( como uma classe pai, ou como um delegador, ou como uma ponte para outros sistemas de desenho, ou apenas por conta própria). O uso adequado doCALayerDelegate
protocolo torna tudo isso possível.Mais informações sobre a configurabilidade de
CALayer
s podem ser encontradas na seção Setting Up Layer Objects do Core Animation Programming Guide .fonte
drawRect:
diz explicitamente "Você nunca deve chamar este método diretamente." Além disso, o CALayerdisplay
diz explicitamente "Não chame este método diretamente." Se você deseja desenharcontents
diretamente nas camadas, não há necessidade de violar essas regras. Você pode simplesmente desenhar na camadacontents
sempre que quiser (mesmo em threads de fundo). Basta adicionar uma subcamada à visualização para isso. Mas isso é diferente de colocá-lo na tela, que precisa esperar até o momento correto de composição.contents
("Se o objeto de camada estiver vinculado a um objeto de visualização, você deve evitar definir o conteúdo desta propriedade diretamente. A interação entre visualizações e camadas geralmente resulta na visualização, substituindo o conteúdo dessa propriedade durante uma atualização subsequente. ") Não estou recomendando especificamente essa abordagem; o desenho prematuro prejudica o desempenho e a qualidade do desenho. Mas se você precisar dele por algum motivo, vejacontents
como obtê-lo.drawRect:
direta. Não era necessário demonstrar esta técnica e foi corrigida.contents
técnica que você está sugerindo ... parece atraente. Eu tentei algo parecido inicialmente, mas não consegui fazer funcionar e descobri que a solução acima era muito menos código, sem nenhuma razão para não ter o mesmo desempenho. No entanto, se você tiver uma solução de trabalho para acontents
abordagem, eu estaria interessado em lê-la (não há razão para que você não possa ter duas respostas para essa pergunta, certo?)display
. É apenas um método público personalizado em uma subclasse UIView, assim como o que é feito em GLKView (outra subclasse UIView, e a única escrita pela Apple que conheço que exige a funcionalidade DRAW NOW! ).Eu tive o mesmo problema e todas as soluções do SO ou do Google não funcionaram para mim. Normalmente,
setNeedsDisplay
funciona, mas quando não ...Eu tentei chamar
setNeedsDisplay
do modo de exibição de todas as maneiras possíveis de todos os threads possíveis e outras coisas - ainda sem sucesso. Nós sabemos, como Rob disse, queMas por alguma razão não iria desenhar desta vez. E a única solução que encontrei é chamá-lo manualmente depois de algum tempo, para deixar passar qualquer coisa que bloqueie o empate, assim:
É uma boa solução se você não precisa que a visualização seja redesenhada com frequência. Caso contrário, se você estiver fazendo alguma coisa de movimento (ação), geralmente não há problemas em apenas pagar
setNeedsDisplay
.Espero que ajude alguém que está perdido ali, como eu.
fonte
Bem, eu sei que isso pode ser uma grande mudança ou até mesmo não adequado para o seu projeto, mas você considerou não realizar o push até que já tenha os dados ? Dessa forma, você só precisa desenhar a vista uma vez e a experiência do usuário também será melhor - o push já estará carregado.
A maneira de fazer isso é
UITableView
didSelectRowAtIndexPath
solicitando os dados de maneira assíncrona. Depois de receber a resposta, você executa manualmente a segue e passa os dados para seu viewController noprepareForSegue
. Enquanto isso, você pode querer mostrar algum indicador de atividade, para um indicador de carregamento simples, verifique https://github.com/jdg/MBProgressHUDfonte