Comment réinitialiser après un zoom UIScrollView?

voix
15

J'ai un graphe tracé à l' intérieur d' un UIScrollView. Il est une grande UIViewaide d' une sous - classe personnalisée de CATiledLayercomme sa couche.

Quand je zoom dans et hors de la UIScrollView, je veux le graphique pour redimensionner dynamiquement comme il le fait quand je retourne le graphique à partir viewForZoomingInScrollView. Cependant, le graphique se redessine au nouveau niveau de zoom, et je veux réinitialiser l'échelle transform 1x1 afin que la prochaine fois que l'utilisateur effectue un zoom avant, la transformée commence à partir de la vue actuelle. Si je réinitialiser la transformation à l' identité en scrollViewDidEndZooming, cela fonctionne dans le simulateur, mais jette un EXC_BAD_ACCSESsur l'appareil.

Cela ne résout même pas le problème entièrement sur le simulateur soit, parce que la prochaine fois que l'utilisateur effectue un zoom avant, la transformée se remet à zéro à tout niveau de zoom, il était, et il ressemble tellement, si je zoomé à 2x, par exemple, il est soudainement à 4x. Quand je termine le zoom, il finit à la bonne échelle, mais l'acte même de zooming est mauvais.

Alors d'abord: comment puis-je permettre le graphique de se redessiner à l'échelle standard de 1x1 après le zoom, et comment puis-je avoir un zoom lisse tout au long?

Edit: De nouveaux résultats L'erreur semble être « [CALayer retainCount]: message sent to deallocated instance»

Je ne suis jamais moi-même désaffecter toutes les couches. Avant, je ne même supprimer des vues ou quoi que ce soit. Cette erreur était jeté sur le zoom et sur rotate. Si je supprime l'objet avant la rotation et l'ajouter à nouveau par la suite, il ne jette pas l'exception. Ce n'est pas une option pour le zoom.

Créé 15/01/2009 à 21:16
source utilisateur
Dans d'autres langues...                            


7 réponses

voix
41

Je ne peux pas vous aider à se écraser, autre que vous dire de vérifier et assurez-vous que vous n'êtes pas sans le vouloir autoreleasing une vue ou une couche quelque part dans votre code. Je l'ai vu le simulateur gérer le calendrier de autoreleases différemment sur l'appareil (le plus souvent lorsque les fils sont impliqués).

La mise à l' échelle de la vue est un problème avec UIScrollViewj'ai rencontré, cependant. Lors d' un événement de pincement zoom, UIScrollViewprendra la vue que vous avez spécifié dans la viewForZoomingInScrollView:méthode déléguée et appliquer une transformation à elle. Cette transformée fournit une mise à l' échelle en douceur de la vue sans avoir à redessiner chaque image. A la fin de l'opération de zoom, votre méthode de délégué scrollViewDidEndZooming:withView:atScale:sera appelé et vous donner une chance de faire un rendu plus haute qualité de votre point de vue sur le nouveau facteur d'échelle. En général, il est suggéré que vous réinitialiser le transform sur votre point de vue d'être CGAffineTransformIdentityet que votre vue se redessiner manuellement à la nouvelle échelle de taille.

Cependant, cela pose un problème parce que UIScrollViewne semble pas contrôler l'affichage du contenu de transformation, ainsi de suite l'opération de zoom , il définit la transformation de l'affichage du contenu à tout ce que le facteur d'échelle global est. Puisque vous avez Redessiné manuellement votre point de vue au dernier facteur d'échelle, il aggrave la mise à l' échelle, qui est ce que vous voyez.

Pour contourner ce problème, j'utilise une sous-classe UIView pour moi de contenu avec les méthodes suivantes définies:

- (void)setTransformWithoutScaling:(CGAffineTransform)newTransform;
{
    [super setTransform:newTransform];
}

- (void)setTransform:(CGAffineTransform)newValue;
{
    [super setTransform:CGAffineTransformScale(newValue, 1.0f / previousScale, 1.0f / previousScale)];
}

où previousScale est une variable d'instance de flotteur de la vue. Je mets en œuvre alors la méthode de zoom délégué comme suit:

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

En faisant cela, les transformations envoyées à l'affichage du contenu sont ajustés en fonction de l'échelle à laquelle la vue a été redessinée dernière. Lorsque le pincement zoom est effectué, la transformation est remis à zéro à une échelle de 1,0 en contournant l'ajustement de la normale setTransform:méthode. Cela semble fournir le comportement de mise à l' échelle correcte tout en vous permettant de dessiner une vue nette à la fin d'un zoom.

MISE A JOUR (7/23/2010): iPhone OS 3.2 et au- dessus ont modifié le comportement des vues de défilement en ce qui concerne le zoom. Maintenant, un UIScrollViewrespectera l'identité transformons que vous appliquez à une vue du contenu et de fournir uniquement le facteur d'échelle relative -scrollViewDidEndZooming:withView:atScale:. Par conséquent, le code ci - dessus pour une UIViewsous - classe est nécessaire uniquement pour les appareils fonctionnant sous les versions iPhone OS de plus de 3,2.

Créé 16/01/2009 à 20:29
source utilisateur

voix
12

Merci à toutes les réponses précédentes, et voici ma solution.
Mettre en œuvre UIScrollViewDelegate méthodes:

