ios - Cambiar el tamaño de UITableView para adaptarse al contenido
objective-c sizetofit (16)
Estoy creando una aplicación que tendrá una pregunta en UILabel
y una respuesta de opción múltiple en UITableView
, cada fila muestra una opción múltiple. Las preguntas y respuestas variarán, por lo que necesito que este UITableView
sea dinámico en altura.
Me gustaría encontrar un sizeToFit
para la mesa. Donde el marco de la tabla se establece a la altura de todo su contenido.
¿Alguien puede aconsejarme sobre cómo puedo lograr esto?
Agregue un observador para la propiedad contentSize en la vista de tabla y ajuste el tamaño del marco según corresponda
[your_tableview addObserver:self forKeyPath:@"contentSize" options:0 context:NULL];
luego en la devolución de llamada:
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
CGRect frame = your_tableview.frame;
frame.size = your_tableview.contentSize;
your_tableview.frame = frame;
}
Espero que esto te ayudará.
Como una extensión de la respuesta de Anooj VM, sugiero lo siguiente para actualizar el tamaño del contenido solo cuando cambie.
Este enfoque también deshabilita el desplazamiento correctamente y admite listas más grandes y rotación . No es necesario dispatch_async porque los cambios de contentSize se envían en el hilo principal.
- (void)viewDidLoad {
[super viewDidLoad];
[self.tableView addObserver:self forKeyPath:@"contentSize" options:NSKeyValueObservingOptionOld|NSKeyValueObservingOptionNew context:NULL];
}
- (void)resizeTableAccordingToContentSize:(CGSize)newContentSize {
CGRect superviewTableFrame = self.tableView.superview.bounds;
CGRect tableFrame = self.tableView.frame;
BOOL shouldScroll = newContentSize.height > superviewTableFrame.size.height;
tableFrame.size = shouldScroll ? superviewTableFrame.size : newContentSize;
[UIView animateWithDuration:0.3
delay:0
options:UIViewAnimationOptionCurveLinear
animations:^{
self.tableView.frame = tableFrame;
} completion: nil];
self.tableView.scrollEnabled = shouldScroll;
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context {
if ([change[NSKeyValueChangeKindKey] unsignedIntValue] == NSKeyValueChangeSetting &&
[keyPath isEqualToString:@"contentSize"] &&
!CGSizeEqualToSize([change[NSKeyValueChangeOldKey] CGSizeValue], [change[NSKeyValueChangeNewKey] CGSizeValue])) {
[self resizeTableAccordingToContentSize:[change[NSKeyValueChangeNewKey] CGSizeValue]];
}
}
- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation {
[super didRotateFromInterfaceOrientation:fromInterfaceOrientation];
[self resizeTableAccordingToContentSize:self.tableView.contentSize]; }
- (void)dealloc {
[self.tableView removeObserver:self forKeyPath:@"contentSize"];
}
En caso de que no quiera seguir los cambios en el tamaño del contenido de la vista de tabla, puede encontrar esta subclase útil.
protocol ContentFittingTableViewDelegate: UITableViewDelegate {
func tableViewDidUpdateContentSize(_ tableView: UITableView)
}
class ContentFittingTableView: UITableView {
override var contentSize: CGSize {
didSet {
if !constraints.isEmpty {
invalidateIntrinsicContentSize()
} else {
sizeToFit()
}
if contentSize != oldValue {
if let delegate = delegate as? ContentFittingTableViewDelegate {
delegate.tableViewDidUpdateContentSize(self)
}
}
}
}
override var intrinsicContentSize: CGSize {
return contentSize
}
override func sizeThatFits(_ size: CGSize) -> CGSize {
return contentSize
}
}
En caso de que su contentSize no sea correcto, esto se debe a que se basa en el valor estimado de RowHeight (automático), utilícelo antes
tableView.estimatedRowHeight = 0;
En realidad, encontré la respuesta yo mismo.
Solo creo un nuevo CGRect
para tableView.frame
con la height
de table.contentSize.height
Eso establece la altura de UITableView
a la height
de su contenido. Como el código modifica la interfaz de usuario, no olvide ejecutarlo en el hilo principal:
dispatch_async(dispatch_get_main_queue(), ^{
//This code will run in the main thread:
CGRect frame = self.tableView.frame;
frame.size.height = self.tableView.contentSize.height;
self.tableView.frame = frame;
});
Establecer propiedades de vista de tabla para
tableView.rowHeight = UITableViewAutomaticDimension
tableView.estimatedRowHeight = 100
Y en la celda de vista de tabla, configure la propiedad de su etiqueta numberOfLines = 0
establezca las restricciones superior, inferior, izquierda, derecha de su etiqueta en las restricciones superior, inferior, izquierda, derecha de la etiqueta ** Calculará la altura automáticamente
Hay una manera mucho mejor de hacerlo si usa AutoLayout: cambie la restricción que determina la altura. Simplemente calcule el alto de los contenidos de su tabla, luego encuentre la restricción y cámbiela. Aquí hay un ejemplo (suponiendo que la restricción que determina la altura de su tabla es en realidad una restricción de altura con la relación "Igual"):
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
for constraint in tableView.constraints {
if constraint.firstItem as? UITableView == tableView {
if constraint.firstAttribute == .height {
constraint.constant = tableView.contentSize.height
}
}
}
}
Intenté esto en iOS 7 y funcionó para mí
- (void)viewDidLoad
{
[super viewDidLoad];
[self.tableView sizeToFit];
}
Puedes probar esta vista AGTableView
personalizada
Para establecer una restricción de altura de la vista de tabla usando el guión gráfico o mediante programación. (Esta clase recupera automáticamente una restricción de altura y establece la altura de la vista del contenido en la altura de vista de la pantalla).
class AGTableView: UITableView {
fileprivate var heightConstraint: NSLayoutConstraint!
override init(frame: CGRect, style: UITableViewStyle) {
super.init(frame: frame, style: style)
self.associateConstraints()
}
required public init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
self.associateConstraints()
}
override open func layoutSubviews() {
super.layoutSubviews()
if self.heightConstraint != nil {
self.heightConstraint.constant = self.contentSize.height
}
else{
self.sizeToFit()
print("Set a heightConstraint to Resizing UITableView to fit content")
}
}
func associateConstraints() {
// iterate through height constraints and identify
for constraint: NSLayoutConstraint in constraints {
if constraint.firstAttribute == .height {
if constraint.relation == .equal {
heightConstraint = constraint
}
}
}
}
}
Nota Si hay algún problema para establecer una Altura, entonces yourTableView.layoutSubviews()
.
Tenía una vista de tabla dentro de la vista de desplazamiento y tuve que calcular la altura de tableView y cambiar su tamaño en consecuencia. Esos son los pasos que he tomado:
0) agregue un UIView a su scrollView (probablemente funcione sin este paso, pero lo hice para evitar cualquier posible conflicto) - esta será una vista de contenedor para su vista de tabla. Si sigue este paso, establezca las fronteras de las vistas a la derecha de las vistas de tabla.
1) crear una subclase de UITableView:
class IntrinsicTableView: UITableView {
override var contentSize:CGSize {
didSet {
self.invalidateIntrinsicContentSize()
}
}
override var intrinsicContentSize: CGSize {
self.layoutIfNeeded()
return CGSize(width: UIViewNoIntrinsicMetric, height: contentSize.height)
}
}
2) establecer la clase de una vista de tabla en Storyboard a IntrinsicTableView: captura de pantalla: http://joxi.ru/a2XEENpsyBWq0A
3) Establezca el heightConstraint en su vista de tabla
4) arrastre el IBoutlet de su tabla a su ViewController
5) arrastre el IBoutlet de la restricción de altura de su tabla a su ViewController
6) agregue este método en su ViewController:
override func viewWillLayoutSubviews() {
super.updateViewConstraints()
self.yourTableViewsHeightConstraint?.constant = self.yourTableView.intrinsicContentSize.height
}
Espero que esto ayude
objc versión de Musa almatri
(void)viewWillLayoutSubviews
{
[super updateViewConstraints];
CGFloat desiredHeight = self.tableView.contentSize.height;
// clamp desired height, if needed, and, in that case, leave scroll Enabled
self.tableHeight.constant = desiredHeight;
self.tableView.scrollEnabled = NO;
}
Solución Mu para esto en swift 3 : llame a este método en viewDidAppear
func UITableView_Auto_Height(_ t : UITableView)
{
var frame: CGRect = t.frame;
frame.size.height = t.contentSize.height;
t.frame = frame;
}
Solución Swift 4 sin KVO, DispatchQueue, estableciendo restricciones usted mismo. Esta solución se basa en la respuesta de Gulz.
1) Cree una subclase de UITableView:
class IntrinsicTableView: UITableView {
override var contentSize:CGSize {
didSet {
self.invalidateIntrinsicContentSize()
}
}
override var intrinsicContentSize: CGSize {
self.layoutIfNeeded()
return CGSize(width: UIViewNoIntrinsicMetric, height: contentSize.height)
}
}
2) Agregue un UITableView
a su diseño y establezca restricciones en todos los lados. Establezca la clase de este en IntrinsicTableView
.
3) Debería ver algunos errores, porque Storyboard no tiene en cuenta nuestra subclase '' intrinsicContentSize
''. Solucione esto abriendo el inspector de tamaño y reemplazando el intrinsicContentSize por un valor de marcador de posición. Esto es una anulación para el tiempo de diseño. En tiempo de ejecución usará la anulación en nuestra clase IntrinsicTableView
Solución Swift Siga estos pasos
1- Establece la restricción de altura para la tabla desde el guión gráfico.
2- Arrastre la restricción de altura desde el guión gráfico y cree @IBOutlet en el archivo viewController.
@IBOutlet weak var tableHeight: NSLayoutConstraint!
3- Luego puedes cambiar la altura de la tabla dinámicamente usando este código:
override func viewWillLayoutSubviews() {
super.updateViewConstraints()
self.tableHeight?.constant = self.table.contentSize.height
}
. .
Actualizar
Si se corta la última fila, intente llamar a viewWillLayoutSubviews () en la función willDisplay cell
func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
self.viewWillLayoutSubviews()
}
Swift 3, iOS 10.3
Solución 1: simplemente ponga self.tableview.sizeToFit()
en la función cellForRowAt indexPath
. Asegúrese de establecer la altura de la vista de tabla más alta que la que necesita. Esta es una buena solución si no tiene vistas debajo de la vista de tabla. Sin embargo, si lo tiene, la restricción de la tabla de abajo no se actualizará (no intenté solucionarlo porque se me ocurrió la solución 2)
Ejemplo:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if let cell = tableView.dequeueReusableCell(withIdentifier: "TestCell", for: indexPath) as? TestCell {
cell.configureCell(data: testArray[indexPath.row])
self.postsTableView.sizeToFit()
return cell
}
return UITableViewCell()
}
Solución 2: establezca la restricción de altura de la tabla en el guión gráfico y arrástrela al ViewController. Si conoce la altura promedio de su celda y sabe cuántos elementos contiene su matriz, puede hacer algo como esto:
tableViewHeightConstraint.constant = CGFloat(testArray.count) * 90.0 // Let''s say 90 is the average cell height
*EDITAR:
Después de todas las soluciones que probé y cada una de ellas solucionaba algo, pero no del todo, this es la respuesta que explica y soluciona este problema por completo.
La respuesta de Mimo y la respuesta de Anooj VM son impresionantes, pero hay un pequeño problema si tienes una lista grande, es posible que la altura del marco corte algunas de tus celdas.
Asi que. He modificado la respuesta un poco:
dispatch_async(dispatch_get_main_queue()) {
//This code will run in the main thread:
CGFloat newHeight=self.tableView.contentSize.height;
CGFloat screenHeightPermissible=(self.view.bounds.size.height-self.tableView.frame.origin.y);
if (newHeight>screenHeightPermissible)
{
//so that table view remains scrollable when ''newHeight'' exceeds the screen bounds
newHeight=screenHeightPermissible;
}
CGRect frame = self.tableView.frame;
frame.size.height = newHeight;
self.tableView.frame = frame;
}