Quand mes "lignes" UIStackView sont écrasées, elles jettent AutoLayout
warnings. Cependant, ils affichent bien et rien d'autre ne va pas en dehors de ces types de journalisation:
Impossible de satisfaire simultanément aux contraintes. Probablement au moins une des contraintes de la liste suivante est une de celles que vous ne souhaitez pas. Essayez ceci: (1) regardez chaque contrainte et essayez de déterminer celle à laquelle vous ne vous attendez pas; (2) Trouvez le code qui a ajouté la ou les contraintes indésirables et corrigez-le. (Remarque: si vous voyez
NSAutoresizingMaskLayoutConstraints
que vous ne comprenez pas, reportez-vous à la documentation de la propriétéUIView
de la propriététranslatesAutoresizingMaskIntoConstraints
.)
Donc, je ne sais pas encore comment résoudre ce problème, mais cela ne semble rien casser, à part le fait d'être ennuyeux.
Est-ce que quelqu'un sait comment le résoudre? Il est intéressant de noter que les contraintes de mise en page sont assez souvent marquées avec 'masquer UISV', ce qui signifie qu'il devrait peut-être ignorer les hauteurs minimales pour les sous-vues ou quelque chose dans ce cas?
Vous avez ce problème parce que lorsque vous définissez une sous-vue à partir de UIStackView
sur masquée, elle limite d'abord sa hauteur à zéro afin de l'animer.
Je recevais l'erreur suivante:
2015-10-01 11:45:13.732 <redacted>[64455:6368084] Unable to simultaneously satisfy constraints.
Probably at least one of the constraints in the following list is one you don't want. Try this: (1) look at each constraint and try to figure out which you don't expect; (2) find the code that added the unwanted constraint or constraints and fix it. (Note: If you're seeing NSAutoresizingMaskLayoutConstraints that you don't understand, refer to the documentation for the UIView property translatesAutoresizingMaskIntoConstraints)
(
"<NSLayoutConstraint:0x7f7f5be18c80 V:[UISegmentedControl:0x7f7f5bec4180]-(8)-| (Names: '|':UIView:0x7f7f5be69d30 )>",
"<NSLayoutConstraint:0x7f7f5be508d0 V:|-(8)-[UISegmentedControl:0x7f7f5bec4180] (Names: '|':UIView:0x7f7f5be69d30 )>",
"<NSLayoutConstraint:0x7f7f5bdfbda0 'UISV-hiding' V:[UIView:0x7f7f5be69d30(0)]>"
)
Will attempt to recover by breaking constraint
<NSLayoutConstraint:0x7f7f5be18c80 V:[UISegmentedControl:0x7f7f5bec4180]-(8)-| (Names: '|':UIView:0x7f7f5be69d30 )>
Make a symbolic breakpoint at UIViewAlertForUnsatisfiableConstraints to catch this in the debugger.
The methods in the UIConstraintBasedLayoutDebugging category on UIView listed in <UIKit/UIView.h> may also be helpful.
Ce que j’essayais de faire, c’était de placer un UIView
dans mon UIStackView
contenant un incrément de UISegmentedControl
de 8 pts sur chaque bord.
Lorsque je le définissais à masqué, il tentait de contraindre la vue du conteneur à une hauteur nulle, mais parce que j'ai un ensemble de contraintes de haut en bas, il y avait un conflit.
Pour résoudre le problème, j'ai modifié la priorité des contraintes de haut en bas et de 8 pt, de 1000 à 999, de sorte que le UISV-hiding
contrainte peut alors être prioritaire si nécessaire.
J'avais un problème similaire qui n'était pas facile à résoudre. Dans mon cas, une vue de pile était intégrée à une vue de pile. UIStackView interne avait deux étiquettes et un espacement différent de zéro.
Lorsque vous appelez addArrangedSubview (), il créera automatiquement des contraintes similaires à celles-ci:
V:|[innerStackView]| | = outerStackView
V:|[label1]-(2)-[label2]| | = innerStackView
Désormais, lorsque vous essayez de masquer innerStackView, vous obtenez un avertissement concernant les contraintes ambiguës.
Pour comprendre pourquoi, voyons d'abord pourquoi pas se produit lorsque innerStackView.spacing
Est égal à 0
. Lorsque vous appelez innerStackView.hidden = true
, @Liamnichols était correct ... le outerStackView
interceptera cet appel comme par magie et créera une hauteur 0
UISV -hiding contraint avec la priorité 1000 (obligatoire). Cela consiste vraisemblablement à permettre aux éléments de la vue de pile d'être animés de manière invisible si votre code de masquage est appelé dans un bloc UIView.animationWithDuration()
. Malheureusement, il ne semble pas y avoir de moyen d'empêcher l'ajout de cette contrainte. Néanmoins, vous ne recevrez pas d'avertissement "Impossible de satisfaire simultanément les contraintes" (USSC), car les événements suivants se produisent:
Il est clair que ces 4 contraintes peuvent être satisfaites. La vue pile empile tout simplement en un pixel de hauteur 0.
Revenons maintenant à l’exemple de buggy, si nous définissons le spacing
sur 2
, Nous avons maintenant les contraintes suivantes:
La vue de pile ne peut pas avoir une hauteur de 0 pixel ni un contenu de 2 pixels. Les contraintes ne peuvent être satisfaites.
Remarque: vous pouvez voir ce comportement avec un exemple plus simple. Ajoutez simplement UIView à une vue de pile en tant que sous-vue arrangée. Puis définissez une contrainte de hauteur sur cette UIView avec une priorité de 1000. Maintenant, essayez d'appeler hide sur ça.
Remarque: quelle que soit la raison, cela ne s'est produit que lorsque ma vue de pile était une sous-vue d'un UICollectionViewCell ou d'un UITableViewCell. Toutefois, vous pouvez toujours reproduire ce problème en dehors d'une cellule en appelant innerStackView.systemLayoutSizeFittingSize(UILayoutFittingCompressedSize)
lors de la prochaine boucle d'exécution après le masquage de la vue de pile interne.
Remarque: Même si vous essayez d'exécuter le code dans une UIView.performWithoutAnimations, la vue de pile ajoutera une contrainte de hauteur 0 qui provoquera l'avertissement USSC.
Il y a au moins 3 solutions à ce problème:
spacing
par 0. Ceci est agaçant car vous devez inverser le processus (et vous rappeler de l’espacement initial) à chaque nouvelle présentation du contenu.removeFromSuperview
. Ceci est encore plus gênant car, lorsque vous inversez le processus, vous devez rappelez-vous où pour insérer l'élément supprimé. Vous pouvez optimiser en appelant seulement removeArrangedSubview, puis en vous cachant, mais il reste encore beaucoup de comptabilité à tenir.spacing
différent de zéro) dans une vue UIV. Spécifiez au moins une contrainte comme priorité non requise (999 ou moins). C'est la meilleure solution car vous n'avez aucune comptabilité à effectuer. Dans mon exemple, j'ai créé des contraintes de début, de début et de fin en 1000 entre la vue de pile et la vue wrapper, puis j'ai créé une contrainte 999 du bas de la vue de pile à la vue wrapper. Ainsi, lorsque la vue de pile externe crée une contrainte de hauteur zéro, la contrainte 999 est rompue et l'avertissement USSC ne s'affiche pas. (Remarque: Ceci est similaire à la solution de Les contentView.translatesAutoResizingMaskToConstraints de la sous-classe UICollectionViewCell doivent-ils être définis sur false
)En résumé, les raisons pour lesquelles vous obtenez ce comportement sont les suivantes:
Si Apple) (1) vous permettait de spécifier la priorité des contraintes (en particulier des espaceurs), ou (2) vous autorisait la désactivation de l’automatique UISV-masquant contrainte, ce problème serait facilement résolu.
La plupart du temps, cette erreur peut être résolue en réduisant la priorité des contraintes afin d'éliminer les conflits.
Lorsque vous définissez une vue sur masqué, le UIStackview
essaiera de l'animer. Si vous voulez cet effet, vous devez définir la bonne priorité pour les contraintes afin qu'elles ne soient pas en conflit (comme beaucoup l'ont suggéré ci-dessus).
Cependant, si vous ne vous souciez pas de l'animation (peut-être que vous la cachez dans ViewDidLoad), vous pouvez alors simple removeFromSuperview
avoir le même effet, mais sans aucun problème de contraintes, car celles-ci seront supprimées avec la vue.
J'ai rencontré les mêmes erreurs avec les vues de pile intégrées, même si tout fonctionnait bien au moment de l'exécution.
J'ai résolu les erreurs de contrainte en masquant d'abord toutes les vues de sous-pile (en mettant isHidden = true
) avant de masquer la vue de pile parent.
Cela n'a pas eu la complexité de supprimer des vues sous-arrangées, de conserver un index pour pouvoir les rajouter.
J'espère que cela t'aides.
Tout d’abord, comme d’autres l’ont suggéré, assurez-vous que les contraintes que vous pouvez contrôler, c’est-à-dire que les contraintes inhérentes à UIStackView sont définies sur la priorité 999 afin qu’elles puissent être remplacées lorsque la vue est masquée.
Si le problème persiste, il est probablement dû à l'espacement dans les vues de pile masquées. Ma solution consistait à ajouter UIView en tant qu'espaceur et à définir l'espacement UIStackView sur zéro. Définissez ensuite les contraintes View.height ou View.width (selon une pile verticale ou horizontale) sur l'espacement de StackView. .
Ensuite, ajustez les priorités de prise en charge et de résistance du contenu de vos vues nouvellement ajoutées. Vous devrez peut-être également modifier la distribution du parent StackView.
Tout ce qui précède peut être effectué dans Interface Builder. De plus, vous devrez peut-être masquer/afficher certaines des vues nouvellement ajoutées par programmation afin d'éviter tout espacement indésirable.
J'ai récemment lutté avec des erreurs de mise en page automatique lorsque je masquais un UIStackView
. Plutôt que de faire un tas de livres de comptabilité et d'empaquetage dans UIViews
, j'ai choisi de créer un point de vente pour mon parentStackView
et des points de vente pour les enfants que je souhaite masquer/afficher.
@IBOutlet weak var parentStackView: UIStackView!
@IBOutlet var stackViewNumber1: UIStackView!
@IBOutlet var stackViewNumber2: UIStackView!
Dans le storyboard, voici à quoi ressemble mon parentStack:
Il a 4 enfants et chacun des enfants a un tas de vues de pile à l'intérieur d'eux. Lorsque vous masquez une vue de pile, si elle contient également des éléments d'interface utilisateur, vous verrez un flux d'erreurs de présentation automatique. Plutôt que de me cacher, j'ai choisi de les supprimer.
Dans mon exemple, parentStackViews
contient un tableau des 4 éléments: Top Stack View, StackViewNumber1, Stack View Number 2 et Stop Button. Leurs indices dans arrangedSubviews
sont 0, 1, 2 et 3, respectivement. Lorsque je veux en cacher un, je le supprime simplement du tableau parentStackView's
arrangedSubviews
. Comme il n'est pas faible, il reste en mémoire et vous pouvez le remettre à l'index souhaité plus tard. Je ne le réinitialise pas, il reste donc suspendu jusqu'à ce que vous en ayez besoin, mais n'empêche pas la mémoire.
Donc, fondamentalement, vous pouvez ...
1) Faites glisser IBOutlets pour votre pile parent et les enfants que vous souhaitez masquer/afficher sur le story-board.
2) Lorsque vous souhaitez les masquer, supprimez la pile que vous souhaitez masquer du tableau parentStackView's
arrangedSubviews
.
3) Appelez self.view.layoutIfNeeded()
avec UIView.animateWithDuration
.
Notez que les deux dernières vues de pile ne sont pas weak
. Vous devez les garder pour quand vous les annulez.
Disons que je veux masquer stackViewNumber2:
parentStackView.removeArrangedSubview(stackViewNumber2)
stackViewNumber2.removeFromSuperview()
Puis animez le:
UIView.animate(withDuration: 0.25,
delay: 0,
usingSpringWithDamping: 2.0,
initialSpringVelocity: 10.0,
options: [.curveEaseOut],
animations: {
self.view.layoutIfNeeded()
},
completion: nil)
Si vous souhaitez "afficher" un stackViewNumber2
Plus tard, vous pouvez simplement l'insérer dans l'index souhaité parentStackView
arrangedSubViews
et animer la mise à jour.
parentStackView.removeArrangedSubview(stackViewNumber1)
stackViewNumber1.removeFromSuperview()
parentStackView.insertArrangedSubview(stackViewNumber2, at: 1)
// Then animate it
UIView.animate(withDuration: 0.25,
delay: 0,
usingSpringWithDamping: 2.0,
initialSpringVelocity: 10.0,
options: [.curveEaseOut],
animations: {
self.view.layoutIfNeeded()
},
completion: nil)
J'ai trouvé que c'était beaucoup plus facile que de faire de la comptabilité sur des contraintes, de bricoler avec des priorités, etc.
Si vous souhaitez masquer quelque chose par défaut, vous pouvez simplement le disposer dans le storyboard et le supprimer dans viewDidLoad
et le mettre à jour sans l'animation à l'aide de view.layoutIfNeeded()
.
D'après la réponse de @ Senseful, voici une extension UIStackView pour envelopper une vue de pile dans une vue et appliquer les contraintes qu'il recommande:
/// wraps in a `UIView` to prevent autolayout warnings when a stack view with spacing is placed inside another stack view whose height might be zero (usually due to `hidden` being `true`).
/// See http://stackoverflow.com/questions/32428210
func wrapped() -> UIView {
let wrapper = UIView()
translatesAutoresizingMaskIntoConstraints = false
wrapper.addSubview(self)
for attribute in [NSLayoutAttribute.Top, .Left, .Right, .Bottom] {
let constraint = NSLayoutConstraint(item: self,
attribute: attribute,
relatedBy: .Equal,
toItem: wrapper,
attribute: attribute,
multiplier: 1,
constant: 0)
if attribute == .Bottom { constraint.priority = 999 }
wrapper.addConstraint(constraint)
}
return wrapper
}
Au lieu d'ajouter votre stackView
, utilisez stackView.wrapped()
.
Je voulais cacher tout le fichier UIStackView à la fois, mais les erreurs que je rencontrais étaient identiques à celles de l'OP. Cela a résolu le problème pour moi:
for(UIView *currentView in self.arrangedSubviews){
for(NSLayoutConstraint *currentConstraint in currentView.constraints){
[currentConstraint setPriority:999];
}
}
Senseful a fourni une excellente réponse à la racine du problème ci-dessus, je vais donc aller directement à la solution.
Tout ce que vous avez à faire est de définir toutes les priorités de contrainte stackView inférieures à 1000 (999 fera le travail). Par exemple, si la vue de pile est contrainte à gauche, à droite, en haut et en bas de son aperçu, les 4 contraintes doivent avoir une priorité inférieure à 1000.
Vous avez peut-être créé une contrainte lorsque vous travaillez avec une classe de taille spécifique (par exemple, wCompact hRegular), puis vous avez créé un duplicata lors du passage à une autre classe de taille (par exemple, wAny hAny). vérifiez les contraintes des objets d'interface utilisateur dans des classes de taille différentes et voyez s'il y a des anomalies avec les contraintes. vous devriez voir les lignes rouges indiquant les contraintes en collision. Je ne peux pas mettre une photo avant d'avoir 10 points de réputation désolé: /
J'ai eu une rangée de boutons avec contrainte de hauteur. Cela se produit lorsqu'un bouton est masqué. La définition de la priorité de cette contrainte de hauteur des boutons sur 999 a résolu le problème.