objective-c macos cocoa nstableview autolayout

objective c - Ajustar automáticamente la altura de un NSTableView



objective-c macos (5)

Esta respuesta es para Swift 4, dirigida a macOS 10.10 y posteriores:

1. respuesta

Puede usar el fittingSize de tamaño de la vista de fittingSize para calcular el tamaño de su fittingSize .

tableView.needsLayout = true tableView.layoutSubtreeIfNeeded() let height = tableView.fittingSize.height

2. Responder

Entiendo que desea mover ese código fuera del controlador de vista, pero como la vista de la tabla en sí no sabe nada sobre el número de elementos (solo a través de la delegación) o los cambios de modelo, lo pondría en el controlador de vista. Desde macOS 10.10, puede utilizar preferredContentSize en su NSViewController dentro de una NSViewController para establecer el tamaño.

func updatePreferredContentSize() { tableView.needsLayout = true tableView.layoutSubtreeIfNeeded() let height = tableView.fittingSize.height let width: CGFloat = 320 preferredContentSize = CGSize(width: width, height: height) }

En mi ejemplo, estoy usando un ancho fijo pero también podrías usar el calculado (aún no lo he probado).

Desearía llamar al método de actualización siempre que cambie su origen de datos y / o cuando esté a punto de mostrar la ventana emergente.

¡Espero que esto resuelva tu problema!

Ya hice esta pregunta una vez, pero no estoy muy satisfecho con la solución.

Ajustar automáticamente el tamaño de NSTableView

Quiero mostrar un NSTableView en un NSPopover , o en un NSWindow .
Ahora, el tamaño de la ventana debe ajustarse con la vista de tabla.

Al igual que Xcode lo hace:

Esto es bastante simple con Auto Layout, solo puede fijar la vista interior a la vista superior.

Mi problema es que no puedo calcular la altura óptima de la vista de tabla. El siguiente código enumera todas las filas disponibles, pero no devuelve el valor correcto, porque la vista de tabla tiene otros elementos como separadores y la cabecera de la tabla.

