He estado aplastando mi cabeza contra la pared con esto durante las últimas 3 o 4 horas y parece que no puedo entenderlo. Tengo un UIViewController con un UITableView de pantalla completa dentro (hay algunas otras cosas en la pantalla, por lo que no puedo usar un UITableViewController) y quiero que mi tableHeaderView cambie de tamaño con el diseño automático. No hace falta decir que no está cooperando.
Vea la captura de pantalla a continuación.
Debido a que la etiqueta de descripción general (por ejemplo, el texto "Lista de información general aquí") tiene contenido dinámico, estoy usando el diseño automático para cambiar su tamaño y es una supervista. Tengo todo cambiando de tamaño muy bien, excepto por tableHeaderView, que está justo debajo de Paralax Table View en la jerarquía.
La única forma que he encontrado para cambiar el tamaño de esa vista de encabezado es programáticamente, con el siguiente código:
CGRect headerFrame = self.headerView.frame;
headerFrame.size.height = headerFrameHeight;
self.headerView.frame = headerFrame;
[self.listTableView setTableHeaderView:self.headerView];
En este caso, headerFrameHeight es un cálculo manual de la altura tableViewHeader de la siguiente manera (innerHeaderView es el área blanca, o la segunda "Vista", headerView es tableHeaderView) :
CGFloat startingY = self.innerHeaderView.frame.origin.y + self.overviewLabel.frame.origin.y;
CGRect overviewSize = [self.overviewLabel.text
boundingRectWithSize:CGSizeMake(290.f, CGFLOAT_MAX)
options:NSStringDrawingUsesLineFragmentOrigin
attributes:@{NSFontAttributeName: self.overviewLabel.font}
context:nil];
CGFloat overviewHeight = overviewSize.size.height;
CGFloat overviewPadding = ([self.overviewLabel.text length] > 0) ? 10 : 0; // If there's no overviewText, eliminate the padding in the overall height.
CGFloat headerFrameHeight = ceilf(startingY + overviewHeight + overviewPadding + 21.f + 10.f);
El cálculo manual funciona, pero es torpe y propenso a errores si las cosas cambian en el futuro. Lo que quiero poder hacer es que el tableHeaderView cambie de tamaño automáticamente en función de las restricciones proporcionadas, como puede hacerlo en cualquier otro lugar. Pero por mi vida, no puedo entenderlo.
Hay varias publicaciones en SO sobre esto, pero ninguna es clara y terminó confundiéndome más. Aquí hay algunos:
Realmente no tiene sentido cambiar la propiedad translatesAutoresizingMaskIntoConstraints a NO, ya que eso solo me causa errores y no tiene sentido conceptualmente de todos modos.
¡Cualquier ayuda sería realmente apreciada!
EDICIÓN 1: Gracias a la sugerencia de TomSwift, pude resolverlo. En lugar de calcular manualmente la altura de la descripción general, puedo hacer que se calcule para mí de la siguiente manera y luego volver a configurar tableHeaderView como antes.
[self.headerView setNeedsLayout];
[self.headerView layoutIfNeeded];
CGFloat height = [self.innerHeaderView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize].height + self.innerHeaderView.frame.origin.y; // adding the origin because innerHeaderView starts partway down headerView.
CGRect headerFrame = self.headerView.frame;
headerFrame.size.height = height;
self.headerView.frame = headerFrame;
[self.listTableView setTableHeaderView:self.headerView];
Edición 2: como han señalado otros, la solución publicada en la Edición 1 no parece funcionar en viewDidLoad. Sin embargo, parece funcionar en viewWillLayoutSubviews. Código de ejemplo a continuación:
// Note 1: The variable names below don't match the variables above - this is intended to be a simplified "final" answer.
// Note 2: _headerView was previously assigned to tableViewHeader (in loadView in my case since I now do everything programatically).
// Note 3: autoLayout code can be setup programatically in updateViewConstraints.
- (void)viewWillLayoutSubviews {
[super viewWillLayoutSubviews];
[_headerWrapper setNeedsLayout];
[_headerWrapper layoutIfNeeded];
CGFloat height = [_headerWrapper systemLayoutSizeFittingSize:UILayoutFittingCompressedSize].height;
CGRect headerFrame = _headerWrapper.frame;
headerFrame.size.height = height;
_headerWrapper.frame = headerFrame;
_tableView.tableHeaderView = _headerWrapper;
}
fuente
setTableHeaderView
no funciona en Xcode6. El problema es que las celdas están superpuestas por tableHeaderView. Sin embargo, funciona en Xcode5Respuestas:
Debe utilizar el
UIView systemLayoutSizeFittingSize:
método para obtener el tamaño delimitador mínimo de su vista de encabezado.Proporciono más información sobre el uso de esta API en esta Q / A:
¿Cómo cambiar el tamaño de la supervista para que se ajuste a todas las subvistas con diseño automático?
fuente
Realmente luché con este y colocar la configuración en viewDidLoad no funcionó para mí ya que el marco no está configurado en viewDidLoad, también terminé con toneladas de advertencias desordenadas donde la altura de diseño automático encapsulado del encabezado se reducía a 0 Solo noté el problema en iPad al presentar un tableView en una presentación de formulario.
Lo que resolvió el problema para mí fue configurar tableViewHeader en viewWillLayoutSubviews en lugar de viewDidLoad.
func viewWillLayoutSubviews() { super.viewWillLayoutSubviews() if tableView.tableViewHeaderView == nil { let header: MyHeaderView = MyHeaderView.createHeaderView() header.setNeedsUpdateConstraints() header.updateConstraintsIfNeeded() header.frame = CGRectMake(0, 0, CGRectGetWidth(tableView.bounds), CGFloat.max) var newFrame = header.frame header.setNeedsLayout() header.layoutIfNeeded() let newSize = header.systemLayoutSizeFittingSize(UILayoutFittingCompressedSize) newFrame.size.height = newSize.height header.frame = newFrame self.tableView.tableHeaderView = header } }
fuente
Encontré una manera elegante de usar el diseño automático para cambiar el tamaño de los encabezados de las tablas, con y sin animación.
Simplemente agregue esto a su View Controller.
func sizeHeaderToFit(tableView: UITableView) { if let headerView = tableView.tableHeaderView { let height = headerView.systemLayoutSizeFittingSize(UILayoutFittingCompressedSize).height var frame = headerView.frame frame.size.height = height headerView.frame = frame tableView.tableHeaderView = headerView headerView.setNeedsLayout() headerView.layoutIfNeeded() } }
Para cambiar el tamaño de acuerdo con una etiqueta que cambia dinámicamente:
@IBAction func addMoreText(sender: AnyObject) { self.label.text = self.label.text! + "\nThis header can dynamically resize according to its contents." } override func viewDidLayoutSubviews() { // viewDidLayoutSubviews is called when labels change. super.viewDidLayoutSubviews() sizeHeaderToFit(tableView) }
Para animar un cambio de tamaño de acuerdo con los cambios en una restricción:
@IBOutlet weak var makeThisTallerHeight: NSLayoutConstraint! @IBAction func makeThisTaller(sender: AnyObject) { UIView.animateWithDuration(0.3) { self.tableView.beginUpdates() self.makeThisTallerHeight.constant += 20 self.sizeHeaderToFit(self.tableView) self.tableView.endUpdates() } }
Vea el proyecto AutoResizingHeader para ver esto en acción. https://github.com/p-sun/Swift2-iOS9-UI
fuente
@IBOutlet makeThisTaller
y@IBAction fun makeThisTaller
como en el ejemplo. Además, restrinja todos los lados de su etiqueta al tableViewHeader (por ejemplo, arriba, abajo, izquierda y derecha).lblFeedDescription.preferredMaxLayoutWidth = lblFeedDescription.bounds.width
donde etiqueta es aquella que quiero aumentar de tamaño. Gracias !Esta solución cambia el tamaño de tableHeaderView y evita un bucle infinito en el
viewDidLayoutSubviews()
método que estaba teniendo con algunas de las otras respuestas aquí:override func viewDidLayoutSubviews() { super.viewDidLayoutSubviews() if let headerView = tableView.tableHeaderView { let height = headerView.systemLayoutSizeFitting(UILayoutFittingCompressedSize).height var headerFrame = headerView.frame // comparison necessary to avoid infinite loop if height != headerFrame.size.height { headerFrame.size.height = height headerView.frame = headerFrame tableView.tableHeaderView = headerView } } }
Vea también esta publicación: https://stackoverflow.com/a/34689293/1245231
fuente
Su solución usando systemLayoutSizeFittingSize: funciona si la vista de encabezado se actualiza una vez en cada apariencia de vista. En mi caso, la vista del encabezado se actualizó varias veces para reflejar los cambios de estado. Pero systemLayoutSizeFittingSize: siempre informó del mismo tamaño. Es decir, el tamaño correspondiente a la primera actualización.
Para obtener systemLayoutSizeFittingSize: para devolver el tamaño correcto después de cada actualización, primero tenía que eliminar la vista del encabezado de la tabla antes de actualizarla y volver a agregarla:
self.listTableView.tableHeaderView = nil; [self.headerView removeFromSuperview];
fuente
Esto funcionó para mí en ios10 y Xcode 8
func layoutTableHeaderView() { guard let headerView = tableView.tableHeaderView else { return } headerView.translatesAutoresizingMaskIntoConstraints = false let headerWidth = headerView.bounds.size.width; let temporaryWidthConstraints = NSLayoutConstraint.constraintsWithVisualFormat("[headerView(width)]", options: NSLayoutFormatOptions(rawValue: UInt(0)), metrics: ["width": headerWidth], views: ["headerView": headerView]) headerView.addConstraints(temporaryWidthConstraints) headerView.setNeedsLayout() headerView.layoutIfNeeded() let headerSize = headerView.systemLayoutSizeFittingSize(UILayoutFittingCompressedSize) let height = headerSize.height var frame = headerView.frame frame.size.height = height headerView.frame = frame self.tableView.tableHeaderView = headerView headerView.removeConstraints(temporaryWidthConstraints) headerView.translatesAutoresizingMaskIntoConstraints = true }
fuente
Funciona tanto para la vista del encabezado como para el pie de página, simplemente reemplace el encabezado con el pie de página
func sizeHeaderToFit() { if let headerView = tableView.tableHeaderView { headerView.setNeedsLayout() headerView.layoutIfNeeded() let height = headerView.systemLayoutSizeFitting(UILayoutFittingCompressedSize).height var frame = headerView.frame frame.size.height = height headerView.frame = frame tableView.tableHeaderView = headerView } }
fuente
Para iOS 12 y superior, los siguientes pasos garantizarán que el diseño automático funcione correctamente tanto en la tabla como en el encabezado.
fuente
En mi caso
viewDidLayoutSubviews
funcionó mejor.viewWillLayoutSubviews
hacetableView
que aparezcan líneas blancas de a. También agregué verificar si miheaderView
objeto ya existe.- (void)viewDidLayoutSubviews { [super viewDidLayoutSubviews]; if ( ! self.userHeaderView ) { // Setup HeaderView self.userHeaderView = [[[NSBundle mainBundle] loadNibNamed:@"SSUserHeaderView" owner:self options:nil] objectAtIndex:0]; [self.userHeaderView setNeedsLayout]; [self.userHeaderView layoutIfNeeded]; CGFloat height = [self.userHeaderView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize].height; CGRect headerFrame = self.userHeaderView.frame; headerFrame.size.height = height; self.userHeaderView.frame = headerFrame; self.tableView.tableHeaderView = self.userHeaderView; // Update HeaderView with data [self.userHeaderView updateWithProfileData]; } }
fuente
Es muy posible utilizar un AutoLayout genérico basado
UIView
en cualquier estructura de subvista interna de AL como archivotableHeaderView
.¡Lo único que se necesita es establecer un
tableFooterView
antes simple !Sea
self.headerView
algo basado en restriccionesUIView
.- (void)viewDidLoad { ........................ self.tableView.tableFooterView = [UIView new]; [self.headerView layoutIfNeeded]; // to set initial size self.tableView.tableHeaderView = self.headerView; [self.tableView.leadingAnchor constraintEqualToAnchor:self.headerView.leadingAnchor].active = YES; [self.tableView.trailingAnchor constraintEqualToAnchor:self.headerView.trailingAnchor].active = YES; [self.tableView.topAnchor constraintEqualToAnchor:self.headerView.topAnchor].active = YES; // and the key constraint [self.tableFooterView.trailingAnchor constraintEqualToAnchor:self.headerView.trailingAnchor].active = YES; }
Si
self.headerView
cambia la altura bajo la rotación de la interfaz de usuario, se debe implementar- (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator { [super viewWillTransitionToSize:size withTransitionCoordinator:coordinator]; [coordinator animateAlongsideTransition: ^(id<UIViewControllerTransitionCoordinatorContext> context) { // needed to resize header height self.tableView.tableHeaderView = self.headerView; } completion: NULL]; }
Se puede usar la categoría ObjC para este propósito
@interface UITableView (AMHeaderView) - (void)am_insertHeaderView:(UIView *)headerView; @end @implementation UITableView (AMHeaderView) - (void)am_insertHeaderView:(UIView *)headerView { NSAssert(self.tableFooterView, @"Need to define tableFooterView first!"); [headerView layoutIfNeeded]; self.tableHeaderView = headerView; [self.leadingAnchor constraintEqualToAnchor:headerView.leadingAnchor].active = YES; [self.trailingAnchor constraintEqualToAnchor:headerView.trailingAnchor].active = YES; [self.topAnchor constraintEqualToAnchor:headerView.topAnchor].active = YES; [self.tableFooterView.trailingAnchor constraintEqualToAnchor:headerView.trailingAnchor].active = YES; } @end
Y también extensión Swift
extension UITableView { func am_insertHeaderView2(_ headerView: UIView) { assert(tableFooterView != nil, "Need to define tableFooterView first!") headerView.layoutIfNeeded() tableHeaderView = headerView leadingAnchor.constraint(equalTo: headerView.leadingAnchor).isActive = true trailingAnchor.constraint(equalTo: headerView.trailingAnchor).isActive = true topAnchor.constraint(equalTo: headerView.topAnchor).isActive = true tableFooterView?.trailingAnchor.constraint(equalTo: headerView.trailingAnchor).isActive = true } }
fuente
Esta solución me funciona perfectamente:
https://spin.atomicobject.com/2017/08/11/swift-extending-uitableviewcontroller/
Extiende el UITableViewController. Pero si solo está usando un UITableView, seguirá funcionando, simplemente extienda UITableView en lugar de UITableViewController. Llame a los métodos
sizeHeaderToFit()
osizeFooterToFit()
siempre que haya un evento que cambie latableViewHeader
altura.fuente
Copiado de esta publicación
override func viewDidLayoutSubviews() { super.viewDidLayoutSubviews() if let headerView = tableView.tableHeaderView { let height = headerView.systemLayoutSizeFitting(UIView.layoutFittingCompressedSize).height var headerFrame = headerView.frame //Comparison necessary to avoid infinite loop if height != headerFrame.size.height { headerFrame.size.height = height headerView.frame = headerFrame tableView.tableHeaderView = headerView } } }
fuente