- (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 est ma sous - classe de UIView
  • tmvScrollView - sortie à UIScrollView
  • mettre maximumZoomScale et minimumZoomScale avant
  • créer previousScale variable d'instance et définissez sa valeur à 1.0f

Fonctionne parfaitement pour moi.

BR, Eugene.

Créé 02/08/2011 à 17:33
source utilisateur

voix
5

J'ai une discussion détaillée de la façon (et pourquoi) zooming UIScrollView travaille à github.com/andreyvit/ScrollingMadness/ .

(Le lien contient également une description de la façon de zoomer programme UIScrollView, comment émuler la pagination photo de style bibliothèque + zoom + défilement, un exemple de projet et la classe ZoomScrollView qui encapsule une partie de la magie du zoom.)

Citation:

UIScrollView n'a pas de notion d'un « niveau de zoom », parce que chaque sous-vue, il contient peut avoir son propre niveau de zoom. Notez qu'il n'y a pas de champ dans UIScrollView pour maintenir le niveau de zoom. Cependant, nous savons que quelqu'un stocke ce niveau de zoom, parce que si vous pincez zoom un sous-vue, puis réinitialiser son transform à CGAffineTransformIdentity, puis pincer à nouveau, vous remarquerez que le niveau de zoom précédent de la sous-vue a été restauré.

En effet, si vous regardez le démontage, il est UIView qui stocke son propre niveau de zoom ( à l' intérieur objet UIGestureInfo pointé par le champ _gestureInfo). Il dispose également d' un ensemble de belles méthodes non documentées comme zoomScaleet setZoomScale:animated:. (Rappelez - vous, il a aussi un tas de méthodes liées à la rotation, peut - être que nous obtenons le geste de rotation soutenir un jour bientôt.)

Cependant, si nous créons une nouvelle UIView juste pour zoomer et ajouter notre vision réelle zoomable comme son enfant, nous serons toujours commencer par le niveau de zoom 1.0. Ma mise en œuvre de programmes zooming est basée sur cette astuce.

Créé 08/05/2009 à 00:04
source utilisateur

voix
4

Je cherchais juste pour remettre la charge par défaut échelle de zoom, parce que quand je l'agrandir, scrollview est d'avoir même échelle de zoom pour la prochaine fois jusqu'à ce qu'il soit désallouée.

-(void) viewWillAppear:(BOOL)animated {
      [self.scrollView setZoomScale:1.0f];
}

Changé le jeu.

Créé 03/06/2015 à 06:53
source utilisateur

voix
3

Une approche assez fiable, appropriée pour iOS 3.2 et 4.0 et versions ultérieures, est la suivante. Vous devez être prêt à fournir votre point de vue évolutif (le chef de la sous - vue défilante) dans une échelle demandée. Puis, scrollViewDidEndZooming:vous enlèverez la version transformée échelle floue de ce point de vue et le remplacer par une nouvelle vue attirée sur la nouvelle échelle.

Tu auras besoin de:

  • Un ivar pour maintenir l'échelle précédente; appelons-le oldScale

  • Un moyen d'identifier le point de vue évolutif, à la fois pour viewForZoomingInScrollView:et scrollViewDidEndZooming:; ici, je vais appliquer une balise (999)

  • Une méthode qui crée la vue évolutif, les balises, et il insère dans la vue de défilement (voici que je vais l' appeler addNewScalableViewAtScale:). Rappelez - vous, il doit tout faire selon l'échelle - il taille la vue et ses sous - vues ou dessin et tailles de contentSize de la vue de défilement pour correspondre.

  • Constantes pour la minimumZoomScale et maximumZoomScale. Ceux-ci sont nécessaires parce que quand nous remplaçons la vue réduite par la version détaillée plus, le système va penser que l'échelle actuelle est 1, donc nous devons le tromper en permettant à l'utilisateur à l'échelle de la vue vers le bas.

Très bien alors. Lorsque vous créez la vue de défilement, vous insérez le point de vue évolutif à l' échelle 1.0. Cela pourrait être dans votre viewDidLoad, par exemple ( svest la vue de défilement):

[self addNewScalableViewAtScale: 1.0];
sv.minimumZoomScale = MIN;
sv.maximumZoomScale = MAX;
self->oldScale = 1.0;
sv.delegate = self;

Ensuite , dans votre scrollViewDidEndZooming:vous faites la même chose, mais pour compenser le changement d'échelle:

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;
Créé 12/11/2010 à 06:33
source utilisateur

voix
2

Notez que la solution fournie par Brad a un problème cependant: si vous continuez programme zoomer (disons le double événement du robinet) et laisser l' utilisateur manuellement (pincement-out) effectuer un zoom arrière, après un certain temps la différence entre la vraie échelle et l'échelle UIScrollView est suivi va croître trop grand, donc la previousScalevolonté à un certain point chute de la précision du flotteur qui finira par entraîner un comportement imprévisible

Créé 28/07/2009 à 04:43
source utilisateur

voix
0

Swift 4. * et Xcode 9.3

Réglage ScrollView échelle de zoom à 0 réinitialisera votre ScrollView à son état initial.

self.scrollView.setZoomScale(0.0, animated: true)
Créé 29/06/2018 à 05:21
source utilisateur

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more