26

a中有 1 到 3 个UITableViewCells UITableViewView。有没有办法在之后始终将单元格定位在屏幕底部reloadData

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

14 回答 14

13

我创建了一个新的示例解决方案,因为以前的答案对于现代使用来说并不过时。

最新技术使用自动布局和自动调整单元格,因此以前的答案将不再有效。我重新设计了解决方案以使用现代功能,并创建了一个示例项目以放在 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) {
    }];
}
于 2015-08-26T18:14:54.053 回答
10

每当添加一行时调用此方法:

- (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);
}
于 2014-05-28T09:53:25.657 回答
5

您可以在表格视图中设置标题并使其足够高以将第一个单元格向下推。然后相应地设置 tableView 的 contentOffset 。我不认为有一种快速的内置方法可以做到这一点。

于 2013-03-08T02:37:40.940 回答
3

我不喜欢空单元格contentInsettransform基于解决方案的解决方案,而是提出了其他解决方案:

例子

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;
}
于 2016-06-24T22:49:59.987 回答
2

这可以通过使用以下函数快速完成

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 }
}
于 2016-06-06T17:05:59.743 回答
2

这是@Brennan 接受的解决方案的 Swift 3 版本,经过测试和批准:)

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

   } 
于 2017-05-12T12:01:42.160 回答
1

用这个。这肯定会有所帮助。

- (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];
    }
}
于 2014-08-28T18:52:32.493 回答
0
if(indexPath.row!=CategoriesArray.count-1)  
{    
    cell.hidden = YES;
}

return cell;
于 2013-03-08T05:03:42.550 回答
0

我会根据单元格的数量在其父视图中调整 UITableView 的大小并重新定位。我想这是涉及最少变通办法的解决方案。另外,你真的需要使用 UITableView 吗?

于 2013-03-08T16:44:35.007 回答
0

在新部分中添加一个空白单元格,并将其设置为索引为零的部分。

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

它对我有用。希望这可以帮助。

于 2014-04-24T05:59:12.467 回答
0

无需一行代码的优雅快速的解决方案。

使用容器视图并将 UITableViewController 放入容器(嵌入 segue)。

您可以为此容器设置任何高度。

于 2014-11-20T18:11:01.560 回答
0

我发现这样做的最佳方法是观察 tableview 的内容大小并在必要时调整插图。例如:

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];
    }
}
于 2017-04-14T13:43:11.360 回答
0

所有答案都有一些动态 rowHeight 和/或动画的怪癖。对我来说,最好的工作解决方案是表格的转换(翻转):

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

里面cellForRowAt

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

您还可以反转您的数据数组,也可以翻转节页眉/页脚。您的页脚也将成为您的新页眉 - 但是,它有效。

于 2017-09-15T08:35:46.240 回答
0
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)
    }

}

我使用了@bkokot 解决方案,几乎没有添加。它做了两件事

1. Start showing cells from bottom of UITableView
2. Scroll cells to bottom so that last inserted row become visible
(just like chat)
于 2018-03-29T12:45:36.100 回答