如何将 UITableViewCell 对齐到 UITableView 的底部?

Posted

技术标签:

【中文标题】如何将 UITableViewCell 对齐到 UITableView 的底部?【英文标题】:How to align UITableViewCell to the bottom of the UITableView? 【发布时间】:2015-05-02 00:33:33 【问题描述】:

当您使用insertRowsAtIndexPaths:withRowAnimation: 插入第一个UITableViewCell 时,它通常出现在UITableView 的顶部。在Periscope app 中,情况正好相反——第一个插入的单元格底部对齐。随着新单元格被推入,旧单元格在表格中向上移动。这是如何实现的?

【问题讨论】:

1) 你确定这是UITableViewUITableViewCells 吗? 2) 假设他们是,您确定第一个屏幕截图中UITableViewtop 与第二个屏幕截图中UITableViewtop 位置相同吗? @nhgrif 我不确定他们是如何实现的。我知道如何完全不使用表格来实现这种效果,但是使用标准表格框架将是理想的,因为它可以为您处理单元内存管理和动画。至于两张照片中的 frame.origin.y 相同,在注入新单元格的过程中很难协调整个表格向上滑动,因为动画持续时间和单元格注入曲线无法公开访问。跨度> 我没说这很容易。 我想知道是否可以通过从足够的空单元格开始填充屏幕并在添加新单元格时保持表格视图滚动到底部来完成。添加单元格时你会看到什么动画? @rdlmar 这是个好主意。只需在顶部放置一个大空白单元格。动画效果可以在their website看到(他们有视频演示)。 【参考方案1】:

如果您对我在 Periscope ios 应用程序中的操作方式感兴趣,它实际上非常简单...

TL;DR;添加一个高度等于表格视图框架高度的透明表格标题标题视图。然后,当您向表格添加单元格时,只需为表格视图的内容偏移设置动画即可。

给你的表格视图一个标题视图:

- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section

    UIView *headerView = [[UIView alloc] initWithFrame:CGRectZero];
    headerView.userInteractionEnabled = NO; // all touches within this space must go through to the video layer

    return headerView;   // empty header above chat, so messages flow in from bottom


- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section

    return self.tableView.frame.size.height;            // empty header above chat, so messages flow in from bottom

向表中添加数据(在我的例子中,消息被添加到名为 _messages 的数组中。然后我在 UITableViewController 上调用 reloadData)。然后调用此方法为单元格设置动画:

- (void)scrollTableToBottom

    if (!self.isViewLoaded || _messages.count == 0)
        return;

    CGFloat offsetY = self.tableView.contentSize.height - self.tableView.frame.size.height + self.tableView.contentInset.bottom;

    [UIView animateWithDuration:0.33
            delay:0
            options:UIViewAnimationOptionCurveEaseOut | UIViewAnimationOptionAllowUserInteraction
            animations:^
                [self.tableView setContentOffset:CGPointMake(0, offsetY) animated:NO];
            
            completion:nil];

希望对您有所帮助。我发现这是一种非常便宜/简单的模拟固定在底部的细胞的方法。我知道有些人提到把桌子倒过来,但这对我来说似乎很疯狂。 :-)

【讨论】:

您好@Aaron Wasserman,感谢您发布您的代码。你能推荐一些淡出动画的方法吗?我尝试了很多解决方案,无法获得所需的结果。 我的每个单元格都有一个视图模型,用于跟踪它出现在屏幕上的时间以及它开始淡出的时间。然后,一旦一个单元格被重用并设置了它的新视图模型,您就可以计算从哪里开始动画。诀窍是使用 CAAnimations,它可以很容易地从图层中应用或删除。 感谢分享! @AaronWasserman,我将 tableview 和 tableview 单元格转换为 180 度。 tableView.transform = CGAffineTransformMakeRotation(-M_PI); cell.transform = CGAffineTransformMakeRotation(M_PI);我尝试删除rowatindexpaths,但它看起来不像潜望镜那么好,所以你能告诉我如何删除具有淡出效果的旧cmets? @AaronWasserman 感谢您发布此信息。使用自动调整单元格时,我有一些奇怪的滚动行为。您能否解释一下您是如何配置它来处理动态高度单元的?谢谢。【参考方案2】:

Swift 4 版本的 Aaron Wasserman 答案。

func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? 
    let headerView = UIView(frame: .zero)
    headerView.isUserInteractionEnabled = false
    return headerView


// Added logic to avoid blank space at the top
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat 
    return tableView.frame.size.height - CGFloat(messages.count * cellHeight)


 func scrollToBottom(animated: Bool) 
    if isViewLoaded && messages.count > 0 

        let offset = tableView.contentSize.height - tableView.frame.size.height + tableView.contentInset.bottom

        UIView.animate(withDuration: 0.33, delay: 0, options: [.curveEaseOut, .allowUserInteraction], animations: 
            self.tableView.setContentOffset(CGPoint(x: 0, y: offset), animated: animated)
        , completion: nil)
    

scrollToBottom方法需要在tableView.reloadData()之后调用

【讨论】:

【参考方案3】:

我对 Mr.Wasserman 答案的修改

extension MyViewController : UITableViewDelegate 

  func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat 
      return UITableView.automaticDimension
  

  func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? 
      let headerView = UIView(frame: .zero)
      headerView.isUserInteractionEnabled = false
      return headerView
  

  func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat 
      let diff = tableView.contentSize.height - tableView.bounds.height
      return diff > 0 ? 0 : -diff
  


【讨论】:

【参考方案4】:

你可以使用insertRowsAtIndexPath方法,然后让tableview滚动到底部。

[self.tableView insertRowsAtIndexPaths:@[[NSIndexPath indexPathForRow:self.posts.count-1 inSection:0]] withRowAnimation:UITableViewRowAnimationBottom];
[self.tableView scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionBottom animated:YES];

如果您希望在 tableview 的顶部有那个巨大的间隙,您可以设置滚动偏移,并在新项目进入时调整它。

另一种方法是将 tableview 倒置,然后将每一行倒置。

【讨论】:

【参考方案5】:

参加聚会有点晚了,但这是另一种方法。 无需添加任意标题视图即可完成。每当内容大小发生变化时调整内容偏移量。

class BottomEndianTableView: UITableView 

    private var observer: Any?

    override init(frame: CGRect, style: UITableView.Style) 
        super.init(frame: frame, style: style)
        commonInit()
    

    required init?(coder: NSCoder) 
        super.init(coder: coder)
        commonInit()
    

    private func commonInit() 
        observer = observe(\.contentSize)  _, _ in
            DispatchQueue.main.async  [weak self] in
                self?.scrollToEnd(animated: false)
            
        
    

    func scrollToEnd(animated: Bool) 
        let scrollDistance = contentSize.height - frame.height
        setContentOffset(CGPoint(x: 0, y: scrollDistance), animated: animated)
    


您也可以拨打scrollToEnd(animated: true)响应键盘显示

【讨论】:

以上是关于如何将 UITableViewCell 对齐到 UITableView 的底部?的主要内容,如果未能解决你的问题,请参考以下文章

如何使 UITableViewCell 居中,约束无法对齐

如何正确地将静态“正确细节”UITableViewCell 内容与图像对齐?

如何使用UITextAlignment将UITableViewCell中的文本与Swift中的左右对齐

StoryBoard 中的自定义 UITableViewCell AutoLayout 右对齐

如何在 UITableViewCell 中左对齐或右对齐 UILabel

如何添加约束以右对齐 UITableViewCell 内的图像