Bug ou mauvais code ? :/

« hello world !! »

je suis sur un petit projet pour ma première app iOS, cependant il y a un bug que je n’arrive pas a résoudre depuis plusieurs semaines…

Très simplement…
lorsque je modifie le contenu d’un UILabel avec un .text, le contenu change mais d’autres objets de mon UIView change de position, et plus exactement, reviennent à leurs positions d’origine du storyboard…
Si vous regardez le code tel qu’il est en PJ, je reste appuyé sur mon bouton de mouvement, ainsi je prend += 1 en y toutes les 0.01 sec, je devrais donc avoir un mouvement continu.
Et pour une raison que j’ignore le timer du mouvement se calibre sur le timer du score (toutes les secondes) qui réinitialise la position de mon image…

j’ai fais des recherches sur le sujet… mais impossible d’en comprendre le sens…

Je précise qu’il y a qu’un seul bouton, avec 2 action,

touch down
& >> touch up inside

je vous ai fait un projet blanc avec la reproduction du problème, en espérant trouver de l’aide ici :confused:

(j’ai pas pu joindre de la vidéo ici pour voir sur simulateur, je peux envoyer video de 7MO par mail pour explication, go MP)

merci !!! :smiley:

Nico

Je n’ai pas cherché la réponse à ta question, j’ai juste un conseil : abandonne cette technique d’animation old school !

Utilise plutôt les fonctions d’animations intégré des vues iOS.

https://developer.apple.com/documentation/uikit/uiviewpropertyanimator

C’est simple à utiliser et infiniment plus efficace qu’un bricolage avec un Timer toutes les 10 ms. Déjà parce que iOS exécute l’animation à 60 Hz, ce qui produit une grande fluidité visuelle.

Et par la même occasion, pourquoi ne pas passer à SwiftUI à ce stade de ton apprentissage ?

1 J'aime

Je pense que Draken est vraiment de bon conseil. Passe à SwiftUI.

Mais, pour répondre à ta question, si tu mets repeats à true, tes timers bouclent à l’infini, ce qui doit avoir des effets sans doute non désirés. Je n’ai pas vraiment suivi le fil de ton programme, mais je ne suis pas sûr que tu aies voulu ça.

1 J'aime

Au fait, quel est ton objectif à moyen terme ? Créer des applications iOS classiques ou des jeux ? J’ai regardé ton code et c’est pour un jeu. UIKit n’est pas franchement adapté à cet usage. On peut faire des petites choses sympathiques, mais ça reste du bidouillage inapproprié à des projets vraiment intéressant.

C’est encore pire avec SwiftUI, dont la simplicité très orienté applications classiques, rend difficile la réalisation des jeux.

Pour ça, il y a SpriteKit, (template Game iOS d’xcode), conçu pour faciliter la création des jeux 2D. La philosophie d’utilisation est très différente d’une application UIKit, mais une fois maitrisé c’est ultra-puissant : sprites de toutes tailles, effets spéciaux, scrolling multi-directionnel, moteur physique (pour avoir des objets rebondissant les uns sur les autres, avec gestion automatique des collisions), moteur de particules pour avoir de superbes explosions et effets de lumière, etc …

Et une gestion du son très efficace, et des effets spéciaux numériques (comme la spatialisation des sources sonores).

On peut aussi utiliser SceneKit, le grand frère de SpriteKit pour créer des jeux 3D. Mais bon, il n’est pas recommandé de se jeter tout de suite dans le grand bain.

Merci pour vos réponse,

Pour tout vous dire, j’ai eu connaissance de SpriteKit il y a peu,

C’est ma toute première apps, donc au delà d’un projet de grande envergure (même si le projet est viable) c’est aussi un support d’apprentissage pour moi,

SwiftUI vs UIkit… je sais pas
Uikit est plus universel non ? Vous pensez que l’un remplacera l’autre ? Quel avantages inconvénients ?

Même si je n’ai rien de figer pour le moment en orientation de langage a apprendre, j’aimerais finir mon apps sur uikit et résoudre ce petit problème tout nul ^^ j’ai bien une solution mais c’est du bricolage…

Je cherche à comprendre.

Merci

:smiley:

SwiftUI a été conçu par Apple pour être le successeur d’UIKit, destiné à le remplacer à moyen terme. Si ton objectif est de devenir développeur professionnel, il est important de connaître UIKit, car la plupart des anciens projets ont été conçus avec, il y aura un gros travail de maintenance du code dans les années à venir.

Si ton but est de créer des applications par toi-même, il faut s’orienter vers SwiftUI, plus simple à apprendre et nettement moins prise de tête.


UIKit, SwiftUI et SpriteKit ne sont pas des langages de programmation, mais des framework graphiques, c’est-a-dire des outils permettent de créer des interfaces graphiques. Le langage a apprendre est toujours le même : Swift, a ne pas confondre avec SwiftUI !

D’ailleurs on peut mixer plusieurs frameworks graphiques dans une même application. Exemple : une application SwiftUI peut contenir une vue SpriteKit, où l’on pose des objets SpriteKit (sorites, effets spéciaux, scrollings, etc…).

merci pour vos réponses !!
j’aime
je vais me pencher sur SpriteKit et SwiftUI

Hello Nicolas,

Bon même si effectivement tu pourras faire plus propre avec les frameworks UI dédié aux jeux, il y a une solution à ton problème.

As-tu utilisé les contraintes autoLayout pour position l’emplacement de ton player ?
Si oui, ce n’est pas le center.y de ton player qu’il faut déplacer, mais plutot modifier la constante de ta contrainte autoLayout.

Comme je n’ai pas le storyboard je n’ai pas toutes les informations.

