Remarque : les choses ont évolué depuis que cette question a été posée; voir ici pour un bon aperçu récent.
Avant la mise en page automatique, vous pouviez modifier le point d'ancrage du calque d'une vue sans déplacer la vue en stockant le cadre, en définissant le point d'ancrage et en restaurant le cadre.
Dans un monde de mise en page automatique, nous ne définissons plus de cadres, mais les contraintes ne semblent pas à la hauteur de la tâche d'ajuster la position d'une vue à l'endroit où nous le souhaitons. Vous pouvez pirater les contraintes pour repositionner votre vue, mais lors de la rotation ou d'autres événements de redimensionnement, celles-ci redeviennent invalides.
L'idée brillante suivante ne fonctionne pas car elle crée une "Association incorrecte des attributs de mise en page (gauche et largeur)":
layerView.layer.anchorPoint = CGPointMake(1.0, 0.5);
// Some other size-related constraints here which all work fine...
[self.view addConstraint:
[NSLayoutConstraint constraintWithItem:layerView
attribute:NSLayoutAttributeLeft
relatedBy:NSLayoutRelationEqual
toItem:layerView
attribute:NSLayoutAttributeWidth
multiplier:0.5
constant:20.0]];
Mon intention ici était de définir le bord gauche de layerView
, la vue avec le point d'ancrage ajusté, à la moitié de sa largeur plus 20 (la distance que je veux insérer à partir du bord gauche de la vue supervisée).
Est-il possible de changer le point d'ancrage, sans changer l'emplacement d'une vue, dans une vue mise en page avec mise en page automatique? Dois-je utiliser des valeurs codées en dur et modifier la contrainte à chaque rotation? J'espère que non.
J'ai besoin de changer le point d'ancrage pour que lorsque j'applique une transformation à la vue, j'obtienne l'effet visuel correct.
la source
layerView
connaît sa largeur? S'en tient-il à autre chose?//Size-related constraints that work fine
- la largeur et la hauteur de la vue du calque sont dérivées de celles de la super-vue.Réponses:
[EDIT: Attention: toute la discussion qui s'ensuivra sera peut-être dépassée ou du moins fortement atténuée par iOS 8, qui ne fera peut-être plus l'erreur de déclencher la mise en page au moment où une transformation de vue est appliquée.]
Mise en page automatique et voir les transformations
La mise en page automatique ne fonctionne pas du tout bien avec les transformations de vue. La raison, pour autant que je sache, est que vous n'êtes pas censé jouer avec le cadre d'une vue qui a une transformation (autre que la transformation d'identité par défaut) - mais c'est exactement ce que fait la mise en page automatique. La façon dont la mise en page automatique fonctionne est que dans
layoutSubviews
le runtime vient se précipiter à travers toutes les contraintes et définir les cadres de toutes les vues en conséquence.En d'autres termes, les contraintes ne sont pas magiques; ils ne sont qu'une liste de choses à faire.
layoutSubviews
est l'endroit où la liste de tâches se fait. Et il le fait en définissant des cadres.Je ne peux pas m'empêcher de considérer cela comme un bug. Si j'applique cette transformation à une vue:
Je m'attends à voir la vue apparaître avec son centre au même endroit qu'avant et à la moitié de sa taille. Mais selon ses contraintes, ce n'est peut-être pas du tout ce que je vois.
[En fait, il y a une deuxième surprise ici: appliquer une transformation à une vue déclenche immédiatement la mise en page. Cela me semble être un autre bug. Ou peut-être que c'est le cœur du premier bug. Ce à quoi je m'attendrais, c'est de pouvoir m'en tirer avec une transformation au moins jusqu'au moment de la mise en page, par exemple, l'appareil est tourné - tout comme je peux m'en tirer avec une animation d'image jusqu'au moment de la mise en page. Mais en fait, le temps de mise en page est immédiat, ce qui semble tout simplement faux.]
Solution 1: aucune contrainte
Une solution actuelle est, si je vais appliquer une transformation semi-permanente à une vue (et pas simplement la remuer temporairement d'une manière ou d'une autre), pour supprimer toutes les contraintes qui l'affectent. Malheureusement, cela provoque généralement la disparition de la vue de l'écran, car la mise en page automatique a toujours lieu, et il n'y a maintenant aucune contrainte pour nous dire où placer la vue. Donc, en plus de supprimer les contraintes, j'ai défini la vue
translatesAutoresizingMaskIntoConstraints
sur OUI. La vue fonctionne désormais à l'ancienne, sans être affectée par la mise en page automatique. (Il est évidemment affecté par la mise en page automatique, mais les contraintes implicites de masque de redimensionnement automatique font que son comportement est exactement comme il l'était avant la mise en page automatique.)Solution 2: utilisez uniquement les contraintes appropriées
Si cela semble un peu drastique, une autre solution consiste à définir les contraintes pour qu'elles fonctionnent correctement avec une transformation prévue. Si une vue est dimensionnée uniquement par sa largeur et sa hauteur internes fixes, et positionnée uniquement par son centre, par exemple, ma transformation d'échelle fonctionnera comme prévu. Dans ce code, je supprime les contraintes existantes sur une sous-vue (
otherView
) et les remplace par ces quatre contraintes, en lui donnant une largeur et une hauteur fixes et en l'épinglant uniquement par son centre. Après cela, ma transformation d'échelle fonctionne:Le résultat est que si vous n'avez aucune contrainte qui affecte le cadre d'une vue, la mise en page automatique ne touchera pas le cadre de la vue - ce qui est exactement ce que vous recherchez lorsqu'une transformation est impliquée.
Solution 3: utiliser une sous-vue
Le problème avec les deux solutions ci-dessus est que nous perdons les avantages des contraintes pour positionner notre point de vue. Voici donc une solution qui résout ce problème. Commencez par une vue invisible dont le travail consiste uniquement à agir en tant qu'hôte et utilisez des contraintes pour la positionner. À l'intérieur, mettez la vue réelle sous forme de sous-vue. Utilisez des contraintes pour positionner la sous-vue dans la vue hôte, mais limitez ces contraintes aux contraintes qui ne riposteront pas lorsque nous appliquerons une transformation.
Voici une illustration:
La vue blanche est la vue de l'hôte; vous êtes censé prétendre qu'il est transparent et donc invisible. La vue rouge est sa sous-vue, positionnée en épinglant son centre au centre de la vue hôte. Maintenant, nous pouvons mettre à l'échelle et faire pivoter la vue rouge autour de son centre sans aucun problème, et en effet l'illustration montre que nous l'avons fait:
Et pendant ce temps, les contraintes sur la vue de l'hôte le maintiennent au bon endroit lorsque nous faisons pivoter l'appareil.
Solution 4: utilisez plutôt les transformations de couche
Au lieu des transformations de vue, utilisez des transformations de couche, qui ne déclenchent pas la mise en page et ne provoquent donc pas de conflit immédiat avec les contraintes.
Par exemple, cette simple animation de vue "throb" peut bien être interrompue lors de la mise en page automatique:
Même si à la fin il n'y a pas eu de changement dans la taille de la vue, le simple fait de définir sa
transform
mise en page provoque la mise en page et les contraintes peuvent faire sauter la vue. (Est-ce que cela ressemble à un bug ou quoi?) Mais si nous faisons la même chose avec Core Animation (en utilisant un CABasicAnimation et en appliquant l'animation au calque de la vue), la mise en page ne se produit pas et cela fonctionne bien:la source
J'ai eu un Isuue similaire et je viens d'entendre Back de l'équipe Autolayout d'Apple. Ils suggèrent d'utiliser l'approche de la vue conteneur suggérée par Matt, mais ils créent une sous-classe d'UIView pour écraser la mise en page Sous-vues et y appliquer un code de mise en page personnalisé - Cela fonctionne comme un charme
Le fichier d'en-tête ressemble à cela afin que vous puissiez lier votre sous-vue de choix directement à partir d'Interface Builder
et le fichier m applique le code spécial comme ça:
Comme vous pouvez le voir, il saisit le point central de la vue lors de son premier appel et réutilise cette position dans d'autres appels afin de placer la vue en conséquence. Cela écrase le code de mise en page automatique dans ce sens, qu'il a lieu après [super layoutSubviews]; qui contient le code de mise en page automatique.
Ainsi, il n'est plus nécessaire d'éviter la mise en page automatique, mais vous pouvez créer votre propre mise en page automatique lorsque les comportements par défaut ne sont plus appropriés. Bien sûr, vous pouvez appliquer des choses beaucoup plus compliquées que ce qui est dans cet exemple, mais c'était tout ce dont j'avais besoin car mon application ne peut utiliser que le mode portrait.
la source
layoutSubviews
. J'ajouterais simplement, cependant, qu'Apple ici ne fait que vous faire faire ce que je pense qu'ils auraient dû faire (dans leur implémentation d'Autolayout) tout au long.Je trouve un moyen simple. Et cela fonctionne sur iOS 8 et iOS 9.
Comme pour ajuster anchorPoint lorsque vous utilisez une mise en page basée sur un cadre:
Lorsque vous ajustez l'ancre de la vue avec la mise en page automatique, vous faites la même chose mais de manière contrainte. Lorsque anchorPoint passe de (0,5, 0,5) à (1, 0,5), le layerView se déplacera à gauche avec une distance égale à la moitié de la longueur de la largeur de la vue, vous devez donc compenser cela.
Je ne comprends pas la contrainte de la question, alors supposons que vous ajoutiez une contrainte centerX relative à superView centerX avec une constante: layerView.centerX = superView.centerX + constant
la source
Si vous utilisez la mise en page automatique, je ne vois pas comment la définition manuelle de la position servira à long terme, car la mise en page automatique finira par écraser la valeur de position que vous avez définie lors du calcul de sa propre mise en page.
Il faut plutôt modifier les contraintes de mise en page elles-mêmes pour compenser les changements produits en définissant le point d'ancrage. La fonction suivante fait cela pour les vues non transformées.
J'admets que ce n'est probablement pas tout ce que vous espériez, car généralement, la seule raison pour laquelle vous souhaitez modifier le point d'ancrage est de définir une transformation. Cela nécessiterait une fonction plus complexe qui met à jour les contraintes de mise en page pour refléter tous les modifications de cadre qui pourraient être causées par la propriété transform elle-même. C'est délicat car les transformations peuvent faire beaucoup pour le cadre. Une transformation de mise à l'échelle ou de rotation rendrait le cadre plus grand, nous aurions donc besoin de mettre à jour toutes les contraintes de largeur ou de hauteur, etc.
Si vous n'utilisez la transformation que pour une animation temporaire, alors ce qui précède peut suffire car je ne pense pas que la mise en page automatique empêchera l'animation en vol de présenter des images qui représentent des violations purement transitoires des contraintes.
la source
tl: dr: Vous pouvez créer une sortie pour l'une des contraintes afin qu'elle puisse être supprimée et ajoutée à nouveau.
J'ai créé un nouveau projet et ajouté une vue avec une taille fixe au centre. Les contraintes sont présentées dans l'image ci-dessous.
Ensuite, j'ai ajouté une sortie pour la vue qui va tourner et pour la contrainte d'alignement centre x.
Plus tard,
viewDidAppear
je calcule le nouveau point d'ancrageEnsuite, je supprime la contrainte pour laquelle j'ai une prise, j'en crée une nouvelle qui est décalée et je la rajoute à nouveau. Après cela, je dis à la vue avec la contrainte modifiée qu'elle doit mettre à jour les contraintes.
Enfin, je viens d'ajouter l'animation de rotation à la vue rotative.
Le calque en rotation semble rester centré (ce qu'il devrait) même lors de la rotation de l'appareil ou de la mise à jour des contraintes. La nouvelle contrainte et le point d'ancrage modifié s'annulent visuellement.
la source
Ma solution actuelle consiste à ajuster manuellement la position du calque dans
viewDidLayoutSubviews
. Ce code pourrait également être utilisé danslayoutSubviews
pour une sous-classe de vue, mais dans mon cas, ma vue est une vue de niveau supérieur dans un contrôleur de vue, donc cela signifiait que je n'avais pas à créer une sous-classe UIView.Cela semble être trop d’efforts, alors d’autres réponses sont les bienvenues.
la source
Inspiré de la réponse de mon mat, j'ai décidé d'essayer une approche différente. Une vue de conteneur, avec des contraintes appliquées de manière appropriée, peut être utilisée. La vue avec le point d'ancrage modifié peut ensuite être placée dans la vue du conteneur, en utilisant des masques de redimensionnement automatique et des paramètres de cadre explicites, comme dans le mauvais vieux temps.
Cela fonctionne un régal, pour ma situation de toute façon. Les vues sont configurées ici dans viewDidLoad:
Peu importe que les cadres de la vue rouge soient à zéro à ce stade, en raison du masque de redimensionnement automatique sur la vue verte.
J'ai ajouté une transformation de rotation sur une méthode d'action, et voici le résultat:
Il semblait se perdre lors de la rotation de l'appareil, j'ai donc ajouté ceci à la méthode viewDidLayoutSubviews:
la source
view.layer
tranquille et de faire tout votre travail dans une sous-couche deview.layer
. En d'autres termes, la vue ne serait qu'un hôte, et toutes les transformations de dessin et de sous-couche, etc., seraient d'un niveau inférieur, non affectées par les contraintes.Je pense que vous allez à l'encontre de l'objectif de la mise en page automatique avec cette méthode. Vous avez mentionné que la largeur et le bord droit dépendent de la vue supervisée, alors pourquoi ne pas simplement ajouter des contraintes le long de cette ligne de pensée?
Perdez le paradigme anchorPoint / transform et essayez:
La
NSLayoutAttributeRight
contrainte signifie exactement commeanchorPoint = CGPointMake(1.0, 0.5)
, et laNSLayoutAttributeWidth
contrainte est à peu près équivalente à votre code précédentNSLayoutAttributeLeft
.la source
anchorPoint
, mon point est que vous ne devriez pas l'utiliser pour les mesures. Le système de mise en page automatique UIView doit être indépendant des transformations CALayer. Donc UIView: mise en page, CALayer: apparence / animationsviewDidLayoutSubviews
devrait résoudre ce problème;position
va toujours avecanchorPoint
. Ma réponse montre simplement comment définir la contrainte de la transformation d'identité.Cette question et ces réponses m'ont inspiré à résoudre mes propres problèmes avec la mise en page automatique et la mise à l'échelle, mais avec les vues de défilement. J'ai créé un exemple de ma solution sur github:
https://github.com/hansdesmedt/AutoLayout-scrollview-scale
Ceci est un exemple d'un UIScrollView avec une pagination personnalisée entièrement réalisée dans AutoLayout et est évolutif (CATransform3DMakeScale) avec un appui long et appuyez pour zoomer. Compatible iOS 6 et 7.
la source
C'est un gros sujet et je n'ai pas lu tous les commentaires mais j'étais confronté au même problème.
J'avais une vue de XIB avec mise en page automatique. Et je voulais mettre à jour sa propriété de transformation. L'intégration de la vue dans une vue de conteneur ne résout pas mon problème car la mise en page automatique agissait de manière étrange sur la vue de conteneur. C'est pourquoi je viens d'ajouter une deuxième vue de conteneur pour contenir la vue de conteneur qui contient ma vue et y appliquait des transformations.
la source
tl; dr Supposons que vous ayez changé le point d'ancrage en (0, 0). Le point d'ancrage est maintenant en haut à gauche. Chaque fois que vous voyez le centre des mots dans la mise en page automatique, vous devriez penser en haut à gauche .
Lorsque vous ajustez votre point d'ancrage, vous changez simplement la sémantique de la mise en page automatique. La mise en page automatique n'interférera pas avec votre point d'ancrage ni vice versa. Si vous ne comprenez pas cela, vous allez passer un mauvais moment .
Exemple:
Figure A. Aucune modification du point d'ancrage
Figure B.Le point d'ancrage a été changé en haut à gauche
Les figures A et B se ressemblent exactement. Rien n'a changé. Juste la définition de quoi centre se réfère a changé.
la source