2017-11-24 8 views
0

Ich habe einen Kuchen von 3 UIView-Ebenen mit programmatischen Einschränkungen.Out-of-Grenzen UIView Constraints programmgesteuert

Funktion Konstruiert für programm eingerichtet Constraints:

func setupViewConstraints(item:UIView, leadingTo:NSLayoutXAxisAnchor, leadingCon:CGFloat, 
trailingTo:NSLayoutXAxisAnchor, trailingCon:CGFloat, topTo:NSLayoutYAxisAnchor, 
topCon:CGFloat, bottomTo:NSLayoutYAxisAnchor, bottomCon:CGFloat) { 
    item.translatesAutoresizingMaskIntoConstraints = false 
    item.leadingAnchor.constraint(equalTo: leadingTo, constant: leadingCon).isActive = true 
    item.trailingAnchor.constraint(equalTo: trailingTo, constant: trailingCon).isActive = true 
    item.topAnchor.constraint(equalTo: topTo, constant:topCon).isActive = true 
    item.bottomAnchor.constraint(equalTo: bottomTo, constant:bottomCon).isActive = true 
} 

Die unterste Basisschicht ist hellgrau.

view = UIView() 
view.backgroundColor = .lightGray 

Die 2. Schicht enthält 2 UIView (rot und blau) mit Einschränkungen.

let red = UIView() 
red.backgroundColor = .red 
view.addSubview(red) 
setupViewConstraints(item: red, leadingTo: view.leadingAnchor, leadingCon: 0, trailingTo: view.trailingAnchor, trailingCon: -(view.frame.width)*0.2), topTo: view.topAnchor, topCon: 0, bottomTo: view.bottomAnchor, bottomCon: -(view.frame.width)*0.8) 

let blue = UIView() 
blue.backgroundColor = .blue 
view.addSubview(blue) 
setupViewConstraints(item: blue, leadingTo: view.leadingAnchor, leadingCon: 0, trailingTo: view.trailingAnchor, trailingCon: -(view.frame.width)*0.2), topTo: red.bottomAnchor, topCon: 0, bottomTo: view.bottomAnchor, bottomCon: 0) 

3 UIView

Und oben Ich habe gelbe UIView Schicht, die alle unteren Schichten überlappt.

let yellow = UIView() 
yellow.backgroundColor = .yellow 
view.addSubview(yellow) 
setupViewConstraints(item: yellow, leadingTo: view.leadingAnchor, leadingCon: 0, trailingTo: view.trailingAnchor, trailingCon: 0, topTo: view.topAnchor, topCon: 0, bottomTo: view.bottomAnchor, bottomCon: 0) 

Auch habe ich UINavigationBar mit UINavigationItem innerhalb des gelben UIView.