@Neldion
ce que tu dis m’intéresses, pourquoi les contraintes seraient mieux ?
le center.y/x utilise les coordonnées avec une base x: 0 y: 0, plus simple non ?
les contraintes se bases sur la taille de l’écran principalement mais la comme ça je vois ça plus complexe… tu peux me détailler un peu ton point de vue ?

sinon je te transmet le dossier :slight_smile:

merci !! :smiley:

Ce que @Neldion veut dire c’est que si le mode de positionnement automatique par contrainte (autolayout) d’un objet graphique a été activé par StoryBoard, toutes les modifications d’états par code sont annulés par le système.

Positionnement par contrainte ou positionnement par code sont mutuellement incompatibles. C’est fromage ou dessert, pas les deux en même temps !

Si les contraintes sont activées pour un objet, il faut utiliser les animations d’UIKit pour l’animer, ou utiliser une contrainte paramétrable dont l’équation dépend d’une variable. Pas simple pour un novice !

@Draken
je suis d’accord avec toi
« Positionnement par contrainte ou positionnement par code sont mutuellement incompatibles. C’est fromage ou dessert, pas les deux en même temps ! »
si tu regards le dossier que j’ai envoyé, effectivement tous mes objets ont une contrainte auto layout de base. les objets que je déplace avec le code à l’aide de center.x/y n’ont alors plus de contraintes ça je comprend. mais comment expliquer que 2 objets différents agissent entre eux sans lien dans le code ?

l’un est un label avec contrainte layout non modifié par la suite dans le code
le cube lui a des contraintes de bases qui sont modifier (avec center.x/y par un bouton.
mais comment expliquer que la modification du text du label réinitialise la position du cube à son emplacement d’origine (contraintes layout) ?

pour info,
mon apps dans lequel je reproduit le problème est un code d’environ 2600 lignes, avec beaucoup plus d’objets en mouvement mais pas tous, UI Image principalement, UI Button etc… je n’ai aucun bug !! sinon le fait de modifier le contenu du label.text …

au dela de tout ce qui a été évoque plus haut, j’essaie just de comprendre parce que je suis sur qu’il y a une explication toute bête ^^
je garde en tête le SpritKit

merci pour l’aide

@NicolasChanel, je viens de regarder ce que tu as partagé.
Pourquoi cela ne fonctionne pas ?
C’est très simple (enfin j’espère que ce sera suffisamment clair :slight_smile: )
chaque update de ton titleScore le layout de ton viewController va s’update (par exemple ça passe par viewDidLayoutSubviews) et donc la position de ton player va être reset par rapport aux contraintes que tu as configuré.
Si tu as placé ton player via des contraintes, il faut du coup jouer sur les contraintes pour le déplacé afin de ne pas être impacté par ces refresh de layout.

Sinon il faut que ton player ne soit pas positionné via des contraintes. Il faut donc lui donner la bonne frame à l’init de ta page.

Dernière solution, abandonner les Timer et utiliser les animation.

Pour rappel, le fait de modifier le center.y ne modifie pas les contraintes autoLayout. Le center.y déplace l’objet, mais ne modifie pas la contrainte. Du coup si le layout se met à jour, ça remet tes objets par rapport aux contraintes.

@Neldion
« Dernière solution, abandonner les Timer et utiliser les animation. »
je pense que c’est une excellente suggestion !!
malheureusement je n’en suis pas encore au stade de maitriser les animations UIKit (j’ai pas encore trop regardé non plus)…
De faite, j’ai l’immense plaisir de vous informez qu’avoir entre 10 et 15 Timers en simultanés, ça marche très bien !! >D
bon je pense que le code vous donnerait des allergies, mais ça marche !!
merci pour l’explication :slight_smile:

question bete… il y a pas une ligne de code dispo pour empêcher la Maj du layout du ViewController sur certain objets ??

Je viens de tester ton application. Contrairement à mes souvenirs (je n’ai pas touché à ça depuis des années), le changement de position d’un objet par code, n’entraine pas le recalcul immédiat des contraintes. C’est une opération complexe consommant des ressources, iOS ne le fait que s’il estime que c’est nécessaire.

C’est pourquoi ton animation fonctionne correctement pendant un moment très court. Ensuite tu écris un texte dans le Label, ce qui provoque le recalcul des contraintes, et remet l’objet player dans sa position « normale », définie par les contraintes du Storyboard.

Une solution

L’équation de placement de ta contrainte est :

Player.centerY = centerY

En fait, c’est une version simplifiée. Pour connaître la formule complète d’une contrainte, il faut la sélectionner et utiliser l’inspecteur d’attributs (en haut à droite de l’écran).


La formule complète est :

Player.CenterY = Superview.Center Y + Constant

La constante est égale à zéro, ce qui ne modifie pas l’affichage.


En écrivant une valeur dans cette constante, on modifie la contrainte et donc la position de l’objet graphique.

Pour agir sur cette contrainte, j’ai commencé par tirer un Outlet dans le code, exactement comme pour un composant graphique. Je lui ai donné le nom de :

PlayerCenterYConstraint


Cela modifie aussi le nom de la contrainte dans le Storyboard

L’outlet permet d’agir par code sur les paramètres de la contrainte. Pour régler ton problème, il suffit de modifier la valeur de la constante.


Je viens de tester, ça fonctionne parfaitement.

——-
Édit : j’ai tapé le code à l’arrache. En relisant le post, je me rend compte que j’ai utilisé une majuscule pour le nom de l’outlet. Très mauvaise idée ! Cela doit commencer par une minuscule, comme n’importe quelle variable, selon les conventions d’écriture de Swift, et de la programmation objet en général.