如何在视图出现之前滚动到iPhone上UITableView的底部

时间:2010-05-05 01:58:49

标签: ios objective-c cocoa-touch uitableview

我有一个UITableView,其中填充了可变高度的单元格。当视图被推入视图时,我希望表格滚动到底部。

我目前有以下功能

NSIndexPath *indexPath = [NSIndexPath indexPathForRow:[log count]-1 inSection:0];
[self.table scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionBottom animated:NO];

log是一个可变数组,包含构成每个单元格内容的对象。

上面的代码在viewDidAppear中工作正常,但是当视图首次出现然后跳到底部时,显示表顶部会产生不幸的副作用。如果table view可以在它出现之前滚动到底部,我会更喜欢它。

我尝试了viewWillAppearviewDidLoad中的滚动,但在这两种情况下,数据尚未加载到表中,并且都抛出异常。

任何指导都会非常感激,即使只是告诉我我所拥有的一切都是可能的。

34 个答案:

答案 0 :(得分:144)

我相信拨打[table setContentOffset:CGPointMake(0, CGFLOAT_MAX)]会做你想做的事。

答案 1 :(得分:120)

Jacob's answer开始,这是代码:

- (void) viewDidAppear:(BOOL)animated
{
    [super viewDidAppear:animated];

    if (self.messagesTableView.contentSize.height > self.messagesTableView.frame.size.height) 
    {
        CGPoint offset = CGPointMake(0, self.messagesTableView.contentSize.height - self.messagesTableView.frame.size.height);
        [self.messagesTableView setContentOffset:offset animated:YES];
    }
}

答案 2 :(得分:119)

我认为最简单的方法就是:

if (self.messages.count > 0)
{
    [self.tableView 
        scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:self.messages.count-1 
        inSection:0] 
        atScrollPosition:UITableViewScrollPositionBottom animated:YES];
}

Swift 3版本:

if messages.count > 0 {
    userDefinedOptionsTableView.scrollToRow(at: IndexPath(item:messages.count-1, section: 0), at: .bottom, animated: true)
}

答案 3 :(得分:41)

如果您需要滚动到内容的EXACT端,您可以这样做:

- (void)scrollToBottom
{
    CGFloat yOffset = 0;

    if (self.tableView.contentSize.height > self.tableView.bounds.size.height) {
        yOffset = self.tableView.contentSize.height - self.tableView.bounds.size.height;
    }

    [self.tableView setContentOffset:CGPointMake(0, yOffset) animated:NO];
}

答案 4 :(得分:31)

我正在使用autolayout,但没有一个答案适合我。这是我最终解决的解决方案:

@property (nonatomic, assign) BOOL shouldScrollToLastRow;


- (void)viewDidLoad {
    [super viewDidLoad];

    _shouldScrollToLastRow = YES;
}


- (void)viewDidLayoutSubviews {
    [super viewDidLayoutSubviews];

    // Scroll table view to the last row
    if (_shouldScrollToLastRow)
    {
        _shouldScrollToLastRow = NO;
        [self.tableView setContentOffset:CGPointMake(0, CGFLOAT_MAX)];
    }
}

答案 5 :(得分:23)

在使用自动布局的iOS 7.0中,accepted solution by @JacobRelkin对我无效。

我有UIViewController的自定义子类,并添加了一个实例变量_tableView作为其view的子视图。我使用自动布局定位_tableView。我尝试在viewDidLoad结束时甚至在viewWillAppear:中调用此方法。都没有奏效。

因此,我将以下方法添加到UIViewController的自定义子类中。

- (void)tableViewScrollToBottomAnimated:(BOOL)animated {
    NSInteger numberOfRows = [_tableView numberOfRowsInSection:0];
    if (numberOfRows) {
        [_tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:numberOfRows-1 inSection:0] atScrollPosition:UITableViewScrollPositionBottom animated:animated];
    }
}

[self tableViewScrollToBottomAnimated:NO]结束时致电viewDidLoad。不幸的是,它还会导致tableView:heightForRowAtIndexPath:为每个单元格调用三次。

答案 6 :(得分:22)

这是我在Swift 2.0中实现的扩展。应在加载tableview后调用这些函数:

import UIKit

extension UITableView {
    func setOffsetToBottom(animated: Bool) {
        self.setContentOffset(CGPointMake(0, self.contentSize.height - self.frame.size.height), animated: true)
    }

