Cómo: Vista personalizada de tamaño personalizado desde XIB con vista StackView en iOS

Recientemente comencé a sumergirme en el desarrollo de iOS con Swift nuevamente y ahora estoy buscando un UIControl personalizado. Por diseño y flexibilidad, esta vista personalizada se construye utilizando un StackView.

Lo que quiero lograr ...

... es básicamente tener la misma funcionalidad con el StackView que normalmente tengo cuando lo arrastro directamente al guión gráfico, donde cambia el tamaño para que no quepa. Esto es esencialmente lo mismo que las celdas de vista de tabla de tamaño propio.

Escaparate

Puedo reducir mi CustomView a un ejemplo simple: es un StackView con tres etiquetas,Alignment ajustado aCenter, Distribution Equal Centering. StackView se anclaría a las guías de diseño izquierda, derecha y superior (o al final, al principio y al principio). Esto lo puedo volver a crear fácilmente con un CustomView, cargado desde XIB traducido a un CustomView con los mismos parámetros: adjunté ambos como captura de pantalla.

Captura de pantalla de Simple StackView

Captura de pantalla de CustomView

Cargar el archivo XIB y configurarlo.
import UIKit

@IBDesignable
class CustomView: UIView {

    // loaded from NIB
    private weak var view: UIView!

    convenience init() {
        self.init(frame: CGRect())
    }

    override init(frame: CGRect) {
        super.init(frame: frame)
        self.loadNib()
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        self.loadNib()
    }

    override func layoutSubviews() {
        super.layoutSubvi,ews()

        // we need to adjust the frame of the subview to no longer match the size used
        // in the XIB file BUT the actual frame we got assinged from the superview
        self.view.frame = bounds
    }

    private func loadNib ()  {
        self.view = Bundle (for: type(of: self)).loadNibNamed(
            "CustomView", owner: self, options: nil)! [0] as! UIView
        self.addSubview(self.view)
    }
}

Lo que puede ver aquí es que simplemente cargo el archivo XIB, anulo las subvistas de diseño para adaptar el marco a la vista real y listo.

Preguntas

Entonces mis preguntas están relacionadas con cómo se aborda esto. Las etiquetas en mi StackView tienen un tamaño de contenido intrínseco, es decir, generalmente el StackView simplemente se adapta a la altura de las etiquetas sin quejarse, SI lo diseña en el guión gráfico. Sin embargo, no lo hace, si lo coloca todo en un XIB y deja el diseño tal como está: el IB se quejará de una altura desconocida si la vista se fija de tres maneras como en la captura de pantalla (arriba, adelante, abajo).

Leí la Guía de diseño automático y vi los videos de WWDC "Mysteries of AutoLayout", pero el tema aún es bastante nuevo para mí y no creo que haya tenido el enfoque correcto todavía. Entonces aquí es donde necesito ayuda

(1) ¿Debo establecer restricciones y desactivartranslatesAutoresizingMaskIntoConstraints ?

Lo primero que podría y debería hacer es establecertranslatesAutoresizingMaskIntoConstraints false de modo que no obtengo restricciones autogeneradas y defino mis propias restricciones. Para poder cambiarloadNib dentro de esto:

private func loadNib ()  {
    self.view = Bundle (for: type(of: self)).loadNibNamed(
        "CustomView", owner: self, options: nil)! [0] as! UIView
    self.view.translatesAutoresizingMaskIntoConstraints = false
    self.addSubview(self.view)

    self.addConstraint(NSLayoutConstraint (
        item: self
        , attribute: .bottom
        , relatedBy: .equal
        , toItem: self.view
        , attribute: .bottom
        , multiplier: 1.0
        , constant: 0))

    self.addConstraint(NSLayoutConstraint (
        item: self
        , attribute: .top
        , relatedBy: .equal
        , toItem: self.view
        , attribute: .top
        , multiplier: 1.0
        , constant: 0))

    self.addConstraint(NSLayoutConstraint (
        item: self.view
        , attribute: .leading
        , relatedBy: .equal
        , toItem: self
        , attribute: .leading
        , multiplier: 1.0
        , constant: 0))

    self.addConstraint(NSLayoutConstraint (
        item: self.view
        , attribute: .trailing
        , relatedBy: .equal
        , toItem: self
        , attribute: .trailing
        , multiplier: 1.0
        , constant: 0))
}

Básicamente, estiré la vista para que se ajuste al ancho completo de mi UIView en el Guión gráfico y restringí su parte superior e inferior a superior e inferior de StackView.

Tuve un montón de problemas con XCode 8 que se bloqueó cuando usé este, así que no estoy muy seguro de qué hacer con él.

(2) ¿O debería anularintrinsicContentSize ?

También podría simplemente anularintrinsicContentSize comprobando la altura de todos misarrangedSubviews y tome la que sea más alta, configure la altura de mi vista:

override var intrinsicContentSize: CGSize {
    var size = CGSize.zero

    for (_ , aSubView) in (self.view as! UIStackView).arrangedSubviews.enumerated() {
        let subViewHeight = aSubView.intrinsicContentSize.height
        if  subViewHeight > size.height {
            size.height = aSubView.intrinsicContentSize.height
        }
    }

    // add some padding
    size.height += 0
    size.width = UIViewNoIntrinsicMetric
    return size
}

Si bien esto seguramente me da más flexibilidad, también tendría que invalidar el tamaño del contenido intrínseco, verifique el Tipo dinámico y muchas otras cosas que preferiría evitar.

(3) ¿Estoy incluso cargando el XIB de la manera "propuesta"?

¿O hay una mejor manera, p. mediante el usoUINib.instantiate? Realmente no pude encontrar una mejor práctica para hacer eso.

(4) ¿Por qué tengo que hacer esto en primer lugar?

¿Realmente por qué? Solo moví el StackView a un CustomView. Entonces, ¿por qué StackView deja de adaptarse ahora, por qué tengo que establecer las restricciones superior e inferior?

Lo que más ha ayudado hasta ahora es esa frase clave enhttps://stackoverflow.com/a/24441368 :

"En general, el diseño automático se realiza de arriba hacia abajo. En otras palabras, primero se realiza un diseño de vista principal y luego se realiza cualquier diseño de vista secundaria".

.. pero realmente no estoy tan seguro de eso.

(5) ¿Y por qué tengo que agregar las restricciones en el código?

Suponiendo que desactivotranslatesAutoresizingMaskIntoConstraints entonces no puedo establecer restricciones como para las celdas tableview en IB? Siempre se traducirá en algo como label.top = top y, por lo tanto, la etiqueta se estiraría en lugar de que StackView herede el tamaño / altura. Además, realmente no puedo anclar el StackView a su vista de contenedor en el IB para el archivo XIB.

Espero que alguien pueda ayudarme por ahí. ¡Salud!

Respuestas a la pregunta(1)

Su respuesta a la pregunta