- (CGFloat)heightOfAllRows:(NSTableView *)tableView { CGFloat __block height; [tableView enumerateAvailableRowViewsUsingBlock:^(NSTableRowView *rowView, NSInteger row) { // tried it with this one height += rowView.frame.size.height; // and this one // height += [self tableView:nil heightOfRow:row]; }]; return height; }

1 pregunta

¿Cómo puedo arreglar esto? ¿Cómo puedo calcular correctamente la altura requerida de la vista de tabla?

2. Pregunta

¿Dónde debo ejecutar este código?
No quiero implementar esto en un controlador, porque definitivamente es algo que la vista de tabla debería manejar por sí misma.
Y ni siquiera encontré ningún método de delegación útil.

Así que pensé que lo mejor sería si pudieras subclasificar NSTableView .
Así que mi pregunta 2, ¿dónde implementarla?

Motivación

Definitivamente vale la pena una recompensa


Interface Builder en Xcode coloca automáticamente el NSTableView en un NSScrollView. El NSScrollView es donde realmente se encuentran los encabezados. Cree un NSScrollView como su vista base en la ventana y agregue el NSTableView a él:

NSScrollView * scrollView = [[NSScrollView alloc]init]; [scrollView setHasVerticalScroller:YES]; [scrollView setHasHorizontalScroller:YES]; [scrollView setAutohidesScrollers:YES]; [scrollView setBorderType:NSBezelBorder]; [scrollView setTranslatesAutoresizingMaskIntoConstraints:NO]; NSTableView * table = [[NSTableView alloc] init]; [table setDataSource:self]; [table setColumnAutoresizingStyle:NSTableViewUniformColumnAutoresizingStyle]; [scrollView setDocumentView:table]; //SetWindow''s main view to scrollView

Ahora puede interrogar el contentView del scrollView para encontrar el tamaño del tamaño NSScrollView

NSRect rectOfFullTable = [[scrollView contentView] documentRect];

Debido a que el NSTableView está dentro de un NSScrollView, el NSTableView tendrá un headerView que puede usar para encontrar el tamaño de sus encabezados.

Podría crear una subclase de NSScrollView para actualizar su supervisión cuando el tamaño de la tabla cambie (encabezados + filas) al reemplazar el método reflectScrolledClipView:


No estoy seguro de si mi solución es mejor que la que usted tiene, pero pensé que la ofrecería de todos modos. Lo uso con una vista de impresión. No estoy usando Auto Layout. Solo funciona con enlaces; se necesitaría un ajuste para trabajar con una fuente de datos.

Verás que hay un truco terrible para hacer que funcione: solo agrego 0.5 al valor que calculo cuidadosamente.

Esto tiene en cuenta el espaciado pero no los encabezados, que no muestro. Si está mostrando los encabezados, puede agregar eso en el -tableView:heightOfRow: .

En la subclase o categoría de NSTableView :

- (void) sizeHeightToFit { CGFloat height = 0.f; if ([self.delegate respondsToSelector:@selector(tableView:heightOfRow:)]) { for (NSInteger i = 0; i < self.numberOfRows; ++i) height = height + [self.delegate tableView:self heightOfRow:i] + self.intercellSpacing.height; } else { height = (self.rowHeight + self.intercellSpacing.height) * self.numberOfRows; } NSSize frameSize = self.frame.size; frameSize.height = height; [self setFrameSize:frameSize]; }

En delegado de vista de tabla:

// Invoke bindings to get the cell contents // FIXME If no bindings, use the datasource - (NSString *) stringValueForRow:(NSInteger) row column:(NSTableColumn *) column { NSDictionary *bindingInfo = [column infoForBinding:NSValueBinding]; id object = [bindingInfo objectForKey:NSObservedObjectKey]; NSString *keyPath = [bindingInfo objectForKey:NSObservedKeyPathKey]; id value = [[object valueForKeyPath:keyPath] objectAtIndex:row]; if ([value isKindOfClass:[NSString class]]) return value; else return @""; } - (CGFloat) tableView:(NSTableView *) tableView heightOfRow:(NSInteger) row { CGFloat result = tableView.rowHeight; for (NSTableColumn *column in tableView.tableColumns) { NSTextFieldCell *dataCell = column.dataCell; if (![dataCell isKindOfClass:[NSTextFieldCell class]]) continue; // Borrow the prototype cell, and set its text [dataCell setObjectValue:[self stringValueForRow:row column:column]]; // Ask it the bounds for a rectangle as wide as the column NSRect cellBounds = NSZeroRect; cellBounds.size.width = [column width]; cellBounds.size.height = FLT_MAX; NSSize cellSize = [dataCell cellSizeForBounds:cellBounds]; // This is a HACK to make this work. // Otherwise the rows are inexplicably too short. cellSize.height = cellSize.height + 0.5; if (cellSize.height > result) result = cellSize.height; } return result; }


Puede consultar el marco de la última fila para obtener la altura de la vista de tabla:

- (CGFloat)heightOfTableView:(NSTableView *)tableView { NSInteger rows = [self numberOfRowsInTableView:tableView]; if ( rows == 0 ) { return 0; } else { return NSMaxY( [tableView rectOfRow:rows - 1] ); } }

¡Esto supone una vista de desplazamiento adjunta sin bordes!

Puede consultar el tableView.enclosingScrollView.borderType para verificar si la vista de desplazamiento está acotada o no. Si es así, el ancho del borde debe agregarse al resultado (dos veces; abajo y arriba). Desafortunadamente, no sé de la parte superior de mi cabeza cómo obtener el ancho del borde.

La ventaja de consultar rectOfRow: es que funciona en la misma iteración de runloop como [tableView reloadData]; . En mi experiencia, consultar el marco de la vista de tabla no funciona de manera confiable cuando se hace un reloadData primero (obtendrás la altura previa).


Solo obtenga -[NSTableView frame] NSTableView está incrustado en NSScrollView, pero tiene el tamaño completo.