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

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

我目前有以下功能

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

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

上面的代码在 viewDidAppear中工作得很好,但是这有一个不幸的副作用,当视图第一次出现时显示表的顶部,然后跳到底部。我希望它如果 table view可以滚动到底部之前,它出现。

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

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

182231 次浏览

我相信这个使命

 tableView.setContentOffset(CGPoint(x: 0, y: CGFloat.greatestFiniteMagnitude), animated: false)

会做你想做的事。

您应该改用 UITableViewScrollPositionBottom

雅各布的回答,这是代码:

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

我希望用框架中显示的表的末端加载表

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

在 viewDidLoad 中,BOOL ivar initialLoad 设置为 TRUE。

使用上述解决方案,这将滚动到表的底部(只有在表内容首先加载的情况下) :

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

如果你需要滚动到内容的精确结尾,你可以这样做:

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

我认为最简单的方法是:

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

感谢 Jacob 的回答。如果有人对单点触控 c # 版本感兴趣的话,这个问题非常有帮助

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

在 iOS7.0中使用自动布局时,接受的解决方案@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];
}
}

viewDidLoad结束时调用 [self tableViewScrollToBottomAnimated:NO]可以工作。不幸的是,它还导致 tableView:heightForRowAtIndexPath:每个细胞被调用三次。

我使用的是自动布局,没有一个答案对我有效。下面是我最终成功的解决方案:

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

斯威夫特:

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

事实上,更快捷的方法是:

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

很适合我。

在 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调用

当然是虫子。 可能在您的代码中的某个地方您使用了 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)
})

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

接受的答案不适用于我的表(数千行,动态加载) ,但是下面的代码可以工作:

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

不需要任何滚动,你只需要使用下面的代码:

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

经过大量的调整,这个方法对我很有效:

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

关键原来是 没有包装 scrollToRowAtIndexPath内的 dispatch_async的一些建议,但只是跟随它调用 layoutIfNeeded

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

(另外注意,您需要 viewHasAppeared标志,因为您不希望每次调用 viewDidLayoutSubviews时都使用 goToBottom。例如,当方向改变时,它就会被调用。)

这是我在 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)
}
}
}

使用这个简单的代码可以滚动 tableView 底部

NSInteger rows = [tableName numberOfRowsInSection:0];
if(rows > 0) {
[tableName scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:rows-1 inSection:0]
atScrollPosition:UITableViewScrollPositionBottom
animated:YES];
}

如果以编程方式为表视图设置框架,请确保正确设置了框架。

在斯威夫特,你只需要

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

让它自动滚动到底部

如果您必须在向下滚动之前异步加载数据,以下是可能的解决方案:

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

对于 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)
}

在 Swift 3.0中

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

在 Swift 3.0中 如果你想去任何特定的单元格的桌面视图改变单元格索引值,比如改变“ self. yourArr.count”值。

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

函数对 雨燕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)
}
}
func scrollToBottom() {


let sections = self.chatTableView.numberOfSections


if sections > 0 {


let rows = self.chatTableView.numberOfRows(inSection: sections - 1)


let last = IndexPath(row: rows - 1, section: sections - 1)


DispatchQueue.main.async {


self.chatTableView.scrollToRow(at: last, at: .bottom, animated: false)
}
}
}

你应该加上

DispatchQueue.main.async {
self.chatTableView.scrollToRow(at: last, at: .bottom, animated: false)
}

否则它不会滚动到底部。

细节

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

我认为旧的解决方案对 Swift 不起作用。

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

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

在 iOS12上,这个被接受的答案似乎被打破了




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

我找到了另一个好方法,如下:

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

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

Swift 5 这里有一个解决这个问题的简单方法

步骤:-> 1-当 ViewDidLoad ()时,它将滚动 2-customTableView 是 IBstore 的 tableView Var data: [ 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()

一句话:

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

恕我直言,这个代码比公认的答案更清楚。