Подтвердить что ты не робот

Как разместить UITableViewCell в нижней части экрана?

В UITableViewView есть от одного до трех UITableViewCell. Есть ли способ всегда позиционировать ячейки (я) внизу экрана после reloadData?

+----------------+     +----------------+     +----------------+
|                |     |                |     |                |
|                |     |                |     |                |
|                |     |                |     |                |
|                |     |                |     | +------------+ |
|                |     |                |     | |   cell 1   | |
|                |     |                |     | +------------+ |
|                |     | +------------+ |     | +------------+ |
|                |     | |   cell 1   | |     | |   cell 2   | |
|                |     | +------------+ |     | +------------+ |
| +------------+ |     | +------------+ |     | +------------+ |
| |   cell 1   | |     | |   cell 2   | |     | |   cell 3   | |
| +------------+ |     | +------------+ |     | +------------+ |
+----------------+     +----------------+     +----------------+
4b9b3361

Ответ 1

Я создаю новое примерное решение, поскольку предыдущий ответ не устарел для современного использования.

В последней технике используются ячейки автозапуска и саморазмера, поэтому предыдущий ответ больше не будет работать. Я переработал решение для работы с современными функциями и создал образец проекта для размещения GitHub.

Вместо подсчета высоты каждой строки, которая вызывает дополнительную работу, этот код вместо этого получает кадр для последней строки, чтобы можно было вычислить вставку содержимого сверху. Он использует то, что табличный вид уже делает, поэтому дополнительная работа не требуется.

Этот код также устанавливает только верхнюю вставку, если нижняя вставка установлена ​​для клавиатуры или другого наложения.

Сообщайте о любых ошибках или отправляйте улучшения в GitHub, и я обновлю этот образец.

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) {
    }];
}

Ответ 2

Вызов этого метода всякий раз, когда добавляется строка:

- (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);
}

Ответ 3

Вы можете установить заголовок в своем представлении таблицы и сделать его достаточно высоким, чтобы сдвинуть первую ячейку вниз. Затем соответствующим образом установите contentOffset вашего tableView. Я не думаю, что есть быстрый способ сделать это, хотя.

Ответ 4

Это можно сделать с помощью быстрого использования с помощью функции ниже

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 }
}

Ответ 5

Мне не нравились решения с пустой ячейкой, contentInset или transform, вместо этого я придумал другое решение:

Пример

UITableView макет является приватным и может быть изменен, если Apple пожелает, лучше иметь полный контроль, что делает ваш код более надежным и более гибким. Я переключился на UICollectionView и реализовал специальный макет на основе UICollectionViewFlowLayout для этого (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;
}

Это хорошо работает для моих случаев и, очевидно, может быть оптимизировано путем кэширования высоты содержимого. Кроме того, я не уверен, как это будет выполняться без оптимизации больших наборов данных, я не проверял это. Я собрал образец проекта с демо: MDBottomSnappingCells.

Вот 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;
}

Ответ 6

ИСПОЛЬЗУЙТЕ ЭТО. Конечно, это поможет.

- (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];
    }
}

Ответ 7

Вот версия Swift 3 принятого решения @Brennan, протестирована и одобрена:)

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;
   })

   } 

Ответ 8

if(indexPath.row!=CategoriesArray.count-1)  
{    
    cell.hidden = YES;
}

return cell;

Ответ 9

Я бы изменил размер и изменил положение UITableView в своем родительском представлении в зависимости от количества ячеек. Я предполагаю, что это решение, которое требует минимальных обходных решений. Кроме того, вам действительно нужно использовать UITableView?

Ответ 10

Добавьте пустую ячейку в новый раздел и сделайте ее секцией с нулевым индексом.

-(NSInteger) numberOfSectionsInTableView:(UITableView *)tableView
{
    return 2;
}


-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    if (section==0)
    {
        return 1;
    }
    return [self.yourArray count];
}

Теперь в

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;
}

Теперь

-(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;
}

Теперь вставьте это, когда вы перезагружаете tableView

[self.table reloadData];
[self.table scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:[self.yourArray count]-1 inSection:1] atScrollPosition:UITableViewScrollPositionBottom animated:YES];

Это сработало для меня. Надеюсь, это поможет.

Ответ 11

Элегантное и быстрое решение без строки кода.

Используйте представление контейнера и поместите UITableViewController в контейнер (embed segue).

Вы можете установить любую высоту, которая требуется для этого контейнера.

Ответ 12

Лучший способ сделать это, я нашел, - наблюдать размер содержимого таблицы и при необходимости настраивать вставку. Например:

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];
    }
}

Ответ 13

Все ответы получили некоторые причуды с динамическими rowHeight и/или анимациями. Для меня лучшим рабочим решением было преобразование таблицы (flipY):

tableView.transform = CGAffineTransform (scaleX: 1,y: -1)

внутри cellForRowAt:

cell.contentView.transform = CGAffineTransform (scaleX: 1,y: -1)
cell.accessoryView?.transform = CGAffineTransform (scaleX: 1,y: -1)

Вы также можете отменить свой массив данных и также перевернуть заголовок раздела/нижний колонтитул. Также ваш нижний колонтитул становится вашим новым заголовком, но эй, он работает.