Saltar al contenido

Swift – ¿Cómo crear viewForHeaderInSection personalizado, usando un archivo XIB?

Basta ya de buscar por todo internet porque estás al sitio correcto, tenemos la respuesta que necesitas sin problema.

Solución:

El proceso típico para los encabezados basados ​​en NIB sería:

  1. Crear UITableViewHeaderFooterView subclase con, al menos, una salida para su etiqueta. Es posible que también desee asignarle algún identificador mediante el cual pueda realizar ingeniería inversa a qué sección corresponde este encabezado. Del mismo modo, es posible que desee especificar un protocolo mediante el cual el encabezado puede informar al controlador de vista de los eventos (como tocar el botón). Por lo tanto, en Swift 3 y posteriores:

    // if you want your header to be able to inform view controller of key events, create protocol
    
    protocol CustomHeaderDelegate: class 
        func customHeader(_ customHeader: CustomHeader, didTapButtonInSection section: Int)
    
    
    // define CustomHeader class with necessary `delegate`, `@IBOutlet` and `@IBAction`:
    
    class CustomHeader: UITableViewHeaderFooterView 
        static let reuseIdentifier = "CustomHeader"
    
        weak var delegate: CustomHeaderDelegate?
    
        @IBOutlet weak var customLabel: UILabel!
    
        var sectionNumber: Int!  // you don't have to do this, but it can be useful to have reference back to the section number so that when you tap on a button, you know which section you came from; obviously this is problematic if you insert/delete sections after the table is loaded; always reload in that case
    
        @IBAction func didTapButton(_ sender: AnyObject) 
            delegate?.customHeader(self, didTapButtonInSection: section)
        
    
    
    
  2. Cree NIB. Personalmente, le doy al NIB el mismo nombre que la clase base para simplificar la administración de mis archivos en mi proyecto y evitar confusiones. De todos modos, el key los pasos incluyen:

    • Cree la vista NIB, o si comenzó con una NIB vacía, agregue la vista a la NIB;

    • Establezca la clase base de la vista para que sea cualquiera que sea su UITableViewHeaderFooterView subclase fue (en mi ejemplo, CustomHeader);

    • Agregue sus controles y restricciones en IB;

    • Conectar @IBOutlet referencias a puntos de venta en su código Swift;

    • Conecte el botón al @IBAction; y

    • Para la vista raíz en el NIB, asegúrese de establecer el color de fondo en “predeterminado” o, de lo contrario, recibirá advertencias molestas sobre el cambio de colores de fondo.

  3. En el viewDidLoad en el controlador de vista, registre el NIB. En Swift 3 y posteriores:

    override func viewDidLoad() 
        super.viewDidLoad()
    
        tableView.register(UINib(nibName: "CustomHeader", bundle: nil), forHeaderFooterViewReuseIdentifier: CustomHeader.reuseIdentifier)
    
    
  4. En viewForHeaderInSection, quite la cola de una vista reutilizable con el mismo identificador que especificó en el paso anterior. Una vez hecho esto, ahora puede usar su salida, no tiene que hacer nada con restricciones creadas mediante programación, etc. Lo único que necesita hacer (para que el protocolo del botón funcione) es especificar su delegado. Por ejemplo, en Swift 3:

    override func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? 
        let headerView = tableView.dequeueReusableHeaderFooterView(withIdentifier: "CustomHeader") as! CustomHeader
    
        headerView.customLabel.text = content[section].name  // set this however is appropriate for your app's model
        headerView.sectionNumber = section
        headerView.delegate = self
    
        return headerView
    
    
    override func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat 
        return 44  // or whatever
    
    
  5. Obviamente, si va a especificar el controlador de vista como el delegate para el botón en la vista de encabezado, debe cumplir con ese protocolo:

    extension ViewController: CustomHeaderDelegate 
        func customHeader(_ customHeader: CustomHeader, didTapButtonInSection section: Int) 
            print("did tap button", section)
        
    
    

