tutorial - uitableviewcontroller swift 4
Cómo implementar secciones y encabezados de sección de vista de tabla personalizada con Storyboard (15)
Agregar celda en
StoryBoard
y establecerreuseidentified
Código
class TP_TaskViewTableViewSectionHeader: UITableViewCell{ }
y
Utilizar:
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? { let header = tableView.dequeueReusableCell(withIdentifier: "header", for: IndexPath.init(row: 0, section: section)) return header }
Sin usar un guión gráfico, simplemente podríamos arrastrar una UIView
en el lienzo, colocarla y luego configurarla en los tableView:viewForHeaderInSection
o tableView:viewForFooterInSection
.
¿Cómo logramos esto con StoryBoard donde no podemos arrastrar una UIView en el lienzo?
¿Qué pasa con una solución donde el encabezado se basa en una matriz de vista:
class myViewController: UIViewController {
var header: [UILabel] = myStringArray.map { (thisTitle: String) -> UILabel in
let headerView = UILabel()
headerView.text = thisTitle
return(headerView)
}
Siguiente en el delegado:
extension myViewController: UITableViewDelegate {
func tableView(tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
return(header[section])
}
}
Aquí está la respuesta de @Vitaliy Gozhenko , en Swift.
Para resumir, creará una UITableViewHeaderFooterView que contiene una UITableViewCell. Esta UITableViewCell será "dequeuable" y puedes diseñarla en tu guión gráfico.
Crear una clase UITableViewHeaderFooterView
class CustomHeaderFooterView: UITableViewHeaderFooterView { var cell : UITableViewCell? { willSet { cell?.removeFromSuperview() } didSet { if let cell = cell { cell.frame = self.bounds cell.autoresizingMask = [UIViewAutoresizing.FlexibleHeight, UIViewAutoresizing.FlexibleWidth] self.contentView.backgroundColor = UIColor .clearColor() self.contentView .addSubview(cell) } } }
Conecte su vista de tabla con esta clase en su función viewDidLoad:
self.tableView.registerClass(CustomHeaderFooterView.self, forHeaderFooterViewReuseIdentifier: "SECTION_ID")
Al preguntar, para un encabezado de sección, dequeue un CustomHeaderFooterView, e inserte una celda en él
func tableView(tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? { let view = self.tableView.dequeueReusableHeaderFooterViewWithIdentifier("SECTION_ID") as! CustomHeaderFooterView if view.cell == nil { let cell = self.tableView.dequeueReusableCellWithIdentifier("Cell") view.cell = cell; } // Fill the cell with data here return view; }
Cuando devuelvas contentView de la celda tendrás 2 problemas:
- accidente relacionado con gestos
- no reutiliza ContentView (siempre en la llamada
viewForHeaderInSection
, está creando una nueva celda)
Solución:
Clase de contenedor para encabezado de tabla / pie de página. Es solo contenedor, heredado de UITableViewHeaderFooterView
, que contiene la celda dentro
https://github.com/Magnat12/MGTableViewHeaderWrapperView.git
Clase de registro en su UITableView (por ejemplo, en viewDidLoad)
- (void)viewDidLoad {
[super viewDidLoad];
[self.tableView registerClass:[MGTableViewHeaderWrapperView class] forHeaderFooterViewReuseIdentifier:@"ProfileEditSectionHeader"];
}
En su UITableViewDelegate:
- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {
MGTableViewHeaderWrapperView *view = [tableView dequeueReusableHeaderFooterViewWithIdentifier:@"ProfileEditSectionHeader"];
// init your custom cell
ProfileEditSectionTitleTableCell *cell = (ProfileEditSectionTitleTableCell * ) view.cell;
if (!cell) {
cell = [tableView dequeueReusableCellWithIdentifier:@"ProfileEditSectionTitleTableCell"];
view.cell = cell;
}
// Do something with your cell
return view;
}
Deberías usar la solución de Tieme como base pero olvidarte de viewWithTag:
y otros enfoques sospechosos, en su lugar intenta volver a cargar tu encabezado (volviendo a cargar esa sección).
Entonces, después de que haya sentado la vista de encabezado de celda personalizada con todas las cosas elegantes de AutoLayout, simplemente dequeue y devuelva contentView después de su configuración, como:
-(UIView *) tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {
static NSString *CellIdentifier = @"SectionHeader";
SettingsTableViewCell *sectionHeaderCell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
sectionHeaderCell.myPrettyLabel.text = @"Greetings";
sectionHeaderCell.contentView.backgroundColor = [UIColor whiteColor]; // don''t leave this transparent
return sectionHeaderCell.contentView;
}
En iOS 6.0 y versiones superiores, las cosas han cambiado con la nueva API dequeueReusableHeaderFooterViewWithIdentifier
.
He escrito una samwize.com/2015/11/06/… (probada en iOS 9), que se puede resumir como tal:
- Subclase
UITableViewHeaderFooterView
- Cree Nib con la vista de subclase y agregue 1 vista de contenedor que contenga todas las demás vistas en el encabezado / pie de página
- Registre el Nib en
viewDidLoad
- Implementa
viewForHeaderInSection
y utilizadequeueReusableHeaderFooterViewWithIdentifier
para recuperar el encabezado / pie de página
He estado en problemas en un escenario en el que Header nunca se reutilizó, incluso haciendo todos los pasos adecuados.
Así que como nota de sugerencia para todos los que quieran lograr la situación de mostrar secciones vacías (0 filas) advierta que:
dequeueReusableHeaderFooterViewWithIdentifier no reutilizará el encabezado hasta que devuelva al menos una fila
Espero eso ayude
La solución que se me ocurrió es básicamente la misma solución utilizada antes de la introducción de guiones gráficos.
Crea un nuevo archivo de clase de interfaz vacío. Arrastre un UIView al lienzo, diseño como desee.
Cargue el plumín de forma manual, asígnelo a la sección de encabezado / pie de página correspondiente en los métodos de delegado viewForHeaderInSection o viewForFooterInSection.
Tenía la esperanza de que Apple simplificara este escenario con guiones gráficos y siguiera buscando una solución mejor o más simple. Por ejemplo, los encabezados y pies de tabla personalizados son sencillos de agregar.
Lo tengo trabajando en iOS7 usando una celda prototipo en el guión gráfico. Tengo un botón en la vista del encabezado de la sección personalizada que activa un segue que está configurado en el guión gráfico.
Comience con la solución de Tieme
Como señala pedro.m, el problema es que al tocar el encabezado de sección se selecciona la primera celda de la sección.
Como señala Paul Von, esto se soluciona devolviendo el contentView de la celda en lugar de la celda completa.
Sin embargo, como señala Hons, una pulsación larga en dicho encabezado de sección bloqueará la aplicación.
La solución es eliminar cualquier gestureRecognizers de contentView.
-(UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {
static NSString *CellIdentifier = @"SectionHeader";
UITableViewCell *sectionHeaderView = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
while (sectionHeaderView.contentView.gestureRecognizers.count) {
[sectionHeaderView.contentView removeGestureRecognizer:[sectionHeaderView.contentView.gestureRecognizers objectAtIndex:0]];
}
return sectionHeaderView.contentView; }
Si no está usando gestos en las vistas de encabezado de su sección, parece que este pequeño truco lo hace.
Para dar seguimiento a la sugerencia de Damon , aquí es cómo hice que el encabezado se seleccionara como una fila normal con un indicador de divulgación.
Agregué un botón subclasificado desde UIButton (nombre de la subclase "ButtonWithArgument") a la celda del prototipo del encabezado y borré el texto del título (el texto en negrita "Title" es otro UILabel en la celda del prototipo)
luego, configure el Botón en la vista del encabezado completo y agregue un indicador de divulgación con el truco de Avario
- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
{
static NSString *CellIdentifier = @"PersonGroupHeader";
UITableViewCell *headerView = (UITableViewCell *) [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if(headerView == nil)
{
[NSException raise:@"headerView == nil, PersonGroupTableViewController" format:[NSString stringWithFormat:@"Storyboard does not have prototype cell with identifier %@",CellIdentifier]];
}
// https://.com/a/24044628/3075839
while(headerView.contentView.gestureRecognizers.count)
{
[headerView.contentView removeGestureRecognizer:[headerView.contentView.gestureRecognizers objectAtIndex:0]];
}
ButtonWithArgument *button = (ButtonWithArgument *)[headerView viewWithTag:4];
button.frame = headerView.bounds; // set tap area to entire header view
button.argument = [[NSNumber alloc] initWithInteger:section]; // from ButtonWithArguments subclass
[button addTarget:self action:@selector(headerViewTap:) forControlEvents:UIControlEventTouchUpInside];
// https://.com/a/20821178/3075839
UITableViewCell *disclosure = [[UITableViewCell alloc] init];
disclosure.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
disclosure.userInteractionEnabled = NO;
disclosure.frame = CGRectMake(button.bounds.origin.x + button.bounds.size.width - 20 - 5, // disclosure 20 px wide, right margin 5 px
(button.bounds.size.height - 20) / 2,
20,
20);
[button addSubview:disclosure];
// configure header title text
return headerView.contentView;
}
- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section
{
return 35.0f;
}
-(void) headerViewTap:(UIGestureRecognizer *)gestureRecognizer;
{
NSLog(@"header tap");
NSInteger section = ((NSNumber *)sender.argument).integerValue;
// do something here
}
ButtonWithArgument.h
#import <UIKit/UIKit.h>
@interface ButtonWithArgument : UIButton
@property (nonatomic, strong) NSObject *argument;
@end
ButtonWithArgument.m
#import "ButtonWithArgument.h"
@implementation ButtonWithArgument
@end
Sé que esta pregunta fue para iOS 5, pero para el beneficio de los futuros lectores, tenga en cuenta que con iOS 6 eficaz ahora podemos usar dequeueReusableHeaderFooterViewWithIdentifier
lugar de dequeueReusableCellWithIdentifier
.
Entonces, en viewDidLoad
, llame a registerNib:forHeaderFooterViewReuseIdentifier:
o registerClass:forHeaderFooterViewReuseIdentifier:
Luego, en viewForHeaderInSection
, llame a tableView:dequeueReusableHeaderFooterViewWithIdentifier:
No utiliza un prototipo de celda con esta API (es una vista basada en NIB o una vista creada mediante programación), pero esta es la nueva API para encabezados y pies de página descatalogados.
Si necesita una implementación Swift de esto, siga las instrucciones en la respuesta aceptada y luego en usted UITableViewController implemente los siguientes métodos:
override func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let cell = self.tableView.dequeueReusableCell(withIdentifier: "CustomHeader") as! CustomHeaderUITableViewCell
return cell
}
override func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return 75
}
Si usa storyboards, puede usar una celda prototipo en la vista de tabla para diseñar su vista de encabezado. Establezca un ID único y viewForHeaderInSection, puede quitar la cola de la celda con esa ID y convertirla en una UIView.
Solía hacer lo siguiente para crear vistas de cabecera / pie de página de forma perezosa:
- Agregue un controlador de vista de forma libre para el encabezado / pie de página de la sección al guión gráfico
- Manejar todas las cosas para el encabezado en el controlador de vista
- En el controlador de vista de tabla, proporcione una matriz mutable de controladores de vista para los encabezados / pies de página de sección repoblados con
[NSNull null]
- En viewForHeaderInSection / viewForFooterInSection si el controlador de vista aún no existe, créelo con storyboards instanciando ViewControllerWithIdentifier, recuérdelo en la matriz y devuelva la vista de controladores de vista
Solo use un prototipo de celda como encabezado de sección y / o pie de página.
- agregue una celda extra y coloque sus elementos deseados en ella.
- establecer el identificador a algo específico (en mi caso SectionHeader)
- implementar el
tableView:viewForHeaderInSection:
o el métodotableView:viewForFooterInSection:
- utiliza
[tableView dequeueReusableCellWithIdentifier:]
para obtener el encabezado - implementar el
tableView:heightForHeaderInSection:
-(UIView *) tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {
static NSString *CellIdentifier = @"SectionHeader";
UITableViewCell *headerView = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (headerView == nil){
[NSException raise:@"headerView == nil.." format:@"No cells with matching CellIdentifier loaded from your storyboard"];
}
return headerView;
}
Editar: Cómo cambiar el título del encabezado (pregunta comentada):
- Agregue una etiqueta a la celda del encabezado
- establecer la etiqueta de la etiqueta a un número específico (por ejemplo, 123)
- En su
tableView:viewForHeaderInSection:
method obtenga la etiqueta llamando:
UILabel *label = (UILabel *)[headerView viewWithTag:123];
- Ahora puede usar la etiqueta para establecer un nuevo título:
[label setText:@"New Title"];