2015-09-07 12 views
6

Ich versuche verstehen Autolayout Einschränkungen. Im Anschluss an eine Ray W Tutorial Herausforderung (ohne eine Lösung oder eine Diskussion), sieht das Layout sollte dies wie:Constraints in Code mit swift - nicht aktivierbar

desired layout

es in IB tun war einfach genug - schafft eine gelbe Ansicht mit einer Breite und Höhe, vertikal zentrierte und horizontal an den Rändern fixiert; Erstellen Sie dann Etiketten, Felder und eine Schaltfläche mit einfachen Einschränkungen in dieser Ansicht.

Meine erste Frage ist: Wenn die Etiketten eine intrinsische Inhaltsgröße haben und alles andere an die gelbe Ansicht angeheftet ist, warum muss ich dann eine feste Breite und Höhe definieren? Warum sollte er nicht vom inneren Inhalt seiner Unteransichten auf die Breite und Höhe schließen?

Umzug auf und versucht, dieses Layout in Code zu erstellen gab mir eine Fehlermeldung:

Terminating app due to uncaught exception 'NSGenericException', reason: 'Unable to activate constraint with items <UILabel: 0x7a961d60; frame = (0 0; 0 0); text = 'Password:'; userInteractionEnabled = NO; layer = <_UILabelLayer: 0x7a961e40>> and <UIView: 0x7a96b6f0; frame = (0 0; 0 0); layer = <CALayer: 0x7a96b880>> because they have no common ancestor. Does the constraint reference items in different view hierarchies? That's illegal.'

Zweite Frage: Was sind diese Schichten, und alle meine Ansichten sind in einem heirachy - Superview enthält die gelbe Ansicht enthält die Textbeschriftungen und Felder.

Nach vielen Schmerzen habe ich versucht, die in IB gemachten Einschränkungen exakt neu zu erstellen, aber dies fügte nur den folgenden Fehler hinzu: Konnte Bedingungen gleichzeitig nicht erfüllen.

(
    "<NSLayoutConstraint:0x7ac57370 H:[UIView:0x7a96b6f0(0)]>", 
    "<NSLayoutConstraint:0x7ac57400 H:|-(8)-[UILabel:0x7a96bb50'Username:'] (Names: '|':UIView:0x7a96b6f0)>", 
    "<NSLayoutConstraint:0x7ac57430 UILabel:0x7a96bb50'Username:'.trailing == UITextField:0x7a961020.leading + 8>", 
    "<NSLayoutConstraint:0x7ac57520 UITextField:0x7a961020.trailing == UIView:0x7a96b6f0.trailing - 8>") 

Letzte Frage (n): Woher weiß ich, welche Ansicht 0x7aetc ist? Und wo ist diese Einschränkung in meinem Code? Der Rest sieht gut aus (?).

Ich muss etwas sehr falsch auf einer grundlegenden Ebene tun.

Hier ist mein Code:

import UIKit 

class ViewController: UIViewController { 

    let centerView = UIView() 
    let usernameLabel = UILabel() 
    let passwordLabel = UILabel() 
    let usernameField = UITextField() 
    let passwordField = UITextField() 
    let submitButton = UIButton() 