    func scrollToLastRow(animated: Bool) {
        if self.numberOfRowsInSection(0) > 0 {
            self.scrollToRowAtIndexPath(NSIndexPath(forRow: self.numberOfRowsInSection(0) - 1, inSection: 0), atScrollPosition: .Bottom, animated: animated)
        }
    }
}

答案 7 :(得分:15)

详细

  • Xcode 8.3.2,swift 3.1
  • Xcode 10.2(10E125),Swift 5

代码

import UIKit

extension UITableView {
    func scrollToBottom(animated: Bool) {
        let y = contentSize.height - frame.size.height
        if y < 0 { return }
        setContentOffset(CGPoint(x: 0, y: y), animated: animated)
    }
}

用法

tableView.scrollToBottom(animated: true)

完整样本

  

不要忘记粘贴解决方案代码!

import UIKit

class ViewController: UIViewController {

    private weak var tableView: UITableView?
    private lazy var cellReuseIdentifier = "CellReuseIdentifier"

    override func viewDidLoad() {
        super.viewDidLoad()
        let tableView = UITableView(frame: view.frame)
        view.addSubview(tableView)
        tableView.register(UITableViewCell.self, forCellReuseIdentifier: cellReuseIdentifier)
        self.tableView = tableView
        tableView.dataSource = self
        tableView.performBatchUpdates(nil) { [weak self] result in
            if result { self?.tableView?.scrollToBottom(animated: true) }
        }
    }
}

extension ViewController: UITableViewDataSource {

    func numberOfSections(in tableView: UITableView) -> Int {
        return 1
    }

    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return 100
    }

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: cellReuseIdentifier, for: indexPath )
        cell.textLabel?.text = "\(indexPath)"
        return cell
    }
}

答案 8 :(得分:11)

实际上,在Swift中使用“Swifter”的方法是:

var lastIndex = NSIndexPath(forRow: self.messages.count - 1, inSection: 0)
            self.messageTableView.scrollToRowAtIndexPath(lastIndex, atScrollPosition: UITableViewScrollPosition.Bottom, animated: true)

工作对我来说很完美。

答案 9 :(得分:9)

我希望表格加载到框架中显示的表格的末尾。我发现使用

NSIndexPath *scrollIndexPath = [NSIndexPath indexPathForRow:([self.tableView numberOfRowsInSection:0] - 1) inSection:0];
[[self tableView] scrollToRowAtIndexPath:scrollIndexPath atScrollPosition:UITableViewScrollPositionBottom animated:NO];

无法正常工作,因为当桌面高度小于框架高度时,它会出错。请注意,我的表只有一个部分。

对我有用的解决方案是在viewWillAppear中实现以下代码:

- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
// on the initial cell load scroll to the last row (ie the latest Note)
if (initialLoad==TRUE) {
    initialLoad=FALSE; 
    NSIndexPath *scrollIndexPath = [NSIndexPath indexPathForRow:([self.tableView numberOfRowsInSection:0] - 1) inSection:0];
    [[self tableView] scrollToRowAtIndexPath:scrollIndexPath atScrollPosition:UITableViewScrollPositionBottom animated:NO];
        CGPoint offset = CGPointMake(0, (1000000.0));
        [self.tableView setContentOffset:offset animated:NO];
    }
}

BOOL ivar initialLoad在viewDidLoad中设置为TRUE。

答案 10 :(得分:8)

对于Swift:

if tableView.contentSize.height > tableView.frame.size.height
        {
            let offset = CGPoint(x: 0, y: tableView.contentSize.height - tableView.frame.size.height)
            tableView.setContentOffset(offset, animated: false)
        }

答案 11 :(得分:5)

您应该使用UITableViewScrollPositionBottom代替。

答案 12 :(得分:5)

对于Swift 3(Xcode 8.1):

override func viewDidAppear(_ animated: Bool) {
    let numberOfSections = self.tableView.numberOfSections
    let numberOfRows = self.tableView.numberOfRows(inSection: numberOfSections-1)

    let indexPath = IndexPath(row: numberOfRows-1 , section: numberOfSections-1)
    self.tableView.scrollToRow(at: indexPath, at: UITableViewScrollPosition.middle, animated: true)
}

答案 13 :(得分:4)

当然是一个Bug。 可能在代码中的某处使用table.estimatedRowHeight = value(例如100)。 将此值替换为您认为行高可以达到的最高值,例如500 .. 这应该结合以下代码解决问题:

//auto scroll down example
let delay = 0.1 * Double(NSEC_PER_SEC)
let time = dispatch_time(DISPATCH_TIME_NOW, Int64(delay))