Todo esto suena confuso cuando enumero todos los pasos involucrados, pero es realmente bastante simple una vez que lo ha hecho una o dos veces. Creo que es más sencillo que crear la vista del encabezado mediante programación.


En la respuesta de Matt, protesta:

El problema, simplemente, es que no se puede convertir mágicamente un UIView en una punta en un UITableViewHeaderFooterView simplemente declarándolo así en el inspector de Identidad.

Esto simplemente no es correcto. Si utiliza el enfoque basado en NIB anterior, la clase de la que se crea una instancia para la vista raíz de esta vista de encabezado es a UITableViewHeaderFooterView subclase, no una UIView. Crea una instancia de cualquier clase que especifique para la clase base para la vista raíz de NIB.

Sin embargo, lo que es correcto es que algunas de las propiedades de esta clase (en particular, contentView) no se utilizan en este enfoque basado en NIB. Realmente debería ser una propiedad opcional, al igual que textLabel y detailTextLabel son (o, mejor, deberían agregar el soporte adecuado para UITableViewHeaderFooterView en IB). Estoy de acuerdo en que este es un diseño deficiente por parte de Apple, pero me parece un detalle descuidado e idiosincrásico, pero un problema menor dados todos los problemas en las vistas de tabla. Por ejemplo, es extraordinario que después de todos estos años, todavía no podamos hacer prototipos de vistas de encabezado / pie de página en guiones gráficos y tengamos que confiar en estas técnicas de registro de clases y NIB.

Pero es incorrecto concluir que uno no puede usar register(_:forHeaderFooterViewReuseIdentifier:), un método API que se ha utilizado activamente desde iOS 6. No tiremos al bebé con el agua de la bañera.


Consulte la revisión anterior de esta respuesta para las representaciones de Swift 2.

La respuesta de Rob, aunque suena convincente y ha resistido la prueba del tiempo, es incorrecta y siempre lo fue. Es difícil estar solo frente a la abrumadora multitud de “sabiduría” de aceptación y numerosos votos a favor, pero intentaré reunir el valor para decir la verdad.

El problema, simplemente, es que no se puede convertir mágicamente un UIView en una plumilla en un UITableViewHeaderFooterView simplemente declarándolo así en el inspector de identidad. Un UITableViewHeaderFooterView tiene características importantes que son key a su correcto funcionamiento, y una vista UIV simple, sin importar cómo pueda emitir carece de ellos.

  • Un UITableViewHeaderFooterView tiene un contentView, y todas sus subvistas personalizadas deben agregarse a esto, no a UITableViewHeaderFooterView.

    Pero un UIView emitido misteriosamente como UITableViewHeaderFooterView carece de esto contentView en la punta. Por lo tanto, cuando Rob dice “Agregue sus controles y restricciones en IB”, le pide que agregue subvistas directamente al UITableViewHeaderFooterView, y no a su contentView. Por tanto, el encabezado acaba mal configurado.

  • Otro signo del problema es que no se le permite dar un color de fondo a UITableViewHeaderFooterView. Si lo hace, recibirá este mensaje en la consola:

    La configuración del color de fondo en UITableViewHeaderFooterView ha quedado obsoleta. En su lugar, establezca una UIView personalizada con el color de fondo que desee en la propiedad backgroundView.

    Pero en la punta, no puedes ayuda establecer un color de fondo en su UITableViewHeaderFooterView, y usted hacer obtener ese mensaje en la consola.

Entonces, ¿cuál es la respuesta correcta a la pregunta? Hay no es posible respuesta. Apple ha hecho un gran error aquí. Han proporcionado un método que le permite registrar una plumilla como fuente de su UITableViewHeaderFooterView, pero hay no UITableViewHeaderFooterView en la biblioteca de objetos. Por lo tanto, este método es inútil. Está imposible para diseñar un UITableViewHeaderFooterView correctamente en una plumilla.

