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.