utiliser pinch pour zoom in/out sans UIScrollView
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:
J'ai le crâne qui fume sur le problème depuis hier, chui HS moué, lol
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
Connectez-vous ou Inscrivez-vous pour répondre.
Réponses
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 ^^
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
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...
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)
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
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...
Pour obtenir cette formulation, j'ai du en tester pas mal avant.
Mais votre besoin est peut-être différent du mien.
Eric
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 ^^
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 Tu pourrais faire : 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
Pour la seconde solution proposée par AliGator, je ne sais plus si je l'ai essayée ou pas.
Eric
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 ^^
Pseudo-code :
Merci ^^