utiliser pinch pour zoom in/out sans UIScrollView

vico92vico92 Membre
19:57 modifié dans API UIKit #1
Bonjour,

J'utilise la méthode décrite ci-dessous pour zoomer/dezoomer plusieurs Views simultanément (pour des raisons précises je ne souhaite pas utiliser la classe standard UIScrollView).

L'effet de zoom/dezoom en lui-même fonctionne très bien, mais ce que je voudrais, c'est réussir à  garder la valeur d'echelle obtenue après un premier pinch comme point de départ pour un second pinch. (exactement comme avec les images de la photothèque de base).


Avec le code ci-dessous, on obtient un changement d'échelle "en prise directe" avec la distance entre les 2 doigts...



Chaque View est transformMakeScale par la variable "scaleRatio".

"scaleRatio" varie en fonction de la distance entre 2 doigts sur l'écran via ce code:
<br />- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {<br />	<br />	if ([touches count] == 2) {<br />		NSArray *twoTouches = [touches allObjects];<br />		UITouch *first = [twoTouches objectAtIndex:0];<br />		UITouch *second = [twoTouches objectAtIndex:1];<br />		<br />		initialDistance = [self distanceBetweenTwoPoints:[first locationInView:self] toPoint:[second locationInView:self]];<br />	}<br />}<br /><br />	<br />- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {<br /><br />	if ([touches count] == 2) {<br />		<br />		NSArray *twoTouches = [touches allObjects];<br />		UITouch *first = [twoTouches objectAtIndex:0];<br />		UITouch *second = [twoTouches objectAtIndex:1];<br />		<br />		currentDistance = [self distanceBetweenTwoPoints:[first locationInView:self] toPoint:[second locationInView:self]];<br />	}<br />	<br />	finalDistance = (currentDistance - initialDistance);<br />	ratioScale = finalDistance/300;<br />}<br /><br />


J'ai le crâne qui fume sur le problème depuis hier, chui HS moué, lol  B)

