Mes collègues, J'ai des problèmes avec AutoLayout dans Interface Builder (Xcode 5/iOS 7) . C'est très basique et important, je pense donc que tout le monde devrait savoir comment cela fonctionne correctement. S'il s'agit d'un bogue dans Xcode, il s'agit d'un problème critique!
Donc, chaque fois que j'ai une hiérarchie de vues comme celle-ci, j'ai des problèmes:
>UIViewController
>> UIView
>>>UIScrollView
>>>>UILabel (or any other comparable UIKit Element)
UIScrollView a des contraintes solides, par exemple 50 px de chaque côté (pas de problème) . Ensuite, j'ajoute une contrainte Top Space à UILabel (pas de problème) (et je peux même épingler hauteur/largeur de l'étiquette, rien ne change , mais devrait être inutile en raison de la taille intrinsèque du label)
Le problème commence lorsque j'ajoute une contrainte de fin à UILabel:
Espace de fuite à: Superview égal: 25
Maintenant, deux avertissements se produisent - et je ne comprends pas pourquoi:
A) Ambiguïté de la taille du contenu défilant (la vue défilement a une hauteur/largeur de contenu défilable ambiguë)
B) Vues mal placées (libellé attendu: x = -67 réel: x = 207
J'ai fait cet exemple minimal dans un nouveau projet que vous pouvez télécharger et j'ai joint une capture d'écran. Comme vous pouvez le constater, Interface Builder s'attend à ce que l'étiquette se trouve en dehors de la limite de UIScrollView (le rectangle en pointillé orange). La mise à jour du cadre de l'étiquette avec l'outil de résolution de problèmes le déplace ici.
Remarque: si vous remplacez UIScrollView par un UIView, le comportement est celui attendu (le cadre de l'étiquette est correct et conforme à la contrainte). Donc, il semble y avoir un problème avec UIScrollView ou je manque quelque chose d'important.
Lorsque je lance l'application sans mettre à jour le cadre de l'étiquette, comme suggéré par IB, celle-ci est correctement positionnée, exactement là où elle est supposée se trouver et UIScrollView est consultable en défilement . ne défile pas.
Aidez-moi Obi-Wan Kenobi! Pourquoi cette mise en page ambiguë? Pourquoi la vue égarée?
Vous pouvez télécharger l'exemple de projet ici et essayer de comprendre ce qui se passe: https://github.com/Wirsing84/AutoLayoutProblem
REMARQUE: sur iOS> 10.0, cela n'a pas été testé. par conséquent, la solution pourrait être incomplète dans certaines situations.
Donc, je viens de trier de cette façon:
Dans UIScrollView
add a UIView
(nous pouvons appeler cela contentView
);
Dans cette contentView
, définissez les marges supérieure, inférieure, gauche et droite sur 0 (bien sûr à partir de la scrollView
qui est la superView
); Set aligne également le centre horizontalement et verticalement;
Fini.
Vous pouvez maintenant ajouter toutes vos vues dans cette contentView
et la contentSize
de la scrollView
sera automatiquement redimensionnée en fonction de la contentView
.
Mettre à jour:
Cette erreur m'a pris un certain temps à repérer. Au départ, j'ai essayé de déterminer la taille de ScrollView, mais le message d'erreur indique clairement "taille du contenu". Je me suis assuré que tout ce que j'avais épinglé depuis le haut de la ScrollView était également épinglé vers le bas. Ainsi, ScrollView peut calculer la hauteur de son contenu en recherchant la hauteur de tous les objets et contraintes. Cela résout la hauteur du contenu ambigu, la largeur est assez similaire ... Au départ, la plupart des choses étaient centrées sur X dans le ScrollView, mais je devais également épingler les objets sur le côté du ScrollView. Je n'aime pas cela car l'iPhone6 a peut-être un écran plus large, mais il supprimera l'erreur «largeur du contenu ambigu».
C'est la réponse à ma question personnelle UIScrollView + Vue centrée + Taille du contenu à défilement ambigu + + plusieurs tailles d'iPhone .
Mais cela couvre également votre cas!
Has ambiguous scrollable content width
et Has ambiguous scrollable content height
.Important: vous devez ajouter des contraintes de fin et/ou de fond. Pas "leader et top" - ça ne marche pas!
Vous pouvez le vérifier dans mon exemple de projet , qui montre comment résoudre ce problème.
P.S.
Selon la logique, cette action devrait entraîner des "contraintes contradictoires". Mais non!
Je ne sais pas pourquoi cela fonctionne et comment Xcode détecte quelle contrainte est la plus prioritaire (parce que je ne donne pas la priorité à ces contraintes explicitement) .
Vous devez créer une UIView
en tant que sous-vue de la UIScrollView
comme décrit ci-dessous:
UIViewController
UIView
'Main View' UIScrollView
UIView
'Vue du conteneur' La deuxième étape consiste à créer les contraintes UIScrollView
. Je l'ai fait avec les contraintes de haut, de bas, de début et de fin de son superView.
Ensuite, j’ai ajouté les contraintes pour le conteneur de UIScrollView
et c’est ici que le problème commence. Lorsque vous mettez les mêmes contraintes (haut, bas, début et fin), le storyboard envoie un message d'avertissement:
"A une largeur de contenu défilable ambiguë" et "A une hauteur de contenu défilable ambiguë"
Continuez avec les mêmes contraintes ci-dessus, et ajoutez simplement les contraintesEqual Height
etEqual Width
à votre Vue du conteneur en relation avec la Vue principale et pas à votre UIScrollView
. En d'autres termes, les contraintes de la vue conteneur sont liées à la superview de UIScrollView
.
Après cela, vous ne recevrez plus d’avertissements dans votre Storyboard et vous pourrez continuer à ajouter les contraintes pour vos vues secondaires.
J'ai finalement compris cela, j'espère que c'est plus facile à comprendre.
Vous pouvez souvent contourner cet avertissement en ajoutant une UIView de base à la vue de défilement en tant que "vue de contenu", comme indiqué, et lui donner la même taille que votre vue de défilement et, sur cette vue de contenu, définir ces 6 paramètres.
Comme vous pouvez le constater, vous avez besoin de 6 paramètres au total! Ce qui .. dans des circonstances normales, vous dupliquez 2 contraintes mais c’est le moyen d’éviter cette erreur de scénarimage.
iOS 12, Swift 4
La manière la plus simple d'utiliser autolayout
:
UIScrollView
et épinglez-le 0,0,0,0
à superview (ou à la taille souhaitée)UIView
dans ScrollView, épinglez-le 0,0,0,0
sur les 4 côtés et centrez-le horizontally
et vertically
.bottom
et align center Y
sur 250. (pour le défilement horizontal, changez trailing
et align center X
)Je sais que je serai peut-être en retard, mais la solution suivante résout ce genre de problèmes sans code supplémentaire , en utilisant simplement des storyboards:
Pour l'affichage du contenu, vous devez définir des contraintes pour scrollview et ceci ne changera pas le cadre d'affichage du contenu , comme ceci:
Bien sûr, vous devez créer des contraintes supplémentaires pour la vue du contenu afin que la vue défilement puisse connaître la taille du contenu. Par exemple, définissez hauteur fixe et centre x.
J'espère que cela t'aides.
Découvrez ce didacticiel très rapide et pertinent sur la manière de faire fonctionner la vue par défilement et de le faire défiler entièrement avec la mise en page automatique. Maintenant, la seule chose qui me laisse encore perplexe est pourquoi la taille du contenu de la vue de défilement est toujours plus grande que nécessaire.
http://samwize.com/2014/03/14/how-to-use-uiscrollview-with-autolayout/
Pour moi, ajouter une contentView
n'a pas vraiment fonctionné comme suggéré. De plus, cela crée une surcharge en raison de la vue ajoutée (bien que je ne considère pas cela comme un gros problème). Ce qui a fonctionné le mieux pour moi a été de désactiver la désactivation de la vérification d’ambiguïté pour ma scrollView
. Tout se passe bien, donc je pense que ça va dans des cas simples comme le mien. Mais gardez à l’esprit que, si d’autres contraintes pour votre scrollView
sautent, Interface-Builder _ vous avertira pas.
Voir ci-dessous: vue du contenu dans scrollview vertical et horizontal centralisé . Si vous avez une erreur d’ambiguïté, assurez-vous toujours que deux des éléments ajoutés dans scrollview depuis votre vue du contenu sont 1). capture d'écran,
ces contraintes signifient en défilement combien vous pouvez faire défiler après la hauteur ou la largeur de votre vue de contenu.
cela peut vous aider.
La réponse de @Matteo Gobbi est parfaite, mais dans mon cas, le défilement ne peut pas défiler, je supprime " aligner le centre Y " et ajouter " hauteur> = 1 ", le défilement deviendra déroulant.
Les personnes qui luttent avec uiscrollview ne défilant pas, définissez simplement la contrainte inférieure de la vue contenu avec la disposition inférieure de votre dernière vue (située à l'intérieur de la vue contenu). Ne pas oublier de supprimer la contrainte de centre Y.
Reste que toutes les contraintes sont identiques à celles définies ci-dessus. Scrollview ne souhaitait obtenir qu'une hauteur maximale à partir de la vue de contenu, et nous la définissons comme contrainte inférieure de la dernière vue, ce qui signifie que scrollview modifiera automatiquement le décalage de son contenu.
Dans mon cas, la dernière vue était UILable avec la propriété no of lines = 0 (qui ajuste automatiquement sa hauteur en fonction de son contenu), de sorte qu'elle augmente de façon dynamique la hauteur de uilable et que, éventuellement, notre zone de défilement augmente car la disposition du bas de uilable est alignée sur celle de notre vue de contenu, ce qui oblige scrollview à augmenter son offset de contenu.
import UIKit
class ViewController: UIViewController {
//
let scrollView: UIScrollView = {
let sv = UIScrollView()
sv.translatesAutoresizingMaskIntoConstraints = false
return sv
}()
let lipsumLbl: UILabel = { // you can use here any of your subview
let lbl = UILabel()
lbl.translatesAutoresizingMaskIntoConstraints = false
lbl.text = """
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Fusce eleifend nisi sit amet mi aliquet, ut efficitur risus pellentesque. Phasellus nulla justo, scelerisque ut libero id, aliquet ullamcorper lectus. Integer id iaculis nibh. Duis feugiat mi vitae magna tincidunt, vitae consequat dui tempor. Donec blandit urna nec urna volutpat, sit amet sagittis massa fringilla. Pellentesque luctus erat nec dui luctus, sed maximus urna fermentum. Nullam purus nibh, cursus vel ex nec, pulvinar lobortis leo. Proin ac libero rhoncus, bibendum lorem sed, congue sapien. Donec commodo, est non mollis malesuada, nisi massa tempus ipsum, a varius quam lorem vitae nunc.
Cras scelerisque nisi dolor, in gravida ex ornare a. Interdum et malesuada fames ac ante ipsum primis in faucibus. Maecenas vitae nisl id erat sollicitudin accumsan sit amet viverra sapien. Etiam scelerisque vulputate ante. Donec magna nibh, pharetra sed pretium ac, feugiat sit amet mi. Vestibulum in ipsum vitae dui vehicula pulvinar eget ut lectus. Fusce sagittis a elit ac elementum. Fusce iaculis nunc odio, at fermentum dolor varius et. Suspendisse consectetur congue massa non gravida. Sed id elit vitae nulla aliquam euismod. Pellentesque accumsan risus dolor, eu cursus nibh semper id.
Vestibulum vel tortor tellus. Suspendisse potenti. Pellentesque id sapien eu augue placerat dictum. Fusce tempus ligula at diam lacinia congue in ut metus. Maecenas volutpat tellus in tellus maximus imperdiet. Integer dignissim condimentum lorem, id luctus nisi maximus at. Nulla pretium, est sit amet mollis eleifend, tellus nulla viverra dolor, ac feugiat massa risus a lectus. Pellentesque ut pulvinar urna, blandit gravida ipsum. Nullam volutpat arcu nec fringilla auctor. Integer egestas enim commodo, faucibus neque ac, rutrum magna. Vivamus tincidunt rutrum auctor. Cras at rutrum felis. Fusce elementum lorem ut pharetra venenatis.
"""
lbl.textColor = .darkGray
lbl.font = UIFont.systemFont(ofSize: 16)
lbl.numberOfLines = 0
return lbl
}()
//======================================================================//
//
// MARK:- viewDidLoad
//
override func viewDidLoad() {
super.viewDidLoad()
setupViews()
setupAutoLayout()
}
func setupViews() {
title = "ScrollView Demo"
view.backgroundColor = .white
view.addSubview(scrollView)
scrollView.addSubview(lipsumLbl)
}
func setupAutoLayout() {
scrollView.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true
scrollView.rightAnchor.constraint(equalTo: view.rightAnchor).isActive = true
scrollView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
scrollView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
lipsumLbl.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor).isActive = true
lipsumLbl.topAnchor.constraint(equalTo: scrollView.topAnchor).isActive = true
lipsumLbl.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true
lipsumLbl.rightAnchor.constraint(equalTo: view.rightAnchor).isActive = true
lipsumLbl.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor).isActive = true
}
}
Sortie:
En utilisant contentView (UView
) comme conteneur dans UIScrollView
, respectez les bords (top
, bottom
, trailing
, leading
) de superview (UIScrollView
) et contentView
devraient avoir equalwidth
et equalheight
à supern. Ce sont des contraintes. Et appel de méthode:
-(void)viewDidLayoutSubviews
{
[super viewDidLayoutSubviews];
self.scrollView.contentSize = self.contentView.frame.size;
}
Problèmes résolus pour moi.
J'ai résolu ce type de problème pour mon affichage en utilisant l'option "Résoudre les problèmes de présentation automatique"> "Ajouter des contraintes manquantes" pour la vue sélectionnée
Les deux contraintes suivantes résolvent mon problème:
trailing = Stack View.trailing - 10
bottom = Stack View.bottom + 76
dans lequel: fin, bas sont les bas, fin de UIScrollView
J'ai fait une vidéo sur youTube
Faites défiler StackViews en utilisant uniquement Storyboard dans Xcode
Je pense que 2 types de scénarios peuvent apparaître ici.
La vue à l'intérieur du scrollView -
UIView
)UIStackView
)Pour une vue à défilement vertical dans les deux cas, vous devez ajouter ces contraintes:
4 contraintes en haut, à gauche, en bas et à droite.
Largeur égale à défiler (pour arrêter de défiler horizontalement)
Vous n'avez besoin d'aucune autre contrainte pour les vues ayant sa propre hauteur de contenu intrinsèque.
Pour les vues qui n'ont pas de hauteur de contenu intrinsèque, vous devez ajouter une contrainte de hauteur. La vue défilera uniquement si la contrainte de hauteur est supérieure à la hauteur de scrollView.
Définissez la taille du ViewController (celui qui contient UIScrollView) sur Freeform dans l'inspecteur de taille dans Interface Builder.
Paramètre Freeform dans l'inspecteur de taille dans Interface Builder pour le contrôleur UIView contenant
Défilement vertical
Vous devez juste vous assurer qu'il y a un moyen de faire une continue ligne de contraintes du haut en bas de la vue à défilement . [-X-X-X]
Dans mon cas - une vue de défilement horizontal - j'avais également besoin d'ajouter des contraintes de largeur et de hauteur pour chaque enfant de la vue de défilement, bien que la note technique d'Apple ne vous le dis pas.
[testé sous XCode 7.2 et pour iOS 9.2]
Ce qui a supprimé les erreurs et les avertissements dans Storyboard, c’était de régler la taille intrinsèque de la vue de défilement et de la vue du contenu (dans mon cas, une pile) à Placeholder. Ce paramètre se trouve dans l'inspecteur Taille du Storyboard. Et cela dit - La définition d’une taille de contenu intrinsèque au moment de la conception n’affecte que la vue lors de la modification dans Interface Builder. La vue n'aura pas cette taille de contenu intrinsèque au moment de l'exécution.
Donc, je suppose que nous ne nous trompons pas en réglant ceci.
Remarque: dans le storyboard, j'ai épinglé tous les bords de la vue de défilement à la vue d'ensemble et tous les bords de la vue de pile à la vue de défilement. Dans mon code, j'ai défini la valeur translatesAutoresizingMaskIntoConstraints sur false pour les vues scrollview et stackview. Et je n'ai pas mentionné de taille de contenu. Lorsque la vue de pile croît de manière dynamique, les contraintes définies dans le storyboard permettent de faire défiler la pile.
L'avertissement du storyboard me rendait fou et je ne voulais pas centrer les choses horizontalement ou verticalement, juste pour supprimer l'avertissement.
Approche rapide 4+:
1) Définissez les marges supérieure, inférieure, gauche et droite de UIScrollView sur 0
2) Dans UIScrollView, ajoutez un UIView et définissez les marges de fin, de haut, de bas et de début à 0 (égales aux marges de UIScrollView).
3) La partie la plus importante est de régler la largeur et la hauteur, où la contrainte de hauteur doit avoir une priorité faible .
private func setupConstraints() {
// Constraints for scrollView
scrollView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
scrollView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
scrollView.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
scrollView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
// Constraints for containerView
containerView.topAnchor.constraint(equalTo: scrollView.topAnchor).isActive = true
containerView.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor).isActive = true
containerView.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor).isActive = true
containerView.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor).isActive = true
containerView.widthAnchor.constraint(equalTo: scrollView.widthAnchor).isActive = true
let heightConstraint = containerView.heightAnchor.constraint(equalTo: scrollView.heightAnchor)
heightConstraint.priority = UILayoutPriority(rawValue: 250)
heightConstraint.isActive = true
}
Je recevais la même erreur .. j'ai fait suite
Maintenant, ajoutez scroll/lead/bottom/top/bottom pour scrollView (2) puis UIView (3).
Sélectionnez Afficher (1) et Afficher (3) pour régler la hauteur et le poids de manière égale. Le problème est résolu.
J'ai fait la vidéo qui aidera:
Ce que j'ai fait est de créer une vue de contenu distincte, comme illustré ici.
La vue de contenu est de forme libre et toutes les sous-vues peuvent y être ajoutées avec leurs propres contraintes par rapport à contentView.
UIScrollView est ajouté au contrôleur de vue principal.
J'ajoute par programme le contentView qui est lié via IBOutlet à class et définit le contentView de UIScrollView.
Si quelqu'un a un comportement où vous remarquez que la barre de défilement se trouve à droite mais que le contenu ne bouge pas, ce fait mérite probablement d'être pris en compte:
Vous pouvez également utiliser des contraintes entre le contenu de la vue de défilement et les objets situés en dehors de la vue de défilement pour définir une position fixe pour le contenu de la vue de défilement, de sorte que ce contenu semble flotter sur la vue de défilement.
Cela provient de la documentation d'Apple. Par exemple, si vous épingliez accidentellement votre étiquette/bouton/image/vue en haut de la vue en dehors de la zone de défilement (peut-être un en-tête ou quelque chose juste au-dessus de scrollView?) Au lieu de contentView, vous figeiez tout votre contentView en place.
Comme mentionné dans les réponses précédentes, vous devez ajouter une vue personnalisée dans la vue de défilement:
Ajoutez toutes vos sous-vues à la vue du contenu . À un moment donné, une vue du contenu défilant affiche un avertissement ambigu sur la taille du contenu, vous devez sélectionner la vue du contenu et cliquer sur le bouton "Résoudre les problèmes de mise en page automatique" (dans le coin inférieur droit. de la présentation IB) et sélectionnez l’option "Ajouter les contraintes manquantes".
À partir de maintenant, lorsque vous exécuterez le projet, la vue de défilement mettra automatiquement à jour la taille de son contenu, aucun code supplémentaire n'est nécessaire.