ios - tutorial - ¿Cómo colocar UITableViewCell en la parte inferior de la pantalla?
uitableviewcell lifecycle (14)
Agregue una celda en blanco en una nueva sección y conviértala en la sección en el índice cero.
-(NSInteger) numberOfSectionsInTableView:(UITableView *)tableView
{
return 2;
}
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
if (section==0)
{
return 1;
}
return [self.yourArray count];
}
Ahora en
tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath //method add following in beginnig//
if (indexPath.section == 0 )
{
UITableViewCell * cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:nil];
// cell.backgroundColor = [UIColor clearColor];
return cell;
}
Ahora
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
if (indexPath.section == 0 )
{
if (self.yourArray.count>0)
{
CGFloat totalCellHeight = self.messages.count * yourCellHeight;
if (totalCellHeight>= self.table.bounds.size.height)
{
return 0;
}
return self.table.bounds.size.height - totalCellHeight;
}
else
return self.table.bounds.size.height;
}
return yourCellHeight;
}
Ahora pegue esto donde está recargando TableView
[self.table reloadData];
[self.table scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:[self.yourArray count]-1 inSection:1] atScrollPosition:UITableViewScrollPositionBottom animated:YES];
Funcionó para mí Espero que esto ayude.
Hay de una a tres UITableViewCell
en una UITableViewView
. ¿Hay alguna manera de colocar siempre la (s) celda (s) en la parte inferior de la pantalla después de reloadData
?
+----------------+ +----------------+ +----------------+
| | | | | |
| | | | | |
| | | | | |
| | | | | +------------+ |
| | | | | | cell 1 | |
| | | | | +------------+ |
| | | +------------+ | | +------------+ |
| | | | cell 1 | | | | cell 2 | |
| | | +------------+ | | +------------+ |
| +------------+ | | +------------+ | | +------------+ |
| | cell 1 | | | | cell 2 | | | | cell 3 | |
| +------------+ | | +------------+ | | +------------+ |
+----------------+ +----------------+ +----------------+
Aquí está la versión Swift 3 de la solución aceptada @Brennan, probada y aprobada :)
func updateContentInsetForTableView( tableView:UITableView,animated:Bool) {
let lastRow = tableView.numberOfRows(inSection: 0)
let lastIndex = lastRow > 0 ? lastRow - 1 : 0;
let lastIndexPath = IndexPath(row: lastIndex, section: 9)
let lastCellFrame = tableView.rectForRow(at: lastIndexPath)
let topInset = max(tableView.frame.height - lastCellFrame.origin.y - lastCellFrame.height, 0)
var contentInset = tableView.contentInset;
contentInset.top = topInset;
_ = UIViewAnimationOptions.beginFromCurrentState;
UIView.animate(withDuration: 0.1, animations: { () -> Void in
tableView.contentInset = contentInset;
})
}
Creé una nueva solución de muestra ya que la respuesta anterior no está desactualizada para un uso moderno.
La última técnica usa celdas de auto ajuste y autoevaluación para que la respuesta anterior ya no funcione. Repasé la solución para trabajar con las características modernas y creé un proyecto de muestra para poner en GitHub.
En lugar de contar la altura de cada fila, lo que provoca trabajo adicional, este código obtiene el marco de la última fila para que se pueda calcular el contenido de la parte superior. Aprovecha lo que ya está haciendo la vista de tabla, por lo que no es necesario ningún trabajo adicional.
Este código también solo establece el recuadro superior en caso de que el recuadro inferior esté establecido para el teclado u otra superposición.
Informe cualquier error o envíe mejoras a GitHub y actualizaré esta muestra.
GitHub: https://github.com/brennanMKE/BottomTable
- (void)updateContentInsetForTableView:(UITableView *)tableView animated:(BOOL)animated {
NSUInteger lastRow = [self tableView:tableView numberOfRowsInSection:0];
NSUInteger lastIndex = lastRow > 0 ? lastRow - 1 : 0;
NSIndexPath *lastIndexPath = [NSIndexPath indexPathForItem:lastIndex inSection:0];
CGRect lastCellFrame = [self.tableView rectForRowAtIndexPath:lastIndexPath];
// top inset = table view height - top position of last cell - last cell height
CGFloat topInset = MAX(CGRectGetHeight(self.tableView.frame) - lastCellFrame.origin.y - CGRectGetHeight(lastCellFrame), 0);
UIEdgeInsets contentInset = tableView.contentInset;
contentInset.top = topInset;
UIViewAnimationOptions options = UIViewAnimationOptionBeginFromCurrentState;
[UIView animateWithDuration:animated ? 0.25 : 0.0 delay:0.0 options:options animations:^{
tableView.contentInset = contentInset;
} completion:^(BOOL finished) {
}];
}
Esto se puede hacer de forma rápida utilizando el funcation siguiente
func updateContentInsetForTableView(tblView: UITableView, animated: Bool) {
let lastRow: NSInteger = self.tableView(tblView, numberOfRowsInSection: 0)
let lastIndex: NSInteger = lastRow > 0 ? lastRow - 1 : 0
let lastIndexPath: NSIndexPath = NSIndexPath(forRow: lastIndex, inSection: 0)
let lastCellFrame: CGRect = tblView.rectForRowAtIndexPath(lastIndexPath)
let topInset: CGFloat = max(CGRectGetHeight(tblView.frame) - lastCellFrame.origin.y - CGRectGetHeight(lastCellFrame), 0)
var contentInset: UIEdgeInsets = tblView.contentInset
contentInset.top = topInset
let option: UIViewAnimationOptions = UIViewAnimationOptions.BeginFromCurrentState
UIView.animateWithDuration(animated ? 0.25 : 0.0, delay: 0.0, options: option, animations: { () -> Void in
tblView.contentInset = contentInset
}) { (_) -> Void in }
}
La mejor manera de hacer esto que encontré es observar el tamaño del contenido de la tabla y ajustar el recuadro si es necesario. Por ejemplo:
static char kvoTableContentSizeContext = 0;
- (void) viewWillAppear:(BOOL)animated
{
[_tableView addObserver:self forKeyPath:@"contentSize" options:0 context:&kvoTableContentSizeContext];
}
- (void) viewWillDisappear:(BOOL)animated
{
[_tableView removeObserver:self forKeyPath:@"contentSize"];
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
if (context == &kvoTableContentSizeContext) {
CGFloat contentHeight = _tableView.contentSize.height;
CGFloat tableHeight = _tableView.frame.size.height;
if (contentHeight < tableHeight) {
UIEdgeInsets insets = _tableView.contentInset;
insets.top = tableHeight - contentHeight;
_tableView.contentInset = insets;
} else {
_tableView.contentInset = UIEdgeInsetsZero;
}
} else {
[super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
}
}
Llame a este método siempre que se agregue una fila:
- (void)updateContentInset {
NSInteger numRows=[self tableView:_tableView numberOfRowsInSection:0];
CGFloat contentInsetTop=_tableView.bounds.size.height;
for (int i=0;i<numRows;i++) {
contentInsetTop-=[self tableView:_tableView heightForRowAtIndexPath:[NSIndexPath indexPathForItem:i inSection:0]];
if (contentInsetTop<=0) {
contentInsetTop=0;
break;
}
}
_tableView.contentInset = UIEdgeInsetsMake(contentInsetTop, 0, 0, 0);
}
Me gustaría cambiar el tamaño y la posición de UITableView en su vista principal, dependiendo de la cantidad de celdas. Supongo que es la solución que implica soluciones temporales mínimas. Además, ¿realmente necesita usar un UITableView?
No me gustaban las soluciones basadas en células vacías, contentInset
o transform
, en cambio, se me ocurrió otra solución:
El diseño de UITableView
es privado y está sujeto a cambios si Apple lo desea, es mejor tener un control total para que su código sea a prueba del futuro y más flexible. UICollectionView
a UICollectionView
e implementé un diseño especial basado en UICollectionViewFlowLayout
para eso (Swift 3):
override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
// Do we need to stick cells to the bottom or not
var shiftDownNeeded = false
// Size of all cells without modifications
let allContentSize = super.collectionViewContentSize()
// If there are not enough cells to fill collection view vertically we shift them down
let diff = self.collectionView!.bounds.size.height - allContentSize.height
if Double(diff) > DBL_EPSILON {
shiftDownNeeded = true
}
// Ask for common attributes
let attributes = super.layoutAttributesForElements(in: rect)
if let attributes = attributes {
if shiftDownNeeded {
for element in attributes {
let frame = element.frame;
// shift all the cells down by the difference of heights
element.frame = frame.offsetBy(dx: 0, dy: diff);
}
}
}
return attributes;
}
Funciona bastante bien para mis casos y, obviamente, puede optimizarse de alguna manera almacenando en caché la altura del tamaño del contenido. Además, no estoy seguro de cómo funcionará sin optimizaciones en grandes conjuntos de datos, no lo probé. He creado un proyecto de ejemplo con demostración: MDBottomSnappingCells .
Aquí está la versión de Objective-C:
- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect;
{
// Do we need to stick cells to the bottom or not
BOOL shiftDownNeeded = NO;
// Size of all cells without modifications
CGSize allContentSize = [super collectionViewContentSize];
// If there are not enough cells to fill collection view vertically we shift them down
CGFloat diff = self.collectionView.bounds.size.height - allContentSize.height;
if(diff > DBL_EPSILON)
{
shiftDownNeeded = YES;
}
// Ask for common attributes
NSArray *attributes = [super layoutAttributesForElementsInRect:rect];
if(shiftDownNeeded)
{
for(UICollectionViewLayoutAttributes *element in attributes)
{
CGRect frame = element.frame;
// shift all the cells down by the difference of heights
element.frame = CGRectOffset(frame, 0, diff);
}
}
return attributes;
}
Puede establecer un encabezado en su vista de tabla y hacerlo lo suficientemente alto como para empujar la primera celda hacia abajo. Luego configure el contentOffset de su tableView en consecuencia. Sin embargo, no creo que haya una manera rápida de hacerlo.
Solución elegante y rápida sin una línea de código.
Utilice la vista de contenedor y coloque UITableViewController en el contenedor (inserción de segue).
Puede establecer la altura que desee para este contenedor.
Todas las respuestas tienen algunos caprichos con rowHeight dinámico y / o animaciones. Para mí, la mejor solución de trabajo fue una transformación de la tabla (flipY):
tableView.transform = CGAffineTransform (scaleX: 1,y: -1)
dentro de cellForRowAt
:
cell.contentView.transform = CGAffineTransform (scaleX: 1,y: -1)
cell.accessoryView?.transform = CGAffineTransform (scaleX: 1,y: -1)
También puede invertir su conjunto de datos y también puede cambiar el encabezado de sección / pie de página. También su pie de página se convierte en su nuevo encabezado, pero bueno, funciona.
UTILIZAR ESTA. Seguramente esto ayudará.
- (void)reloadData
{
[super reloadData];
[self recalculateContentInset];
[self recalculateScrollIndicator];
}
- (void)recalculateContentInset
{
CGFloat contentInsetHeight = MAX(self.frame.size.height - self.contentSize.height, 0);
CGFloat duration = 0.0;
[UIView animateWithDuration:duration
delay:0
options:UIViewAnimationOptionCurveEaseOut
animations:^{
[self setContentInset:UIEdgeInsetsMake(contentInsetHeight, 0, 0, 0)];
}completion:nil];
}
- (void)recalculateScrollIndicator
{
if(self.contentSize.height >= self.frame.size.height){
[self setShowsVerticalScrollIndicator:YES];
} else {
[self setShowsVerticalScrollIndicator:NO];
}
}
func updateContentInsetForTableView( tableView:UITableView,animated:Bool) {
let lastRow = tableView.numberOfRows(inSection: 0)
let lastIndex = lastRow > 0 ? lastRow - 1 : 0;
let lastIndexPath = IndexPath(row: lastIndex, section: 9)
let lastCellFrame = tableView.rectForRow(at: lastIndexPath)
let topInset = max(tableView.frame.height - lastCellFrame.origin.y - lastCellFrame.height, 0)
var contentInset = tableView.contentInset;
contentInset.top = topInset;
_ = UIViewAnimationOptions.beginFromCurrentState;
UIView.animate(withDuration: 0.1, animations: { () -> Void in
tableView.contentInset = contentInset;
})
if self.commesnts.count > 0 {
tableView.scrollToRow(at: IndexPath(item:self.commesnts.count-1, section: 0), at: .bottom, animated: true)
}
}
Usé la solución @bkokot con poca adición. Hace dos cosas
1. Start showing cells from bottom of UITableView
2. Scroll cells to bottom so that last inserted row become visible
(just like chat)
if(indexPath.row!=CategoriesArray.count-1)
{
cell.hidden = YES;
}
return cell;