dispatch_after(time, dispatch_get_main_queue(), {
    self.table.scrollToRowAtIndexPath(NSIndexPath(forRow: self.Messages.count - 1, inSection: 0), atScrollPosition: UITableViewScrollPosition.Bottom, animated: false)
})

答案 14 :(得分:3)

经过大量的摆弄后,这对我有用:

var viewHasAppeared = false

override func viewDidLayoutSubviews() {
    super.viewDidLayoutSubviews()
    if !viewHasAppeared { goToBottom() }
}

override func viewDidAppear(animated: Bool) {
    super.viewDidAppear(animated)
    viewHasAppeared = true
}

private func goToBottom() {
    guard data.count > 0 else { return }
    let indexPath = NSIndexPath(forRow: data.count - 1, inSection: 0)
    tableView.scrollToRowAtIndexPath(indexPath, atScrollPosition: .Bottom, animated: false)
    tableView.layoutIfNeeded()
}

根据某些人的建议,密钥结果是{em>不包裹scrollToRowAtIndexPath dispatch_async,而只是通过调用layoutIfNeeded跟随它。< / p>

我对此的理解是,在当前线程中调用scroll方法可以保证在显示视图之前立即设置滚动偏移量。当我调度到主线程时,视图在滚动生效之前立即显示。

(另外请注意,您需要viewHasAppeared标记,因为每次调用goToBottom时都不希望viewDidLayoutSubviews。只要方向发生变化,就会调用它。)

答案 15 :(得分:3)

在Swift 3.0中

self.tableViewFeeds.setContentOffset(CGPoint(x: 0, y: CGFLOAT_MAX), animated: true)

答案 16 :(得分:3)

使用上述解决方案,这将滚动到表格的底部(仅当首先加载表格内容时):

//Scroll to bottom of table
CGSize tableSize = myTableView.contentSize;
[myTableView setContentOffset:CGPointMake(0, tableSize.height)];

答案 17 :(得分:2)

使用此简单代码滚动tableView底部

NSInteger rows = [tableName numberOfRowsInSection:0];
            if(rows > 0) {

                [tableName scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:rows-1 inSection:0]
                                 atScrollPosition:UITableViewScrollPositionBottom
                                         animated:YES];
            }

答案 18 :(得分:2)

如果你必须在向下滚动之前异步加载数据,这里有可能的解决方案:

tableView.alpha = 0 // We want animation!
lastMessageShown = false // This is ivar

viewModel.fetch { [unowned self] result in
    self.tableView.reloadData()

    if !self.lastMessageShown {
        dispatch_async(dispatch_get_main_queue()) { [unowned self] in
            if self.rowCount > 0 {
                self.tableView.scrollToRowAtIndexPath(NSIndexPath(forRow: self.rowCount, inSection: 0), atScrollPosition: .Bottom, animated: false)
            }

            UIView.animateWithDuration(0.1) {
                self.tableView.alpha = 1
                self.lastMessageShown = true // Do it once
            }
        }
    }
}

答案 19 :(得分:2)

感谢Jacob的回答。如果有人对monotouch c#version

感兴趣,真有帮助
private void SetScrollPositionDown()
    {
        if (tblShoppingListItem.ContentSize.Height > tblShoppingListItem.Frame.Size.Height)
        {
            PointF offset = new PointF(0,
                                       tblShoppingListItem.ContentSize.Height -
                                       tblShoppingListItem.Frame.Size.Height);
            tblShoppingListItem.SetContentOffset(offset,true );
        }

    }

答案 20 :(得分:2)

swift 3 上的功能滚动到底部

 override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(false)
        //scroll down
        if lists.count > 2 {
            let numberOfSections = self.tableView.numberOfSections
            let numberOfRows = self.tableView.numberOfRows(inSection: numberOfSections-1)
            let indexPath = IndexPath(row: numberOfRows-1 , section: numberOfSections-1)
            self.tableView.scrollToRow(at: indexPath, at: UITableViewScrollPosition.middle, animated: true)
        }
    }

答案 21 :(得分:1)

接受的答案不适用于我的表(数千行,动态加载),但下面的代码有效:

- (void)scrollToBottom:(id)sender {
    if ([self.sections count] > 0) {
        NSInteger idx = [self.sections count] - 1;
        CGRect sectionRect = [self.tableView rectForSection:idx];
        sectionRect.size.height = self.tableView.frame.size.height;
        [self.tableView scrollRectToVisible:sectionRect animated:NO];
    }
}

答案 22 :(得分:1)

