Essayer de trouver une solution pour mettre à jour plusieurs contraintes pour plusieurs éléments d'interface utilisateur sur un événement. J'ai vu quelques exemples de désactivation, de modification, puis de réactivation de contraintes. Cette méthode semble peu pratique pour les 24 ancres avec lesquelles je travaille.
Un de mes ensembles de modifications:
ticketContainer.translatesAutoresizingMaskIntoConstraints = false
ticketContainer.topAnchor.constraintEqualToAnchor(self.topAnchor).active = true
ticketContainer.leftAnchor.constraintEqualToAnchor(self.rightAnchor, constant: 20).active = true
ticketContainer.widthAnchor.constraintEqualToConstant(200.0).active = true
ticketContainer.leftAnchor.constraintEqualToAnchor(self.leftAnchor, constant: 20).active = true
ticketContainer.widthAnchor.constraintEqualToConstant(100.0).active = true
Avez-vous essayé d'enregistrer les contraintes pertinentes créées à l'aide des ancres de présentation dans les propriétés, puis de modifier simplement la constante? Par exemple.
var ticketTop : NSLayoutConstraint?
func setup() {
ticketTop = ticketContainer.topAnchor.constraintEqualToAnchor(self.topAnchor, constant:100)
ticketTop.active = true
}
func update() {
ticketTop?.constant = 25
}
Selon votre goût pour les extensions d'écriture, voici une approche peut-être plus élégante qui n'utilise pas de propriétés, mais crée plutôt des méthodes d'extension sur NSLayoutAnchor
et UIView
pour aider à une utilisation plus succincte.
Commencez par écrire une extension sur NSLayoutAnchor
comme ceci:
extension NSLayoutAnchor {
func constraintEqualToAnchor(anchor: NSLayoutAnchor!, constant:CGFloat, identifier:String) -> NSLayoutConstraint! {
let constraint = self.constraintEqualToAnchor(anchor, constant:constant)
constraint.identifier = identifier
return constraint
}
}
Cette extension vous permet de définir un identifiant sur la contrainte dans le même appel de méthode qui le crée à partir d'une ancre. Notez que la documentation Apple implique que les ancres XAxis (gauche, droite, interligne, etc.) ne vous permettent pas de créer une contrainte avec les ancres YAxis (haut, bas, etc.), mais je ne remarque pas que cela soit vrai. Si vous souhaitez ce type de vérification du compilateur, vous devez écrire des extensions distinctes pour NSLayoutXAxisAnchor
, NSLayoutYAxisAnchor
et NSLayoutDimension
(pour les contraintes de largeur et de hauteur) qui appliquent l'exigence de type d'ancrage même axe.
Ensuite, vous écririez une extension sur UIView
pour obtenir une contrainte par identifiant:
extension UIView {
func constraint(withIdentifier:String) -> NSLayoutConstraint? {
return self.constraints.filter{ $0.identifier == withIdentifier }.first
}
}
Avec ces extensions en place, votre code devient:
func setup() {
ticketContainer.topAnchor.constraintEqualToAnchor(self.topAnchor, constant:100, identifier:"ticketTop").active = true
}
func update() {
self.constraint(withIdentifier:"ticketTop")?.constant = 25
}
Notez que l'utilisation de constantes ou d'une énumération au lieu de noms de chaînes magiques pour les identificateurs constituerait une amélioration par rapport à ce qui précède, mais je garde cette réponse brève et précise.
Vous pouvez parcourir les contraintes d'une vue et rechercher des éléments et des ancres correspondants. N'oubliez pas que la contrainte sera sur la vue d'ensemble de la vue, sauf s'il s'agit d'une contrainte de dimension. J'ai écrit un code d'assistance qui vous permettra de trouver toutes les contraintes sur une ancre d'une vue.
import UIKit
class ViewController: UIViewController {
let label = UILabel()
let imageView = UIImageView()
override func viewDidLoad() {
super.viewDidLoad()
label.text = "Constraint Finder"
label.translatesAutoresizingMaskIntoConstraints = false
imageView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(label)
view.addSubview(imageView)
label.topAnchor.constraint(equalTo: view.topAnchor, constant: 30).isActive = true
label.leftAnchor.constraint(equalTo: view.leftAnchor, constant: 20).isActive = true
label.widthAnchor.constraint(greaterThanOrEqualToConstant: 50).isActive = true
imageView.topAnchor.constraint(equalTo: label.bottomAnchor).isActive = true
imageView.leftAnchor.constraint(equalTo: view.leftAnchor, constant: 60).isActive = true
imageView.widthAnchor.constraint(equalTo: label.widthAnchor).isActive = true
imageView.heightAnchor.constraint(equalToConstant: 70).isActive = true
print("Label's top achor constraints: \(label.constraints(on: label.topAnchor))")
print("Label's width achor constraints: \(label.constraints(on: label.widthAnchor))")
print("ImageView's width achor constraints: \(imageView.constraints(on: imageView.widthAnchor))")
}
}
public extension UIView {
public func constraints(on anchor: NSLayoutYAxisAnchor) -> [NSLayoutConstraint] {
guard let superview = superview else { return [] }
return superview.constraints.filtered(view: self, anchor: anchor)
}
public func constraints(on anchor: NSLayoutXAxisAnchor) -> [NSLayoutConstraint] {
guard let superview = superview else { return [] }
return superview.constraints.filtered(view: self, anchor: anchor)
}
public func constraints(on anchor: NSLayoutDimension) -> [NSLayoutConstraint] {
guard let superview = superview else { return [] }
return constraints.filtered(view: self, anchor: anchor) + superview.constraints.filtered(view: self, anchor: anchor)
}
}
extension NSLayoutConstraint {
func matches(view: UIView, anchor: NSLayoutYAxisAnchor) -> Bool {
if let firstView = firstItem as? UIView, firstView == view && firstAnchor == anchor {
return true
}
if let secondView = secondItem as? UIView, secondView == view && secondAnchor == anchor {
return true
}
return false
}
func matches(view: UIView, anchor: NSLayoutXAxisAnchor) -> Bool {
if let firstView = firstItem as? UIView, firstView == view && firstAnchor == anchor {
return true
}
if let secondView = secondItem as? UIView, secondView == view && secondAnchor == anchor {
return true
}
return false
}
func matches(view: UIView, anchor: NSLayoutDimension) -> Bool {
if let firstView = firstItem as? UIView, firstView == view && firstAnchor == anchor {
return true
}
if let secondView = secondItem as? UIView, secondView == view && secondAnchor == anchor {
return true
}
return false
}
}
extension Array where Element == NSLayoutConstraint {
func filtered(view: UIView, anchor: NSLayoutYAxisAnchor) -> [NSLayoutConstraint] {
return filter { constraint in
constraint.matches(view: view, anchor: anchor)
}
}
func filtered(view: UIView, anchor: NSLayoutXAxisAnchor) -> [NSLayoutConstraint] {
return filter { constraint in
constraint.matches(view: view, anchor: anchor)
}
}
func filtered(view: UIView, anchor: NSLayoutDimension) -> [NSLayoutConstraint] {
return filter { constraint in
constraint.matches(view: view, anchor: anchor)
}
}
}
extension UIView {
func add(view: UIView, left: CGFloat, right: CGFloat, top: CGFloat, bottom: CGFloat) {
view.translatesAutoresizingMaskIntoConstraints = false
self.addSubview(view)
view.leftAnchor.constraint(equalTo: self.leftAnchor, constant: left).isActive = true
view.rightAnchor.constraint(equalTo: self.rightAnchor, constant: right).isActive = true
view.topAnchor.constraint(equalTo: self.topAnchor, constant: top).isActive = true
view.bottomAnchor.constraint(equalTo: self.bottomAnchor, constant: bottom).isActive = true
}
}
headerView.add(view: headerLabel, left: 20, right: 0, top: 0, bottom: 0)