    override func viewDidLoad() { 
     super.viewDidLoad() 

     centerView.backgroundColor = UIColor.yellowColor() 
     usernameLabel.text = "Username:" 
     passwordLabel.text = "Password:" 
     usernameField.borderStyle = .RoundedRect 
     passwordField.borderStyle = .RoundedRect 
     submitButton.setTitle("Submit!", forState: .Normal) 
     submitButton.setTitleColor(UIColor.blackColor(), forState: .Normal) 
     submitButton.setTitleColor(UIColor.blueColor(), forState: .Highlighted) 

     view.addSubview(centerView) 
     self.centerView.addSubview(usernameField) 
     self.centerView.addSubview(passwordField) 
     self.centerView.addSubview(usernameLabel) 
     self.centerView.addSubview(submitButton) 

     let constraintCenterViewHeight = NSLayoutConstraint(item: centerView, attribute: .Height, relatedBy: .Equal, toItem: nil, attribute: .NotAnAttribute, multiplier: 1.0, constant: 0) 
     let constraintCenterViewWidth = NSLayoutConstraint(item: centerView, attribute: .Width, relatedBy: .Equal, toItem: nil, attribute: .NotAnAttribute, multiplier: 1.0, constant: 0) 

     let constraintCenterViewCenterX = NSLayoutConstraint(item: centerView, attribute: .CenterX, relatedBy: .Equal, toItem: view, attribute: .CenterX, multiplier: 1.0, constant: 0) 
     let constraintCenterViewCenterY = NSLayoutConstraint(item: centerView, attribute: .CenterY, relatedBy: .Equal, toItem: view, attribute: .CenterY, multiplier: 1.0, constant: 0) 

     let constraintUsernameLabelHLeading = NSLayoutConstraint(item: usernameLabel, attribute: .Leading, relatedBy: .Equal, toItem: centerView, attribute: .Leading, multiplier: 1.0, constant: 8) 
     let constraintUsernameLabelHTrailing = NSLayoutConstraint(item: usernameLabel, attribute: .Trailing, relatedBy: .Equal, toItem: usernameField, attribute: .Leading, multiplier: 1.0, constant: 8) 
     let constraintUsernameLabelAlignBottom = NSLayoutConstraint(item: usernameLabel, attribute: .Bottom, relatedBy: .Equal, toItem: usernameField, attribute: .Bottom, multiplier: 1.0, constant: 0) 

     let constraintUsernameFieldVTop = NSLayoutConstraint(item: usernameField, attribute: .Top, relatedBy: .Equal, toItem: centerView, attribute: .Top, multiplier: 1.0, constant: 8) 
     let constraintUsernameFieldHTrailing = NSLayoutConstraint(item: usernameField, attribute: .Trailing, relatedBy: .Equal, toItem: centerView, attribute: .Trailing, multiplier: 1.0, constant: -8) 
     let constraintUsernameFieldVBottom = NSLayoutConstraint(item: usernameField, attribute: .Bottom, relatedBy: .Equal, toItem: passwordField, attribute: .Top, multiplier: 1.0, constant: 8) 

     let constraintPasswordLabelHLeading = NSLayoutConstraint(item: passwordLabel, attribute: .Leading, relatedBy: .Equal, toItem: centerView, attribute: .Leading, multiplier: 1.0, constant: 8) 
     let constraintPasswordLabelHTrailing = NSLayoutConstraint(item: passwordLabel, attribute: .Trailing, relatedBy: .Equal, toItem: passwordField, attribute: .Leading, multiplier: 1.0, constant: 8) 
     let constraintPasswordLabelAlignBottom = NSLayoutConstraint(item: passwordLabel, attribute: .Bottom, relatedBy: .Equal, toItem: passwordField, attribute: .Bottom, multiplier: 1.0, constant: 0) 

     let constraintPasswordFieldHTrailing = NSLayoutConstraint(item: passwordField, attribute: .Trailing, relatedBy: .Equal, toItem: centerView, attribute: .Trailing, multiplier: 1.0, constant: -8) 

     centerView.setTranslatesAutoresizingMaskIntoConstraints(false) 
     usernameLabel.setTranslatesAutoresizingMaskIntoConstraints(false) 
     usernameField.setTranslatesAutoresizingMaskIntoConstraints(false) 
     passwordField.setTranslatesAutoresizingMaskIntoConstraints(false) 
     passwordLabel.setTranslatesAutoresizingMaskIntoConstraints(false) 
     // submitButton.setTranslatesAutoresizingMaskIntoConstraints(false) 

     NSLayoutConstraint.activateConstraints([constraintCenterViewHeight, constraintCenterViewWidth, constraintCenterViewCenterX, constraintCenterViewCenterY, constraintUsernameLabelHLeading, 
      constraintUsernameLabelHTrailing, constraintUsernameLabelAlignBottom, constraintUsernameFieldVTop, constraintUsernameFieldHTrailing, constraintUsernameFieldVBottom, constraintPasswordLabelHLeading, constraintPasswordLabelHTrailing, constraintPasswordLabelAlignBottom, constraintPasswordFieldHTrailing]) 
    } 

    override func didReceiveMemoryWarning() { 
     super.didReceiveMemoryWarning() 
     // Dispose of any resources that can be recreated. 
    } 


} 

Antwort

15

My first question is: if the labels have an intrinsic content size, and everything else is pinned to the yellow view, then why do I need to define a fixed width and height? Why wouldn't it infer the width and height from the intrinsic content of its subviews?

Die username und password Felder haben keine Breite. Sie erhalten ihre Breiten, indem sie an die Etiketten am linken und rechten Rand der centerView angeheftet werden. Wenn Sie Einschränkungen hinzugefügt haben, um diesen Feldern eine Breite zu geben, kann die centerView ihre Breite anhand ihres Inhalts bestimmen.


Moving on and trying to recreate this layout in code was giving me an error: Terminating app due to uncaught exception 'NSGenericException', reason: 'Unable to activate constraint with items > and > because they have no common ancestor. Does the constraint reference items in different view hierarchies? That's illegal.'

Ihr erstes Problem ist, dass Sie nicht die passwordLabel als Subview hinzugefügt haben:

self.centerView.addSubview(passwordLabel) 

das ist, was der Fehler gab. Die passwordLabel war nicht in der Ansichtshierarchie, so dass Auto-Layout nicht wusste, wie man es mit der centerView einschränken kann.

Ihr zweites Problem ist, dass Sie die width und height der centerView auf 0 setzen.Versuchen Sie größere Werte wie 100 und 300:

let constraintCenterViewHeight = NSLayoutConstraint(item: centerView, attribute: .Height, 
    relatedBy: .Equal, toItem: nil, attribute: .NotAnAttribute, multiplier: 1.0, 
    constant: 100) 

let constraintCenterViewWidth = NSLayoutConstraint(item: centerView, attribute: .Width, 
    relatedBy: .Equal, toItem: nil, attribute: .NotAnAttribute, multiplier: 1.0, 
    constant: 300) 