[self.tableViewInfo scrollRectToVisible:CGRectMake(0,self.tableViewInfo.contentSize.height-self.tableViewInfo.height,self.tableViewInfo.width,self.tableViewInfo.height)animated:YES];

答案 23 :(得分:1)

如果以编程方式为tableview设置框架,请确保正确设置框架。

答案 24 :(得分:1)

无需任何滚动即可使用此代码执行此操作:

[YOURTABLEVIEWNAME setContentOffset:CGPointMake(0, CGFLOAT_MAX)];

答案 25 :(得分:1)

在iOS中,这对我来说很好用

CGFloat height = self.inputTableView.contentSize.height;
if (height > CGRectGetHeight(self.inputTableView.frame)) {
    height -= (CGRectGetHeight(self.inputTableView.frame) - CGRectGetHeight(self.navigationController.navigationBar.frame));
}
else {
    height = 0;
}
[self.inputTableView setContentOffset:CGPointMake(0, height) animated:animated];

需要从viewDidLayoutSubviews

调用

答案 26 :(得分:0)

一行:

tableView.scrollToRow(at: IndexPath(row: data.count - 1, section: 0), at: .bottom, animated: true)

恕我直言,此代码比接受的答案更清晰。

答案 27 :(得分:0)

雨燕5 这是解决此问题的简单方法

步骤:-> 1-当ViewDidLoad()时,它将滚动 2-customTableView是IBoutlet的tableView var数据:[String] = [“ Hello”,“ This”,“ is”,“ Your”,“ World”]

   override func viewDidLoad() {
   super.viewDidLoad()
  
3- In viewDidLoad we need to tell about indexPath
   /// data.count-1 will give last cell 
   let indexPath = IndexPath(row: data.count - 1, section:0)

   /// 1st Parameter: (at) will use for indexPath
   /// 2nd Parameter: (at) will use for position's scroll view
   /// 3rd Parameter: (animated) will show animation when screen will appear

   customtableView.scrollToRow(at:indexPath, at:.top, animated:true)
    /// Reload CustomTableView
   customTableView.reloadData()

答案 28 :(得分:0)

滚动到tableView底部的最安全方法是使用“ tableView.scrollRectToVisible”。在调用tableView.reloadData()之后使用。 在Swift 5中

private func scrollAtTheBottom() {
    let lastIndexPath = IndexPath(row: state.myItemsArray.count - 1, section: 0)
    let lastCellPosition = tableView.rectForRow(at: lastIndexPath)
    tableView.scrollRectToVisible(lastCellPosition, animated: true)
}

答案 29 :(得分:0)

我发现了另一种好方法,如下:

func yourFunc() {
    //tableView.reloadData()
    self.perform(#selector(scrollToBottom), with: nil, afterDelay: 0.1)
}

@objc func scrollToBottom() {
    self.tableView.scrollToRow(at: IndexPath(row: 0, section: self.mesArr.count - 1), at: .bottom, animated: false)
}

答案 30 :(得分:0)

在iOS 12上,可接受的答案似乎已损坏。我在以下扩展程序中使用了它



import UIKit

extension UITableView {
    public func scrollToBottom(animated: Bool = true) {
        guard let dataSource = dataSource else {
            return
        }

        let sections = dataSource.numberOfSections?(in: self) ?? 1
        let rows = dataSource.tableView(self, numberOfRowsInSection: sections-1)
        let bottomIndex = IndexPath(item: rows - 1, section: sections - 1)

        scrollToRow(at: bottomIndex,
                    at: .bottom,
                    animated: animated)
    }
}

答案 31 :(得分:0)

我相信旧的解决方案不适用于swift3。

如果你知道表格中的数字行,你可以使用:

tableView.scrollToRow(
    at: IndexPath(item: listCountInSection-1, section: sectionCount - 1 ), 
    at: .top, 
    animated: true)

答案 32 :(得分:0)

在swift 3.0中 如果你想去任何特定的tableview细胞改变细胞索引值就像改变&#34; self.yourArr.count&#34;价值。

self.yourTable.reloadData()
self.scrollToBottom() 
func scrollToBottom(){
    DispatchQueue.global(qos: .background).async {
        let indexPath = IndexPath(row: self.yourArr.count-1, section: 0)
        self.tblComment.scrollToRow(at: indexPath, at: .bottom, animated: true)
    }
}

答案 33 :(得分:0)

在Swift中,你只需要

self.tableView.scrollToNearestSelectedRowAtScrollPosition(UITableViewScrollPosition.Bottom, animated: true)

使其自动滚动到按钮