J'ajoute une vue xib dans mon ViewController. Ensuite, je mets ses contraintes pour l'adapter réellement
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
...
...
view!.addSubview(gamePreview)
gamePreview.translatesAutoresizingMaskIntoConstraints = false
if #available(iOS 9.0, *) {
// Pin the leading Edge of myView to the margin's leading Edge
gamePreview.leadingAnchor.constraintEqualToAnchor(view.leadingAnchor).active = true
//Pin the trailing Edge of myView to the margin's trailing Edge
gamePreview.trailingAnchor.constraintEqualToAnchor(view.trailingAnchor).active = true
} else {
// Fallback on earlier versions
view.addConstraint(NSLayoutConstraint(item: gamePreview, attribute: .TrailingMargin, relatedBy: .Equal, toItem: view, attribute: .TrailingMargin, multiplier: 1, constant: 0))
view.addConstraint(NSLayoutConstraint(item: gamePreview, attribute: .LeadingMargin, relatedBy: .Equal, toItem: view, attribute: .LeadingMargin, multiplier: 1, constant: 0))
}
view.addConstraint(NSLayoutConstraint(item: gamePreview, attribute: .Top, relatedBy: .Equal, toItem: self.topLayoutGuide, attribute: .Bottom, multiplier: 1, constant: 0))
view.addConstraint(NSLayoutConstraint(item: gamePreview, attribute: .Height, relatedBy: .Equal, toItem: nil, attribute: .NotAnAttribute,multiplier: 1, constant: 131))
}
Ce que j'essaie de faire: Ajuster réellement mon point de vue en le limitant au sommet, menant à la vue de ViewController, avec une hauteur préfixée. La vue que j'ajoute à la vue principale a sa propre vue en arrière-plan transparent, donc aucun besoin de marge (la vue est censée être la taille de la largeur du périphérique, donc).
J'ai placé 2 couples de lignes qui seraient supposées être égales (dans mes tentatives), avec le if, car les 2 premières lignes de if sont réellement disponibles dans iOS9> uniquement, alors que j'essaie de faire la même chose dans l'instruction else, pour chaque périphérique (à partir de iOS 8).
Voici ce que je reçois:
iOS9 + à gauche, iOS8 + à droite. L'arrière-plan transparent était coloré en rouge pour montrer ce qui se passait (ne vous dérangez pas à une hauteur différente dans les images, elles ont la même hauteur dans l'application, mais regardez plutôt les marges ajoutées à gauche et à droite).
J'ai aussi essayé AutoLayoutDSL-Swift
, mais ça n'aide pas, je n'en suis pas un expert, mais chaque tentative n'a fait qu'empirer les choses.
Comment puis-je écrire ces contraintes en utilisant les méthodes classiques de NSLayoutConstraints, et comment pourrais-je tout écrire de manière plus efficace avec un framework comme AutoLayoutDSL ou un fork de celui-ci? (ou une alternative, bien que maintenant je suis surtout concerné par les bibliothèques officielles)
Vous devez utiliser les attributs leading
et trailing
, et non les attributs leadingMargin
et trailingMargin
:
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
...
...
view!.addSubview(gamePreview)
gamePreview.translatesAutoresizingMaskIntoConstraints = false
view.addConstraint(NSLayoutConstraint(item: gamePreview, attribute: .Trailing, relatedBy: .Equal, toItem: view, attribute: .Trailing, multiplier: 1, constant: 0))
view.addConstraint(NSLayoutConstraint(item: gamePreview, attribute: .Leading, relatedBy: .Equal, toItem: view, attribute: .Leading, multiplier: 1, constant: 0))
view.addConstraint(NSLayoutConstraint(item: gamePreview, attribute: .Top, relatedBy: .Equal, toItem: self.topLayoutGuide, attribute: .Bottom, multiplier: 1, constant: 0))
view.addConstraint(NSLayoutConstraint(item: gamePreview, attribute: .Height, relatedBy: .Equal, toItem: nil, attribute: .NotAnAttribute,multiplier: 1, constant: 131))
}
Deviné grâce à @ Paulw11 ... c'est la solution
view.addConstraint(NSLayoutConstraint(item: gamePreview, attribute: .Trailing, relatedBy: .Equal, toItem: view, attribute: .Trailing, multiplier: 1, constant: 0))
view.addConstraint(NSLayoutConstraint(item: gamePreview, attribute: .Leading, relatedBy: .Equal, toItem: view, attribute: .Leading, multiplier: 1, constant: 0))
view.addConstraint(NSLayoutConstraint(item: gamePreview, attribute: .Top, relatedBy: .Equal, toItem: self.topLayoutGuide, attribute: .Bottom, multiplier: 1, constant: 0))
view.addConstraint(NSLayoutConstraint(item: gamePreview, attribute: .Height, relatedBy: .Equal, toItem: nil, attribute: .NotAnAttribute,multiplier: 1, constant: 131))
De plus, j'ai réussi à le réécrire comme suit, à l'aide de AutoLayoutDSL-Swift
pour les personnes intéressées.
view => gamePreview.trailing == view.trailing
=> gamePreview.leading == view.leading
=> gamePreview.height == 131
=> gamePreview.top == view.top + self.navigationController!.navigationBar.bounds.height + UIApplication.sharedApplication().statusBarFrame.size.height
La dernière ligne est un peu longue car view.top fait référence à la vue vraiment supérieure et ne considère pas le remplissage ajouté à la fois par les hauteurs statusBar et navigationBar. Ils pourraient être remplacés en tant que constantes, en attendant que quelqu'un propose une solution plus élégante.
Mise à jour Swift 4
stackView.translatesAutoresizingMaskIntoConstraints = false
stackView.addConstraint(NSLayoutConstraint(item: stackView, attribute: .trailing, relatedBy: .equal, toItem: stackView, attribute: .trailing, multiplier: 1, constant: 0))
stackView.addConstraint(NSLayoutConstraint(item: stackView, attribute: .leading, relatedBy: .equal, toItem: stackView, attribute: .leading, multiplier: 1, constant: 0))
stackView.addConstraint(NSLayoutConstraint(item: stackView, attribute: .top, relatedBy: .equal, toItem: self.addLayoutGuide, attribute: .bottom, multiplier: 1, constant: 0))
stackView.addConstraint(NSLayoutConstraint(item: stackView, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute,multiplier: 1,
constant: 131))
Pourquoi n'utilisez-vous pas l'activation par contrainte? Vous pourriez faire ceci:
var anchors = [NSLayoutConstraint]()
anchors.append(view.topAnchor.constraint(equalTo: gamePreview.topAnchor, constant: 0))
anchors.append(view.leadingAnchor.constraint(equalTo: gamePreview.leadingAnchor, constant: 0))
anchors.append(view.trailingAnchor.constraint(equalTo: gamePreview.trailingAnchor, constant: 0))
anchors.append(view.heightAnchor.constraint(equalTo: gamePreview.heightAnchor, multiplier: 1, constant: 131))
NSLayoutConstraint.activate(anchors)
Ceci est un autre. J'espère que ça aide. Vous pouvez permuter view
et gamePreview
en fonction du parent/enfant ou créer une extension d'assistance générale pour vos contraintes. J'en ai trouvé un dans un tutoriel fourni par YouTuber Here et l'a légèrement modifié pour accepter les contraintes de début/fin, gauche/droite. Vous pouvez également ajouter safeLayoutGuide
au paragraphe ci-dessous, ce qui le rend convivial pour l’avenir. Quelque chose comme ça:
func anchor(_ top: UIView? = nil, left: UIView? = nil, bottom: UIView? = nil, right: UIView? = nil, width: UIView? = nil, height: UIView? = nil, topConstant: CGFloat = 0, leftConstant: CGFloat = 0, bottomConstant: CGFloat = 0, rightConstant: CGFloat = 0, widthConstant: CGFloat = 0, heightConstant: CGFloat = 0, isForLeading: Bool = false) -> [NSLayoutConstraint] {
translatesAutoresizingMaskIntoConstraints = false
var anchors = [NSLayoutConstraint]()
if #available(iOS 11.0, *) {
if let top = top {
anchors.append(topAnchor.constraint(equalTo: top.safeAreaLayoutGuide.topAnchor, constant: topConstant))
}
if let left = left {
if isForLeading {
anchors.append(leadingAnchor.constraint(equalTo: left.safeAreaLayoutGuide.leadingAnchor, constant: leftConstant))
} else {
anchors.append(leftAnchor.constraint(equalTo: left.safeAreaLayoutGuide.leftAnchor, constant: leftConstant))
}
}
if let bottom = bottom {
anchors.append(bottomAnchor.constraint(equalTo: bottom.safeAreaLayoutGuide.bottomAnchor, constant: -bottomConstant))
}
if let right = right {
if isForLeading {
anchors.append(trailingAnchor.constraint(equalTo: right.safeAreaLayoutGuide.leadingAnchor, constant: rightConstant))
} else {
anchors.append(rightAnchor.constraint(equalTo: right.safeAreaLayoutGuide.rightAnchor, constant: -rightConstant))
}
}
if let width = width {
anchors.append(widthAnchor.constraint(equalTo: width.safeAreaLayoutGuide.widthAnchor, multiplier: 1, constant: widthConstant))
} else if widthConstant > 0 {
anchors.append(widthAnchor.constraint(equalToConstant: widthConstant))
}
if let height = height {
anchors.append(heightAnchor.constraint(equalTo: height.safeAreaLayoutGuide.heightAnchor, multiplier: 1, constant: widthConstant))
} else if heightConstant > 0 {
anchors.append(widthAnchor.constraint(equalToConstant: heightConstant))
}
anchors.forEach({$0.isActive = true})
} else {
if let top = top {
anchors.append(topAnchor.constraint(equalTo: top.topAnchor, constant: topConstant))
}
if let left = left {
if isForLeading {
anchors.append(leadingAnchor.constraint(equalTo: left.leadingAnchor, constant: leftConstant))
} else {
anchors.append(leftAnchor.constraint(equalTo: left.leftAnchor, constant: leftConstant))
}
}
if let bottom = bottom {
anchors.append(bottomAnchor.constraint(equalTo: bottom.bottomAnchor, constant: -bottomConstant))
}
if let right = right {
if isForLeading {
anchors.append(trailingAnchor.constraint(equalTo: right.leadingAnchor, constant: rightConstant))
} else {
anchors.append(rightAnchor.constraint(equalTo: right.rightAnchor, constant: -rightConstant))
}
}
if let width = width {
anchors.append(widthAnchor.constraint(equalTo: width.widthAnchor, multiplier: 1, constant: widthConstant))
} else if widthConstant > 0 {
anchors.append(widthAnchor.constraint(equalToConstant: widthConstant))
}
if let height = height {
anchors.append(heightAnchor.constraint(equalTo: height.heightAnchor, multiplier: 1, constant: widthConstant))
} else if heightConstant > 0 {
anchors.append(widthAnchor.constraint(equalToConstant: heightConstant))
}
anchors.forEach({$0.isActive = true})
}
return anchors
}
Et puis appelez ça comme ça:
_ = view.anchor(gamePreview, left: gamePreview, right: gamePreview, height: gamePreview, heightConstant: 131, isForLeading: true)