ios - custom - xib cell swift
Swift: ¿Cómo crear una vista personalizada para la sección de cabecera, usando un archivo XIB? (3)
El proceso típico para encabezados basados en NIB sería:
Cree la subclase
UITableViewHeaderFooterView
con, al menos, una salida para su etiqueta. Es posible que también desee darle algún identificador mediante el cual pueda aplicar 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 pueda informar al controlador de la vista de los eventos (como la pulsación del botón). Así, en Swift 3:// if you want your header to be able to inform view controller of key events, create protocol protocol CustomHeaderDelegate: class { func didTapButton(in section: Int) } // define CustomHeader class with necessary `delegate`, `@IBOutlet` and `@IBAction`: class CustomHeader: UITableViewHeaderFooterView { 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 @IBAction func didTapButton(_ sender: AnyObject) { delegate?.didTapButton(in: sectionNumber) } }
Crear 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, los pasos clave 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 lo que sea su subclase
UITableViewHeaderFooterView
(en mi ejemplo,CustomHeader
);Agregue sus controles y restricciones en IB;
@IBOutlet
referencias de@IBOutlet
a puntos de venta en su código Swift;@IBAction
el botón a la@IBAction
En
viewDidLoad
en el controlador de vista, registre el NIB. En Swift 3:override func viewDidLoad() { super.viewDidLoad() tableView.register(UINib(nibName: "CustomHeader", bundle: nil), forHeaderFooterViewReuseIdentifier: "CustomHeader") }
O en Swift 2:
override func viewDidLoad() { super.viewDidLoad() tableView.registerNib(UINib(nibName: "CustomHeader", bundle: nil), forHeaderFooterViewReuseIdentifier: "CustomHeader") }
En
viewForHeaderInSection
, elimine la cola de una vista reutilizable utilizando 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 debe hacer (para que el protocolo funcione correctamente) 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 }
O, en Swift 2:
override func tableView(tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? { let headerView = tableView.dequeueReusableHeaderFooterViewWithIdentifier("CustomHeader") as! CustomHeader headerView.customLabel.text = content[section].name headerView.sectionNumber = section headerView.delegate = self return headerView } override func tableView(tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat { return 44 // or whatever }
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 didTapButton(in section: Int) { print("/(section)") } }
Todo esto suena confuso cuando enumero todos los pasos involucrados, pero en realidad es bastante simple una vez que lo has hecho una o dos veces. Creo que es más simple que construir la vista de encabezado mediante programación.
Puedo crear una vista personalizada simple para HeadSInSection en programación como a continuación. Pero quiero hacer cosas mucho más complejas, tal vez la conexión con una clase diferente y alcanzar sus propiedades como una celda tableView. Simplemente, quiero ver lo que hago.
func tableView(tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
if(section == 0) {
let view = UIView() // The width will be the same as the cell, and the height should be set in tableView:heightForRowAtIndexPath:
let label = UILabel()
let button = UIButton(type: UIButtonType.System)
label.text="My Details"
button.setTitle("Test Title", forState: .Normal)
// button.addTarget(self, action: Selector("visibleRow:"), forControlEvents:.TouchUpInside)
view.addSubview(label)
view.addSubview(button)
label.translatesAutoresizingMaskIntoConstraints = false
button.translatesAutoresizingMaskIntoConstraints = false
let views = ["label": label, "button": button, "view": view]
let horizontallayoutContraints = NSLayoutConstraint.constraintsWithVisualFormat("H:|-10-[label]-60-[button]-10-|", options: .AlignAllCenterY, metrics: nil, views: views)
view.addConstraints(horizontallayoutContraints)
let verticalLayoutContraint = NSLayoutConstraint(item: label, attribute: .CenterY, relatedBy: .Equal, toItem: view, attribute: .CenterY, multiplier: 1, constant: 0)
view.addConstraint(verticalLayoutContraint)
return view
}
return nil
}
func tableView(tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return 50
}
¿Hay alguien que explique cómo puedo crear una vista de encabezado tableView personalizada utilizando xib? Me he encontrado con viejos temas de Obj-C pero soy nuevo con el lenguaje Swift. Si alguien lo explicara tan detallado, sería genial.
1.edición: el botón @IBAction no se conecta con mi ViewController. (Fijo)
Resuelto con el propietario del archivo, clase base ViewController (se hizo clic en el menú de contorno izquierdo).
2.edición: problema de altura del encabezado (fijo)
Se solucionó la adición de headerView.clipsToBounds = true en el método viewForHeaderInSection :
Para advertencias de restricción, esta respuesta resolvió mis problemas :
Cuando agregué ImageView incluso la misma restricción de altura con este método en viewController, fluye sobre tableView. Las filas se ven como una imagen .
func tableView(tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return 120
}
Si lo uso, automáticamente AjustaScrollViewInsets en viewDidLoad, en este caso, la imagen fluye bajo la barra de navegación. -fijo-
self.automaticallyAdjustsScrollViewInsets = false
3.edición: Si el botón está bajo Ver (Fijo)
@IBAction func didTapButton(sender: AnyObject) {
print("tapped")
if let upView = sender.superview {
if let headerView = upView?.superview as? CustomHeader {
print("in section /(headerView.sectionNumber)")
}
}
}
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 "sabiduría" de aceptación y numerosos votos, pero intentaré reunir el valor para decir la verdad.
El problema, sencillamente, es que no puede convertir mágicamente una UIView en una punta en un UITableViewHeaderFooterView simplemente declarándolo así en el inspector de Identidad. Un UITableViewHeaderFooterView tiene características importantes que son clave para su correcto funcionamiento, y una UIView simple, sin importar cómo se pueda lanzar , carece de ellas.
Un UITableViewHeaderFooterView tiene un
contentView
, y todas sus subvistas personalizadas deben agregarse a esto, no al UITableViewHeaderFooterView.Pero una vista UIV misteriosa como un UITableViewHeaderFooterView carece de esta
contentView
en la punta. Por lo tanto, cuando Rob dice "Agregue sus controles y restricciones en IB", le pide que agregue subvistas directamente a UITableViewHeaderFooterView , y no a sucontentView
. De este modo, el encabezado termina incorrectamente configurado.Otro signo del problema es que no se le permite dar un color de fondo a un UITableViewHeaderFooterView. Si lo haces, recibirás este mensaje en la consola:
La configuración del color de fondo en UITableViewHeaderFooterView ha quedado en desuso. En su lugar, configure una vista personalizada con el color de fondo que desee en la propiedad backgroundView.
Pero en la punta, no puede evitar establecer un color de fondo en su UITableViewHeaderFooterFooterView, y recibe ese mensaje en la consola.
Entonces, ¿cuál es la respuesta correcta a la pregunta? No hay respuesta posible . Apple ha hecho un gran error aquí. Han proporcionado un método que le permite registrar una plumilla como la fuente de su UITableViewHeaderFooterFooterView, pero no hay un UITableViewHeaderFooterFooterView en la biblioteca de objetos . Por lo tanto este método es inútil . Es imposible diseñar un UITableViewHeaderFooterView correctamente en una punta.
Este es un error enorme en Xcode. Presenté un informe de error sobre este asunto en 2013 y todavía está allí, abierto. Repito el error año tras año, y Apple sigue rechazándolo, diciendo "No se ha determinado cómo o cuándo se resolverá el problema". Así que reconocen el error, pero no hacen nada al respecto.
Sin embargo, lo que puede hacer es diseñar una UIView normal en la punta, y luego, en el código (en su implementación de viewForHeaderInSection
), cargue la vista manualmente desde la punta y contentView
en el contentView
de su vista de encabezado.
Por ejemplo, digamos que queremos diseñar nuestro encabezado en la punta, y tenemos una etiqueta en el encabezado a la que queremos conectar un lab
toma de corriente. Entonces necesitamos tanto una clase de encabezado personalizado como una clase de vista personalizada:
class MyHeaderView : UITableViewHeaderFooterView {
weak var content : MyHeaderViewContent!
}
class MyHeaderViewContent : UIView {
@IBOutlet weak var lab : UILabel!
}
Registramos la clase de nuestra vista de encabezado, no la punta:
self.tableView.register(MyHeaderView.self,
forHeaderFooterViewReuseIdentifier: self.headerID)
En el archivo xib de vista, declaramos que nuestra vista es un MyHeaderViewContent, no un MyHeaderView.
En viewForHeaderInSection
, viewForHeaderInSection
la vista de la punta, la contentView
en el contentView
del encabezado y configuramos la referencia a ella:
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 terrible y molesto, pero es lo mejor que puedes hacer.
No tengo suficiente reputación para agregar comentarios a la respuesta de Matt.
De todos modos, lo único que falta aquí es eliminar todas las subvistas de UITableViewHeaderFooterFooterView.contentView antes de agregar nuevas vistas. Esto restablecerá la celda reutilizada al estado inicial y evitará la pérdida de memoria.