2015-09-07 19 views
6

Sto provando a comprendere i limiti di autolayout. A seguito di una sfida guida Ray W (senza una soluzione e di discussione), il layout dovrebbe assomiglia a questo:Vincoli nel codice utilizzando swift - impossibile attivare

desired layout

Farlo in IB stato abbastanza facile - creare una vista di colore giallo con una larghezza e altezza, centrato verticalmente e appuntato orizzontalmente ai margini; quindi crea etichette, campi e un pulsante con semplici vincoli all'interno di quella vista.

La mia prima domanda è: se le etichette hanno una dimensione intrinseca del contenuto e tutto il resto è fissato alla vista gialla, allora perché devo definire una larghezza e un'altezza fissa? Perché non inferire la larghezza e l'altezza dal contenuto intrinseco delle sue sottoview?

Passando e cercando di ricreare questo layout in codice mi stava dando un errore:

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.'

Seconda domanda: quali sono questi livelli, e tutti i miei punti di vista sono in una heirachy - superview contiene la vista di colore giallo contiene le etichette e i campi di testo.

Dopo molto dolore, ho provato a ricreare esattamente i vincoli apportati in IB, ma questo ha aggiunto solo il seguente errore: Impossibile soddisfare contemporaneamente i vincoli.

(
    "<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>") 

Domanda/e finale/e: come faccio a sapere quale vista è vista 0x7aetc? E dov'è questo vincolo nel mio codice? Il resto sembra ok (?).

Devo fare qualcosa di molto sbagliato ad un livello base.

Ecco il mio codice:

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. 
    } 


} 

risposta

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?

I username e password campi non hanno una larghezza. Otterranno la loro larghezza fissando le etichette a sinistra e a destra dello centerView. Se hai aggiunto dei vincoli per dare a questi campi una larghezza, il centerView potrebbe determinare la sua larghezza dal suo contenuto.


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.'

Il tuo primo problema è che non è stato aggiunto il passwordLabel come una visualizzazione secondaria:

self.centerView.addSubview(passwordLabel) 

questo è ciò che stava dando l'errore. Lo passwordLabel non si trovava nella gerarchia della vista, quindi Layout automatico non sapeva come vincolarlo con lo centerView.

Il secondo problema è che si imposta il width e height del centerView-0.Provate valori più grandi come 100 e 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) 

Il tuo terzo problema è che alcuni dei vostri costanti bisogno di essere negativo:

// 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

Ho guardato questo codice per giorni e non ho mai visto la sottoview mancante, né la larghezza e l'altezza come 0! Ah-ha, (mentre stavo scrivendo che non l'ho capito) il mio vecchio cervello sordo pensava che il bordo finale del nome utenteLabel al nome utente Il bordo anteriore del campo dovesse essere 8 punti più lungo lungo l'asse Y o orizzontale. MA questo è solo un altro stupido errore. Voglio che il bordo finale del nome utenteLabel al nome utente Il bordo anteriore del campo 8 punti indietro lungo la Y o l'asse orizzontale. Molto dispiaciuto, e molto grato per il fatto che tu l'abbia indicato. È bello sapere che questa roba funziona se la capisci. – DrWhat

4

ho ottenuto lo stesso errore, ma la ragione è accaduto era qualcosa di diverso :

ho avuto l'errore perché ho fatto riferimento alla mia superviewprima ho aggiunto la vista della sua superview. < - la scrittura del codice in questo modo è diverso da rispetto al non incluso view.addSubview(stackView) stesso. Entrambi genererebbero lo stesso errore, perché la vista non ha una relazione definita con la sua superview.

Nel codice my, le righe B1, B2 stanno accadendo prima di aggiungere la vista al superview (nella riga AA).

Questo è il mio codice:

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 

    } 


} 

Il modo corretto per farlo è quello di spostare la linea B1, B2 ovunque dopo linea 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 

    } 
} 
Problemi correlati