Este es un gran error en Xcode. Presenté un informe de error sobre este asunto en 2013 y todavía está ahí, abierto. Vuelvo a presentar el error año tras año, y Apple sigue rechazando, diciendo “No se ha determinado cómo ni cuándo se resolverá el problema”. Entonces reconocen el error, pero no hacen nada al respecto.

Lo que tu pueden Sin embargo, lo que sí es diseñar una UIView normal en el nib, y luego, en el código (en su implementación de viewForHeaderInSection), cargue la vista manualmente desde la plumilla y colóquela en el contentView de su vista de encabezado.

Por ejemplo, digamos que queremos diseñar nuestro encabezado en la plumilla, y tenemos una etiqueta en el encabezado a la que queremos conectar una salida. lab. Entonces necesitamos tanto una clase de encabezado personalizada como una clase de vista personalizada:

class MyHeaderView : UITableViewHeaderFooterView 
    weak var content : MyHeaderViewContent!

class MyHeaderViewContent : UIView 
    @IBOutlet weak var lab : UILabel!

Registramos nuestras vistas de encabezado clase, no la punta:

self.tableView.register(MyHeaderView.self,
    forHeaderFooterViewReuseIdentifier: self.headerID)

En la vista xib archivo, declaramos que nuestra vista es un MyHeaderViewContent – no un MyHeaderView.

En viewForHeaderInSection, sacamos la vista de la plumilla y la metemos en el contentView del encabezado y configurar la referencia al mismo:

override func tableView(_ tableView: UITableView, 
    viewForHeaderInSection section: Int) -> UIView? 
    let h = tableView.dequeueReusableHeaderFooterView(
        withIdentifier: self.headerID) as! MyHeaderView
    if h.content == nil 
        let v = UINib(nibName: "MyHeaderView", bundle: nil).instantiate
            (withOwner: nil, options: nil)[0] as! MyHeaderViewContent
        h.contentView.addSubview(v)
        v.translatesAutoresizingMaskIntoConstraints = false
        v.topAnchor.constraint(equalTo: h.contentView.topAnchor).isActive = true
        v.bottomAnchor.constraint(equalTo: h.contentView.bottomAnchor).isActive = true
        v.leadingAnchor.constraint(equalTo: h.contentView.leadingAnchor).isActive = true
        v.trailingAnchor.constraint(equalTo: h.contentView.trailingAnchor).isActive = true
        h.content = v
        // other initializations for all headers go here
    
    h.content.lab.text = // whatever
    // other initializations for this header go here
    return h

Es espantoso y molesto, pero es lo mejor que puede hacer.

Cree un UITableViewHeaderFooterView y su archivo xib correspondiente.

class BeerListSectionHeader: UITableViewHeaderFooterView 
    @IBOutlet weak var sectionLabel: UILabel!
    @IBOutlet weak var abvLabel: UILabel!

Registre la plumilla de manera similar a como registra una celda de vista de tabla. El nombre de la plumilla y el identificador de reutilización deben coincidir con los nombres de sus archivos. (El xib no tiene una identificación de reutilización).

func registerHeader 
    let nib = UINib(nibName: "BeerListSectionHeader", bundle: nil)
    tableView.register(nib, forHeaderFooterViewReuseIdentifier: "BeerListSectionHeader")

Dequeue y use de manera similar a una celda. El identificador es el nombre del archivo.

override func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? 

    let header = tableView.dequeueReusableHeaderFooterView(withIdentifier: "BeerListSectionHeader") as! BeerListSectionHeader

    let sectionTitle = allStyles[section].name
    header.sectionLabel.text = sectionTitle
    header.dismissButton?.addTarget(self, action: #selector(dismissView), for: .touchUpInside)
    return header

No olvide la altura del encabezado.

 override func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat 
     return BeerListSectionHeader.height
 

Reseñas y calificaciones del tutorial

¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)



Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *