我有一个 UITableView
,其中填充了可变高度的单元格。当视图被推入视图时,我希望表格滚动到底部。
我目前有以下功能
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:[log count]-1 inSection:0];
[self.table scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionBottom animated:NO];
log 是一个可变数组,其中包含构成每个单元格内容的对象。
上面的代码在 viewDidAppear
中工作正常,但是这有一个不幸的副作用,即当视图首次出现时显示表的顶部然后跳到底部。如果 table view
可以在它出现之前滚动到底部,我会更喜欢它。
我在 viewWillAppear
和 viewDidLoad
中尝试了滚动,但在这两种情况下,数据都还没有加载到表中,并且都抛出了异常。
任何指导都将不胜感激,即使这只是告诉我我所拥有的一切都是可能的。
我相信呼唤
tableView.setContentOffset(CGPoint(x: 0, y: CGFloat.greatestFiniteMagnitude), animated: false)
会做你想做的。
我认为最简单的方法是这样的:
if (self.messages.count > 0)
{
[self.tableView
scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:self.messages.count-1
inSection:0]
atScrollPosition:UITableViewScrollPositionBottom animated:YES];
}
斯威夫特 3 版本:
if messages.count > 0 {
userDefinedOptionsTableView.scrollToRow(at: IndexPath(item:messages.count-1, section: 0), at: .bottom, animated: true)
}
messages.count
替换为已实现的 myTableView.dataSource!.tableView(myTableView, numberOfRowsInSection: 0)
可能有意义。是的,它更长,但它可能会避免代码重复。您还需要处理可选的 dataSource
(不要像本示例中那样强制展开)。
从 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];
}
}
UIEdgeInsetsInsetRect(self.messagesTableView.frame, self.messagesTableView.safeAreaInsets).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];
}
yOffset = self.tableView.contentSize.height - self.tableView.bounds.size.height;
?谢谢。
self.tableView.contentSize.height
,表格视图的内容可能不可见,因为您在内容下方滚动。因此,您必须滚动到表格视图末尾上方的一个“可见表格视图间隙”。
我正在使用自动布局,但没有一个答案对我有用。这是我最终奏效的解决方案:
@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)];
}
}
setContentOffset
?
CGFLOAT_MAX
,而是使用了 contentSize.height - frame.height + contentInset.bottom
。使用 CGFLOAT_MAX
对我来说似乎很糟糕。
这是我在 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)
}
}
}
在使用自动布局的 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];
}
}
在 viewDidLoad
结束时调用 [self tableViewScrollToBottomAnimated:NO]
有效。不幸的是,它还会导致每个单元格调用 tableView:heightForRowAtIndexPath:
三次。
实际上,快速执行此操作的“更快”方式是:
var lastIndex = NSIndexPath(forRow: self.messages.count - 1, inSection: 0)
self.messageTableView.scrollToRowAtIndexPath(lastIndex, atScrollPosition: UITableViewScrollPosition.Bottom, animated: true)
非常适合我。
细节
Xcode 8.3.2,迅捷 3.1
Xcode 10.2 (10E125)、斯威夫特 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
}
}
我希望表格加载到框架中显示的表格末尾。我发现使用
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。
scrollToRowAtIndexPath
?之后您已经在调用 setContentOffset
,这可能会使第一次调用毫无意义。
对于斯威夫特:
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)
}
您应该改用 UITableViewScrollPositionBottom
。
对于 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)
}
当然是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)
})
经过大量的摆弄,这对我有用:
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
。例如,每当方向改变时都会调用它。)
使用上述解决方案,这将滚动到表格底部(仅当表格内容首先加载时):
//Scroll to bottom of table
CGSize tableSize = myTableView.contentSize;
[myTableView setContentOffset:CGPointMake(0, tableSize.height)];
在 Swift 3.0 中
self.tableViewFeeds.setContentOffset(CGPoint(x: 0, y: CGFLOAT_MAX), 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)
}
否则它不会滚动到底部。
如果您必须在向下滚动之前异步加载数据,这是可能的解决方案:
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上的功能滚动到底部
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)
}
}
使用这个简单的代码滚动 tableView 底部
NSInteger rows = [tableName numberOfRowsInSection:0];
if(rows > 0) {
[tableName scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:rows-1 inSection:0]
atScrollPosition:UITableViewScrollPositionBottom
animated:YES];
}
感谢雅各布的回答。如果有人对 monotouch 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 );
}
}
在一行中:
tableView.scrollToRow(at: IndexPath(row: data.count - 1, section: 0), at: .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
调用
[self.tableViewInfo scrollRectToVisible:CGRectMake(0, self.tableViewInfo.contentSize.height-self.tableViewInfo.height, self.tableViewInfo.width, self.tableViewInfo.height) 动画: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)];
如果您以编程方式为 tableview 设置框架,请确保正确设置框架。
在 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)
}
}
在 swift 3.0 中,如果你想去 tableview 的任何特定单元更改单元格索引值,例如更改 "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)
}
}
[table reloadData];
之后写这个就可以了CGPoint(x: 0, y: tableView.contentSize.height)
?