Ihr drittes Problem ist, dass einige Ihrer Konstanten müssen negativ sein:

// changed to -8 
let constraintUsernameLabelHTrailing = NSLayoutConstraint(item: usernameLabel, 
    attribute: .Trailing, relatedBy: .Equal, toItem: usernameField, 
    attribute: .Leading, multiplier: 1.0, constant: -8) 

// changed to -8 
let constraintUsernameFieldVBottom = NSLayoutConstraint(item: usernameField, 
    attribute: .Bottom, relatedBy: .Equal, toItem: passwordField, 
    attribute: .Top, multiplier: 1.0, constant: -8) 

// changed to -8 
let constraintPasswordLabelHTrailing = NSLayoutConstraint(item: passwordLabel, 
    attribute: .Trailing, relatedBy: .Equal, toItem: passwordField, 
    attribute: .Leading, multiplier: 1.0, constant: -8) 
+0

Ich suchte Tage in diesem Code und sah nie die fehlende subview, noch die Breite und Höhe als 0! Ah-ha, (als ich schrieb, dass ich es nicht verstanden habe) dachte mein stumpfes altes Gehirn, dass die Hinterkante von usernameLabel zur Vorderkante von usernameField 8 Punkte weiter entlang der Y- oder horizontalen Achse sein sollte. ABER das ist nur ein weiterer dummer Fehler. Ich möchte die Trailing-Kante von usernameLabel an die Vorderkante von usernameField entlang der Y- oder horizontalen Achse zurückführen. Sehr leid und sehr dankbar, dass Sie darauf hingewiesen haben. Es ist toll zu wissen, dass dieses Zeug funktioniert, wenn Sie es verstehen. – DrWhat

4

ich die gleiche Fehlermeldung anzeigt, aber der Grund war es passiert etwas anderes :

ich den Fehler bekam, weil ich mein Super verweisen vor ich den Blick auf seine Superview hinzugefügt. < - Schreiben Sie Ihren Code als solcher ist nicht anders als view.addSubview(stackView) selbst nicht enthalten. Beide würden den gleichen Fehler erzeugen - weil die Ansicht keine definierte Beziehung zu ihrer Superansicht hat.

In mein Code unten, Zeilen B1, B2 geschehen, bevor ich die Ansicht der Superview hinzufügen (in Zeile AA).

Dies ist mein Code:

class ViewController: UIViewController { 

    var label = UILabel() 
    var button = UIButton() 
    var stackView = UIStackView() 

    override func viewDidLoad() { 
     super.viewDidLoad() 
     view.backgroundColor = UIColor.cyan 
     navigationItem.title = "VCTitle" 
     setupUI() 
    } 

    private func setupUI(){ 

     label.backgroundColor = UIColor.blue 
     label.heightAnchor.constraint(equalToConstant: 50).isActive = true 
     label.widthAnchor.constraint(equalToConstant: 80).isActive = true 

     button.backgroundColor = UIColor.purple 
     button.heightAnchor.constraint(equalToConstant: 30).isActive = true 
     button.widthAnchor.constraint(equalToConstant: 10).isActive = true 

     setupStackView() 

     stackView.addArrangedSubview(label) 
     stackView.addArrangedSubview(button) 
     stackView.translatesAutoresizingMaskIntoConstraints = false 
     view.addSubview(stackView) // AA 


    } 

    private func setupStackView(){ 

     stackView.axis = UILayoutConstraintAxis.vertical 
     stackView.distribution = UIStackViewDistribution.equalSpacing 
     stackView.alignment = UIStackViewAlignment.center 
     stackView.spacing = 15 

     stackView.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true // B1 
     stackView.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true // B2 

    } 


} 

Der richtige Weg, es zu tun Linie B1 zu bewegen, B2, um überall nach Linie AA.

class ViewController: UIViewController { 

    var label = UILabel() 
    var button = UIButton() 
    var stackView = UIStackView() 

    override func viewDidLoad() { 
     super.viewDidLoad() 
     view.backgroundColor = UIColor.cyan 
     navigationItem.title = "VCTitle" 
     setupUI() 
    } 

    private func setupUI(){ 

     label.backgroundColor = UIColor.blue 
     label.heightAnchor.constraint(equalToConstant: 50).isActive = true 
     label.widthAnchor.constraint(equalToConstant: 80).isActive = true 

     button.backgroundColor = UIColor.purple 
     button.heightAnchor.constraint(equalToConstant: 30).isActive = true 
     button.widthAnchor.constraint(equalToConstant: 10).isActive = true 

     setupStackView() 

     stackView.addArrangedSubview(label) 
     stackView.addArrangedSubview(button) 
     stackView.translatesAutoresizingMaskIntoConstraints = false 
     view.addSubview(stackView) //AA 

     // The 2 lines below are now in the right place. 
     stackView.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true // B1 
     stackView.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true // B2 


    } 

    private func setupStackView(){ 

     stackView.axis = UILayoutConstraintAxis.vertical 
     stackView.distribution = UIStackViewDistribution.equalSpacing 
     stackView.alignment = UIStackViewAlignment.center 
     stackView.spacing = 15 

    } 
} 
Verwandte Themen