//Add navigation item and buttons 
     naviItem = UINavigationItem() 
     naviItem.setRightBarButton(UIBarButtonItem(barButtonSystemItem:.add, target:self, action:#selector(goToDestVC)), animated: true) 
     naviItem.setLeftBarButton(UIBarButtonItem(image: UIImage(named: "hamburger_slim_30"), style: .plain, target: self, action: #selector(hamburgerBtnPressed)), animated: true) 

     //Add navigation bar with transparent background 
     naviBar = UINavigationBar() 
     naviBar.setBackgroundImage(UIImage(), for: .default) 
     naviBar.shadowImage = UIImage() 
     naviBar.isTranslucent = true 

// Assign the navigation item to the navigation bar 
     naviBar.items = [naviItem] 

     view.addSubview(naviBar) 
     setupViewConstraints(item: naviBar, leadingTo: yellow.leadingAnchor, leadingCon: 0, trailingTo: yellow.trailingAnchor, trailingCon: 0, topTo: yellow.topAnchor, topCon: 0, bottomTo: yellow.bottomAnchor, bottomCon: -(view.frame.height)*0.9)) 

Top UIView layer with UINavigationBar

Und ich habe hamburgerBtnPressed Funktion, die die gelbe Schicht nach rechts um 80% verschieben sollte (ich die Werte der führenden ändern und Konstanten um 80% nachgestellt), aber dies nicht Arbeit!!!

var hamburgerMenuIsVisible = false 

    @objc func hamburgerBtnPressed(_ sender: Any) { 

     if !hamburgerMenuIsVisible { 

      let menuWidth = (self.view.frame.width)*0.8 

       setupViewConstraints(item: layoutView, leadingTo: view.leadingAnchor, leadingCon: menuWidth, trailingTo: view.trailingAnchor, trailingCon: menuWidth, topTo: view.topAnchor, topCon: 0, bottomTo: view.bottomAnchor, bottomCon: 0) 

      hamburgerMenuIsVisible = true 

     } else { 

      setupViewConstraints(item: layoutView, leadingTo: view.leadingAnchor, leadingCon: 0, trailingTo: view.trailingAnchor, trailingCon: 0, topTo: view.topAnchor, topCon: 0, bottomTo: view.bottomAnchor, bottomCon: 0) 

      hamburgerMenuIsVisible = false 
     } 

     // layoutIfNeeded() lays out the subviews immediately and forces the layout before drawing 
     UIView.animate(withDuration: 0.2, delay:0.0, options: .curveEaseIn, animations: { 

      self.view.layoutIfNeeded() 

     }) { (animationComplete) in 
      print("Animation is complete!") 
     } 
    } 

Aber wenn ich die Werte der Vorder- und die Hinter Konstanten negativ ändern, wird alles funktionieren, und das Menü wird ohne Probleme nach links verschieben.

let menuWidth = -(self.view.frame.width)*0.8 

To the left

Bitte erläutern .. was das Problem ist? Warum ist die gelbe UIView nach links mit negativen Werten von Constraints verschoben und arbeitet nicht mit positiven Werten von Constraints? und gibt einen Fehler:

Probably at least one of the constraints in the following list is one you don't want. 
(
    "<NSLayoutConstraint:0x6040002853c0 UIView:0x7fa947c35850.trailing == UIView:0x7fa947e1d2d0.trailing (active)>", 
    "<NSLayoutConstraint:0x604000092750 UIView:0x7fa947c35850.trailing == UIView:0x7fa947e1d2d0.trailing + 331.2 (active)>" 
) 

Will attempt to recover by breaking constraint 
<NSLayoutConstraint:0x604000092750 UIView:0x7fa947c35850.trailing == UIView:0x7fa947e1d2d0.trailing + 331.2 (active)> 

Update: ich wählen haben Option 2: Halten Sie einen Verweis auf die Einschränkung, die Sie ändern wollen, und nur seine konstant einzustellen. Sie müssen setNeedsLayout auch vor layoutIfNeeded aufrufen.

Aktualisiert Code:

var leadingC: NSLayoutConstraint! 
var trailingC: NSLayoutConstraint! 
var yellow: UIView! 

Loadview():

yellow = UIView() 
     yellow.backgroundColor = .yellow 
     view.addSubview(yellow) 

     //Set up leading and trailing constraints for handling yellow view shift 
     leadingC = yellow.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 0) 
     trailingC = yellow.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: 0) 

//Put leadingC.constant and trailingC.constant into the function 
     setupViewConstraints(item: yellow, leadingTo: view.leadingAnchor, leadingCon: leadingC!.constant, trailingTo: view.trailingAnchor, trailingCon: trailingC.constant, topTo: view.topAnchor, topCon: 0, bottomTo: view.bottomAnchor, bottomCon: 0) 

Aktualisiert Hamburger Funktion: "Animation, complete"

@objc func hamburgerBtnPressed(_ sender: Any) { 

     if !hamburgerMenuIsVisible { 

      let menuWidth = (self.view.frame.width)*0.8 


      leadingC!.constant = menuWidth 
      trailingC!.constant = menuWidth 

      print(leadingC.constant, trailingC.constant) 

      hamburgerMenuIsVisible = true 

     } else { 

      leadingC!.constant = 0 
      trailingC!.constant = 0 


      hamburgerMenuIsVisible = false 
     } 

     // layoutIfNeeded() lays out the subviews immediately and forces the layout before drawing 
     UIView.animate(withDuration: 0.2, delay:0.0, options: .curveEaseIn, animations: { 

      self.view.setNeedsLayout() 
      self.view.layoutIfNeeded() 

     }) { (animationComplete) in 
      print("Animation is complete!") 
     } 
    } 
    var hamburgerMenuIsVisible = false 

Ich habe keine Fehler und wurde auch gedruckt, aber nichts passiert auf dem Bildschirm, keine Animation.

Output

+0

Dies ist ein Grund, persönlich ich * nicht * Einschränkung Schöpfung in eine generische Funktion kombinieren - zu schwer zu entziffern. Erstens, wenn ich dich richtig gelesen habe, gibt es * kein * Problem mit roten, blauen und grauen Ansichten, richtig? Wenn ja, bitte entfernen Sie sie von Ihrer Frage, da es für Lärm sorgt. Versuchen Sie, 80% eines Frames zu berechnen, um (a) zu verschieben oder (b) die gelbe Ansicht zu verkleinern? Denn im ersten Fall haben Sie bereits gesagt, dass es funktioniert - weil Sie führende/nachlaufende Anker bewegen. Aber wenn Sie Letzteres wollen, müssen Sie den Breiten-Anker ändern - was ich in Ihrer generischen Funktion nicht sehe. – dfd

+0

Ich möchte die gelbe UIView um 80% nach rechts verschieben, um (a) die gelbe Ansicht zu verschieben. Ich verwende Trailing-Anker für "Breite", und ich kann nur Breite OR Trailing-Anker setzen, sonst um Konflikte zu bekommen. – Rurom

+0

Korrigieren. Es klingt also nach einer * statischen * Breite. Das ist Teil der Verwirrung ... (1) Warum berechnen Sie 80% der Bildbreite? Das ändert sich nicht. Mehr, (2) Um diese * statische * Breite zu behalten, ändern Sie die Konstanten - möglicherweise wird es mit den Multiplikatoren funktionieren, aber ich denke nicht - von dem führenden * und * nach einem negativen Wert, um nach links und zurück zu ihrem Original zu verschieben Wert nach rechts verschieben. Ich dachte, das ist das, was du gesagt hast? Was vermisse ich? – dfd

Antwort

1

Erstens muss es negative Werte, da die Einschränkungen in der richtigen Richtung zu einrichten. Ändern Sie dies und Sie können alle diese negativen Konstanten entfernen:

item.leadingAnchor.constraint(equalTo: leadingTo, constant: leadingCon).isActive = true 
item.trailingAnchor.constraint(equalTo: trailingTo, constant: trailingCon).isActive = true 
item.topAnchor.constraint(equalTo: topTo, constant:topCon).isActive = true 
item.bottomAnchor.constraint(equalTo: bottomTo, constant:bottomCon).isActive = true 

zu

item.leadingAnchor.constraint(equalTo: leadingTo, constant: leadingCon).isActive = true 
trailingTo.constraint(equalTo: item.trailingAnchor, constant: trailingCon).isActive = true 
item.topAnchor.constraint(equalTo: topTo, constant:topCon).isActive = true 
bottomTo.constraint(equalTo: item.bottomAnchor, constant:bottomCon).isActive = true 

Zweitens jedes Mal rufen setupViewConstraints Sie erstellen und aktivieren einen anderen Satz von Einschränkungen.

Option 1:

alle Einschränkungen für die gelbe Ansicht entfernen, bevor sie sich wieder zu setzen.

Option 2:

einen Verweis auf die Einschränkung halten, die Sie ändern wollen, und nur seine konstant einzustellen. Möglicherweise müssen Sie auch setNeedsLayout vor layoutIfNeeded anrufen.

Option 3:

Fügen Sie 2 Einschränkungen hinzu. Die anfängliche führende Einschränkung und eine mit der gewünschten Breite. Ändern Sie die Priorität der ersten Integritätsbedingung in 999 (Standard ist 1000) und schalten Sie die Eigenschaft isActive des anderen Constraints um, wenn Sie das Menü ein-/ausblenden möchten.

let leading = yellow.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 0) 
leading.priority = UILayoutPriority(999) 
leading.isActive = true 

let otherConstraint = yellow.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: (view.frame.width)*0.8) 
otherConstraint.isActive = false // toggle this property to show/hide 

Option 2 ist wahrscheinlich die beste Leistung. Aus dem Apfel docs:

Setting the constant on an existing constraint performs much better than removing the constraint and adding a new one that's exactly like the old except that it has a different constant

+0

Danke. Ich habe Option 2 gewählt (bitte überprüfen Sie das Update), und ich habe keine Fehler, "Animation abgeschlossen!"wurde in Ausgaben gedruckt, aber nichts passiert auf dem Bildschirm, keine Animation. Was ist der Grund? – Rurom

+0

Es sieht so aus als ob Sie isActive im Animationsblock aufrufen müssen. – Pulsar

+0

Wie kann ich es im Animationsblock aufrufen? Könnten Sie ein Beispiel? – Rurom

Verwandte Themen