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.

Foi útil?

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 para scrollViewDidEndZooming:; 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

Swift 4. * e Xcode 9.3

Definir escala de zoom scrollView para 0 irá redefinir sua scrollView ao seu estado inicial.

self.scrollView.setZoomScale(0.0, animated: true)
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top