Como faço para redefinir após um zoom UIScrollView?
-
19-08-2019 - |
Pergunta
Eu tenho um gráfico a ser desenhado dentro de um UIScrollView
. É uma grande UIView
usando uma subclasse personalizada de CATiledLayer
como sua camada.
Quando eu zoom in e out do UIScrollView
, eu quero o gráfico para redimensionar dinamicamente como ele faz quando eu voltar o gráfico de viewForZoomingInScrollView
. No entanto, o gráfico redesenha-se no novo nível de zoom, e eu quero repor a escala transformar a 1x1 de modo que a próxima vez que os zooms usuário, a transformação começa a partir da visão atual. Se eu redefinir a transformação para Identidade em scrollViewDidEndZooming
, ele funciona no simulador, mas lança uma EXC_BAD_ACCSES
no dispositivo.
Este nem sequer resolver totalmente o problema no simulador também, porque a próxima vez que os zooms usuário, a transformação redefine-se a tudo o zoom nível que era, e por isso parece que, se eu fosse ampliada para 2x, por exemplo, é de repente em 4x. Quando eu terminar o zoom, ele acaba na escala correta, mas o ato de zoom parece ruim.
Então, primeiro: como faço para permitir que o gráfico para redesenhar próprio na escala padrão de 1x1 Após o zoom, e como faço para ter um zoom suave ao longo
? Editar: Novas descobertas
O erro parece ser "[CALayer retainCount]: message sent to deallocated instance
"
Eu nunca estou desalocá quaisquer camadas eu. Antes, eu não estava mesmo eliminar quaisquer pontos de vista ou qualquer coisa. Este erro foi sendo jogado em zoom e também em rotação. Se eu excluir o objeto antes de rotação e re-adicioná-lo mais tarde, não lançar a exceção. Esta não é uma opção para controlar o zoom.
Solução
Eu não posso ajudá-lo com a queda, que não dizer-lhe para verificar e certificar-se de que você não está involuntariamente autoreleasing uma vista ou em algum lugar camada de dentro de seu código. Eu vi o punho simulador o tempo de autoreleases forma diferente do que no dispositivo (na maioria das vezes, quando tópicos estão envolvidos).
A escala vista é um problema com UIScrollView
eu correr em, no entanto. Durante um evento de zoom pitada, UIScrollView
terá a vista que especificado no método delegado viewForZoomingInScrollView:
e aplicar uma transformação para ele. Esta transformada fornece um escalonamento suave da visão sem ter de redesenhar que cada quadro. No final da operação de zoom, o seu método delegado scrollViewDidEndZooming:withView:atScale:
será chamado e dar-lhe uma chance de fazer uma tradução mais alta qualidade de sua visão no novo fator de escala. Geralmente, é sugerido que você redefinir a transformação na sua visão de ser CGAffineTransformIdentity
e depois ter sua visão redesenhar próprio manualmente na nova escala de tamanho.
No entanto, isso causa um problema porque UIScrollView
não aparecer para monitorar transformar a exibição de conteúdo, de modo que na próxima operação de zoom que define a transformação da exibição de conteúdo para qualquer que seja o fator de escala geral é. Desde que você tenha redesenhado manualmente seu ponto de vista no último fator de escala, que agrava o dimensionamento, o que é que você está vendo.
Como alternativa, eu uso uma subclasse UIView para minha exibição de conteúdo com os seguintes métodos definidos:
- (void)setTransformWithoutScaling:(CGAffineTransform)newTransform;
{
[super setTransform:newTransform];
}
- (void)setTransform:(CGAffineTransform)newValue;
{
[super setTransform:CGAffineTransformScale(newValue, 1.0f / previousScale, 1.0f / previousScale)];
}
onde previousScale é uma variável exemplo flutuador da vista. Eu, então, implementar o método delegado zoom da seguinte forma:
- (void)scrollViewDidEndZooming:(UIScrollView *)scrollView withView:(UIView *)view atScale:(float)scale;
{
[contentView setTransformWithoutScaling:CGAffineTransformIdentity];
// Code to manually redraw view at new scale here
contentView.previousScale = scale;
scrollView.contentSize = contentView.frame.size;
}
Ao fazer isso, as transformações enviados para a exibição de conteúdo são ajustados com base na escala em que a vista era última redesenhado. Quando a mola de zoom é feito, a transformação é reposto para uma escala de 1,0, ignorando o ajustamento no método setTransform:
normal. Este parece fornecer o comportamento de escala correta, deixando você desenhar uma visão nítida após a conclusão de um zoom.
UPDATE (7/23/2010): iPhone OS 3.2 e acima mudaram o comportamento de pontos de vista de rolagem em relação ao zoom. Agora, um UIScrollView
respeitará transformar a identidade que se aplicam a uma exibição de conteúdo e só fornecem o fator de escala relativa em -scrollViewDidEndZooming:withView:atScale:
. Portanto, o código acima para uma subclasse UIView
é necessário apenas para dispositivos que executam versões do iPhone OS com mais de 3.2.
Outras dicas
Graças a todas as respostas anteriores, e aqui está a minha solução.
Implementar UIScrollViewDelegate métodos:
- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView
{
return tmv;
}
- (void)scrollViewDidEndZooming:(UIScrollView *)scrollView withView:(UIView *)view atScale:(float)scale
{
CGPoint contentOffset = [tmvScrollView contentOffset];
CGSize contentSize = [tmvScrollView contentSize];
CGSize containerSize = [tmv frame].size;
tmvScrollView.maximumZoomScale = tmvScrollView.maximumZoomScale / scale;
tmvScrollView.minimumZoomScale = tmvScrollView.minimumZoomScale / scale;
previousScale *= scale;
[tmvScrollView setZoomScale:1.0f];
[tmvScrollView setContentOffset:contentOffset];
[tmvScrollView setContentSize:contentSize];
[tmv setFrame:CGRectMake(0, 0, containerSize.width, containerSize.height)];
[tmv reloadData];
}
- TMV é o meu subclasse de UIView
- tmvScrollView - saída para UIScrollView
- set maximumZoomScale e minimumZoomScale antes
- criar previousScale variável de instância e defina seu valor como 1.0f
Obras para me perfeitamente.
BR, Eugene.
Eu estava olhando apenas para repor a carga a escala de zoom padrão, porque quando eu ampliá-la, scrollview é ter mesma escala de zoom para a próxima vez até que ele obtém desalocada.
-(void) viewWillAppear:(BOOL)animated {
[self.scrollView setZoomScale:1.0f];
}
mudou o jogo.
Eu tenho uma discussão detalhada de como (e por que) UIScrollView zoom obras em github.com/andreyvit/ScrollingMadness/ .
(O link contém também uma descrição de como ampliar programaticamente UIScrollView, como emular Photo Library estilo paginação + zoom + rolagem, um projeto de exemplo e classe ZoomScrollView que encapsula um pouco da magia de zoom.)
Citação:
UIScrollView não tem uma noção de um “nível de zoom atual”, porque cada subexibição ele contém pode ter o seu próprio nível de zoom atual. Note-se que não há nenhum campo em UIScrollView para manter o nível de zoom atual. No entanto, saber que alguém lojas que nível de zoom, porque se você beliscar-zoom em uma subexibição, em seguida, redefinir a sua transformação para CGAffineTransformIdentity, e, em seguida, apertar novamente, você vai notar que o nível de zoom anterior do subexibição foi restaurada.
Na verdade, se você olhar para a desmontagem, é UIView que armazena o seu próprio nível de zoom (dentro UIGestureInfo objeto apontado pelo campo _gestureInfo). Ele também tem um conjunto de métodos ilegais agradáveis ??como zoomScale
e setZoomScale:animated:
. (Lembre-se, ele também tem um monte de métodos de rotação-relacionados, talvez nós estamos recebendo apoio gesto de rotação algum dia em breve.)
No entanto, se criar uma nova UIView apenas para zoom e adicionar nosso ponto de vista com zoom real como seu filho, vamos começar sempre com o nível de zoom 1.0. Minha implementação de zoom programático baseia-se este truque.
Uma abordagem bastante confiável, apropriado para iOS 3.2 e 4.0 e versões posteriores, é o seguinte. Você deve estar preparado para fornecer a sua visão escalável (o chefe subexibição da exibição da rolagem) em qualquer escala solicitado. Então, em scrollViewDidEndZooming:
você irá remover a versão turva transformado escala deste ponto de vista e substituí-la por uma nova visão atraídos para a nova escala.
Você vai precisar de:
-
Um ivar para manter a escala anterior; Vamos chamá-lo oldScale
-
Uma maneira de identificar o ponto de vista escalável, tanto para
viewForZoomingInScrollView:
e parascrollViewDidEndZooming:
; aqui, eu estou indo para aplicar uma etiqueta (999) -
Um método que cria a visão escalável, marcas de TI, e insere-o o ponto de vista de rolagem (aqui está eu vou chamá-lo
addNewScalableViewAtScale:
). Lembre-se, ele deve fazer tudo de acordo com a escala -. Que dimensiona a vista e seus subviews ou desenho e tamanhos da exibição da rolagem contentSize para corresponder -
As constantes para o minimumZoomScale e maximumZoomScale. Estes são necessários porque quando substituímos a vista escalado pela versão maior detalhado, o sistema vai pensar que a escala atual é 1, por isso devemos enganar-lo em permitindo ao usuário dimensionar a vista de volta para baixo.
Muito bem, então. Quando você criar a exibição de rolagem, você inserir o ponto de vista escalável em escala 1.0. Isso pode ser em sua viewDidLoad
, por exemplo (sv
é a visão de rolagem):
[self addNewScalableViewAtScale: 1.0];
sv.minimumZoomScale = MIN;
sv.maximumZoomScale = MAX;
self->oldScale = 1.0;
sv.delegate = self;
Então em seu scrollViewDidEndZooming:
você fizer a mesma coisa, mas para compensar a mudança de escala:
UIView* v = [sv viewWithTag:999];
[v removeFromSuperview];
CGFloat newscale = scale * self->oldScale;
self->oldScale = newscale;
[self addNewScalableViewAtScale:newscale];
sv.minimumZoomScale = MIN / newscale;
sv.maximumZoomScale = MAX / newscale;
Note que a solução fornecida por Brad tem um problema, porém: se você manter programaticamente zoom in (digamos que no evento duplo toque) e deixar o usuário manualmente (beliscar-out) diminuir o zoom, depois de algum tempo a diferença entre a verdadeira escala eo UIScrollView escala é o acompanhamento vai crescer muito grande, então o previousScale
, em algum momento cair da precisão do flutuador, que acabará por resultar em um comportamento imprevisível
Definir escala de zoom scrollView para 0 irá redefinir sua scrollView ao seu estado inicial.
self.scrollView.setZoomScale(0.0, animated: true)