Réponses

  • zoczoc Membre
    juin 2009 modifié #2
    Bah moi déjà , pour calculer le ratio, je diviserais currentDistance par initialDistance (pour avoir un pourcentage de réduction/agrandissement).

    Ensuite, pour avoir un zoom tenant compte de l'opération précédente, je multiplierais le pourcentage obtenu lors du précédent zoom par le pourcentage du zoom courant.

    Maintenant, tout ceci n'est qu'une réflexion "sur l'instant", vu que je n'ai jamais codé une telle chose. Peut-être que je suis complètement "à  coté de la plaque"  ;)

    Edit: Le problème de ma solution c'est que le zoom n'est pas linéaire, et donc ne réagira pas comme celui de UISCrollView...
  • vico92vico92 Membre
    19:57 modifié #3
    dans 1244386563:

    Bah moi déjà , pour calculer le ratio, je diviserais currentDistance par initialDistance (pour avoir un pourcentage de réduction/agrandissement).

    Ensuite, pour avoir un zoom tenant compte de l'opération précédente, je multiplierais le pourcentage obtenu lors du précédent zoom par le pourcentage du zoom courant.

    Maintenant, tout ceci n'est qu'une réflexion "sur l'instant", vu que je n'ai jamais codé une telle chose. Peut-être que je suis complètement "à  coté de la plaque"  ;)

    Edit: Le problème de ma solution c'est que le zoom n'est pas linéaire, et donc ne réagira pas comme celui de UISCrollView...


    Slt Zoc, je n'ai pas besoin du ratio currentDistance/initialDistance, ce qui m'interesse là  c'est la distance parcourue par les doigts par rapport à  leur position initiale, c'est donc bien une soustraction qu'il faut ici.

    Pour le zoom, ce que tu proposes ne fonctionnera pas non-plus, mais merci de t'être penché sur la question, si tu as une autre idée ^^
  • Eric P.Eric P. Membre
    19:57 modifié #4
    Bonjour,

    Dans iPocket Draw, je fais comme cela :

    Dans touchesBegan, je calcule previousDistance = distance entre les 2 doigts.

    Puis dans touchesMoved, je calcule la nouvelle distance "distance" entre les 2 doigts et je fais zoomFactor = zoomFactor * distance / previousDistance;

    Ca fonctionne pas trop mal.

    Eric
  • vico92vico92 Membre
    juin 2009 modifié #5
    Bon, alors comme ce que tu dis correspond à  ce que disait Zoc, j'imagine que vous avez raison (mes plates excuses à  toi, Zoc) cependant je n'obtiens pas quelque-chose de coherent pour l'instant, quand le facteur zoom entre en jeu l'image disparaà®t carrément, j'ai tenté de multiplier le zoom par plusieurs facteurs pour m'assurer que l'image ne devenait tout simplement pas minuscule, mais ce n'est pas ça...

    Je vais continuer mes recherches autour de ta formule zoom=zoom*finalDistance/initialDistance

    petite question:
    tu appliques ta valeur zoom à  l'image avec un CGAffineTransformMakeScale (zoom, zoom) ?

    EDIT: autre question: avec "zoomFactor = zoomFactor * distance / previousDistance;" la valeur zoomFactor s'auto-incrémente sans cesse, ce qui aurait pour affet que l'image continue de grandir même si tu arrête d'éloigner tes doigts l'un de l'autre.... Ce qui m'étonne aussi, c'est que distance/previousDistance donne probablement une valeur entre 2 et 6, disons, et là  je comprends encore moins comment cette equation peut renvoyer un facteur d'agrandissement ou de reduction qui serait raisonnable... B)

    Tu peux m'en dire un peu plus, steuplééé ? En plus je me doute que tu sais bien de quoi tu parles vu que ton appli est bel et bien là  et que ça marche (bravo d'ailleurs)
  • Eric P.Eric P. Membre
    19:57 modifié #6
    Oui désolé j'ai oublié un bout.
    A chaque passage dans touchesMoved, il y a aussi un "previousDistance = distance;"
    Par contre je n'utilise pas CGAffineTransformMakeScale puisque mon dessin est fait avec des objets lignes, polygones, rectangles, textes,... que je redessine à  chaque fois en appliquant zoomFactor sur leurs coordonnées et épaisseur.

    Eric
  • vico92vico92 Membre
    19:57 modifié #7
    dans 1244402588:

    A chaque passage dans touchesMoved, il y a aussi un "previousDistance = distance;"


    Ah bon ? Mais dans ce cas, pourquoi on trouve au même endroit (toucheMoved) :
    zoomFactor = zoomFactor * distance / previousDistance;
    et:
    previousDistance = distance;

    Je comprend plus rien du tout là  moi... :'(
  • Eric P.Eric P. Membre
    19:57 modifié #8
    Pour que la variation du zoom soit progressive car sans le previousDistance = distance; elle serait exponentielle (je pense).

    Pour obtenir cette formulation, j'ai du en tester pas mal avant.

    Mais votre besoin est peut-être différent du mien.

    Eric
  • vico92vico92 Membre
    19:57 modifié #9
    J'ai téléchargé ton appli et c'est bien la même chose que je voudrais obtenir.
    Ce que je disais dans mon message précédent c'est que si:
    previousDistance = distance;

    alors :
    distance / previousDistance = 1

    et donc l'équation suivante devient suspecte, non ?:
    zoomFactor = zoomFactor * distance / previousDistance;

    Merci, désolé d'insister, mais j'ai besoin de bien comprendre ^^
  • AliGatorAliGator Membre, Modérateur
    19:57 modifié #10
    A mon avis ce qu'il suggérait c'était de mettre "previousDistance = distance"... après le calcul du nouveau zoomFactor.

    Puisqu'on multiplie le zoomFactor actuel par le ratio distance/previousDistance, et donc qu'on le calcule d'après le zoomFactor précédent, il faut à  chaque "pas" utiliser la distance du pas précédent pour le calcul (d'où previousDistance = distance à  la fin du code pour qu'au passage précédent ce soit la distance du pas d'avant qu'on utilise en tant que previousDistance).

    Une autre solution serait de ne pas multiplier le zoomFactor précédent par le ratio entre les 2 pas, mais de le recalculer depuis le début. Ainsi au lieu de faire
    zoomFactor = zoomFactor * (distance/previousDistance);<br />// multiplier le zoom actuel (celui appliqué au pas précédent) par le ratio entre le pas courant et le pas précédent pour avoir le nouveau zoom<br />previousDistance = distance; // for next step
    
    Tu pourrais faire :
    zoomFactor = distance/firstDistance;
    
    et initialiser firstDistance dans le touchBegan (et donc ne pas le réajuster à  chaque pas). Ainsi au lieu de cumuler les ratios intermédiaires à  chaque "pas", à  chaque touchMoved quoi, tu calcules directement le ratio entre la distance actuelle et la distance initialisée au début du mouvement.

    Ca marche aussi... sauf qu'en faisant ceci on repart de zéro à  chaque touch gesture (à  chaque touchBegan). Ainsi si on commence le touch avec une distance de 20 entre les 2 touches, et qu'on écarte les doigts, ça va passer à  30, soit un zoom de 30/20=1.5, puis on continue d'écarter les doits ça va passer à  40, donc un zoom de 40/20 = 2... mais quand on relâche les doigts puis les repose, et qu'on démarre avec une distance de 40, ça va repartir sur un zoom de 1, au lieu de repartir sur le zoom actuel de 2 et continuer à  le faire évoluer.

    Alors qu'en faisant avec la méthode du cumul, on a d'abord previousDistance = 20, puis lors du premier déplacement on a distance = 30 donc zoomFactor = zoomFactor * 30/20 = 1.5 (si zoomFactor était à  1 au début), mais on remplace ensuite previousDistance par 30 et non plus 20... du coup au 2e déplacement si on a une distance de 40, ça fait zoomFactor = 1.5 * (40/30) = 2, donc comme dans le cas d'avant avec l'autre solution. Sauf que si on relâche les doigts et qu'on recommence une touch gesture, si on commence avec previousDistance = 40, ça va repartir d'un zoomFactor = zoomFactor * distance/previousDistance = zoomFactor*40/40 = 2, donc ça va bien repartir du zoomFactor actuel ;)
  • Eric P.Eric P. Membre
    19:57 modifié #11
    Comme l'a expliqué AliGator, previousDistance = distance; est placé après le calcul de zoomFactor alors que distance est calculé avant d'après la position des doigts. Donc dès que les doigts bougent distance est bien différent de previousDistance et le zoom change.

    Pour la seconde solution proposée par AliGator, je ne sais plus si je l'ai essayée ou pas.

    Eric
  • vico92vico92 Membre
    juin 2009 modifié #12
    dans 1244469197:

    Ca marche aussi... sauf qu'en faisant ceci on repart de zéro à  chaque touch gesture

    Oui c'est ça, c'est bien le probleme que j'ai, à  chaque "touch Gesture", ça repart de zéro, mais je comprend mieux maintenant, merci Ali (tu vas toujours au fond des choses en prenant le temps de bien expliquer, et ça c'est très appréciable^^).

    En fait je croyais que tout le code contenu dans touchMoved s'executait en simultanée, et qu'à  cause de previousDistance = distance, distance/previousDistance serait toujours égal à  1, alors que non, d'après ce que vous me dites, l'exécution du code se fera dans son ordre d'écriture.

    Dans ta 2eme solution, tu suggeres d'initialiser firstDistance dans le touchBegan, c'est exactement ce que j'avais fait, et les conséquences sont celles décrites.

    Donc en résumé:
    1-je n'écris rien dans le touchBegan
    2-Dans touchMoved les 2 variables: "distance" et "previousDistance" calculent toutes les 2 la distance entre 2 doigts.
    3-Toujours dans touchMoved, juste après
    zoomFactor = zoomFactor * (distance/previousDistance);
    j'ajoute:
    previousDistance = distance;

    Bon, je test ça dès ce soir et je vous dis ce qu'il en est.

    Merci bcp à  vous 2 les gars ^^



  • AliGatorAliGator Membre, Modérateur
    19:57 modifié #13
    Bah sinon tu peux utiliser ma solution... qui était la tienne à  l'origine... mais rajouter alors un "zoomAtGestureStart" que tu mets à  jour : dans le touchBegan tu mémorises le zoomFactor actuel dedans, dans le touchMoved tu fais ton calcul habituel... mais en multipliant par zoomAtGestureStart, justement pour cumuler le facteur de zoom de cette gesture au facteur de zoom global enfin sa valeur avant de commencer la gesture, quoi, et comme ça tu cumules l'action ponctuelle du zoom effectué par la gesture courante avec le zoom global tel qu'il évolue au fur et à  mesure que tu cumules les gestures.

    Pseudo-code :
    -(void)touchBegan:...<br />{<br />&nbsp; zoomFactorAtGestureStart = zoomFactor;<br />&nbsp; distanceAtGestureStart = calculDistance(pt1, pt2);<br />}<br /><br />[code](void)touchMoved:...<br />{<br />&nbsp; distance = calculDistance(pt1,pt2);<br />&nbsp; CGFloat zoomRatioForThisGesture = distance / distanceAtGestureStart;<br />&nbsp; zoomFactor = zoomAtGestureStart * zoomRatioForThisGesture;<br />}
    
  • vico92vico92 Membre
    19:57 modifié #14
    Cool, ça marche !!  <3 <br />
    Merci ^^
Connectez-vous ou Inscrivez-vous pour répondre.