我有一个UITableView
,其中填充了具有动态高度的单元格。当从视图控制器推送视图控制器时,我希望表格滚动到底部。
我尝试过使用contentOffset
和tableView scrollToRowAtIndexPath
,但我仍然无法获得完全正确的解决方案。
任何人都可以帮我解决这个问题吗?
这是我要滚动的代码:
let indexPath = NSIndexPath(forRow: commentArray.count-1, inSection: 0)
tableView.scrollToRowAtIndexPath(indexPath, atScrollPosition: .Bottom, animated: true)
答案 0 :(得分:71)
Swift 3.0
写一个函数:
func scrollToBottom(){
DispatchQueue.main.async {
let indexPath = IndexPath(row: self.dataArray.count-1, section: 0)
self.tableView.scrollToRow(at: indexPath, at: .bottom, animated: true)
}
}
并在重新加载tableview数据后调用它
tableView.reloadData()
scrollToBottom()
答案 1 :(得分:19)
我会使用更通用的方法:
extension UITableView {
func scrollToBottom(){
DispatchQueue.main.async {
let indexPath = IndexPath(
row: self.numberOfRows(inSection: self.numberOfSections -1) - 1,
section: self.numberOfSections - 1)
self.scrollToRow(at: indexPath, at: .bottom, animated: true)
}
}
func scrollToTop() {
DispatchQueue.main.async {
let indexPath = IndexPath(row: 0, section: 0)
self.scrollToRow(at: indexPath, at: .top, animated: false)
}
}
}
答案 2 :(得分:13)
我尝试了Umair的方法,但是在UITableView
s中,有时可以有一个包含0行的节;在这种情况下,代码将指向无效的索引路径(空节的行0不是行)。
从行/节的数量中减去1可能是另一个痛点,因为行/节可能包含0个元素。
这是我滚动到最底部单元格的解决方案,请确保索引路径有效:
extension UITableView {
func scrollToBottomRow() {
DispatchQueue.main.async {
guard self.numberOfSections > 0 else { return }
// Make an attempt to use the bottom-most section with at least one row
var section = max(self.numberOfSections - 1, 0)
var row = max(self.numberOfRows(inSection: section) - 1, 0)
var indexPath = IndexPath(row: row, section: section)
// Ensure the index path is valid, otherwise use the section above (sections can
// contain 0 rows which leads to an invalid index path)
while !self.indexPathIsValid(indexPath) {
section = max(section - 1, 0)
row = max(self.numberOfRows(inSection: section) - 1, 0)
indexPath = IndexPath(row: row, section: section)
// If we're down to the last section, attempt to use the first row
if indexPath.section == 0 {
indexPath = IndexPath(row: 0, section: 0)
break
}
}
// In the case that [0, 0] is valid (perhaps no data source?), ensure we don't encounter an
// exception here
guard self.indexPathIsValid(indexPath) else { return }
self.scrollToRow(at: indexPath, at: .bottom, animated: true)
}
}
func indexPathIsValid(_ indexPath: IndexPath) -> Bool {
let section = indexPath.section
let row = indexPath.row
return section < self.numberOfSections && row < self.numberOfRows(inSection: section)
}
}
答案 3 :(得分:7)
当您推送具有tableview的viewcontroller时,只有在Tableview完成重新加载后才应滚动指定的indexPath。
yourTableview.reloadData()
dispatch_async(dispatch_get_main_queue(), { () -> Void in
let indexPath = NSIndexPath(forRow: commentArray.count-1, inSection: 0)
tableView.scrollToRowAtIndexPath(indexPath, atScrollPosition: .Bottom, animated: true)
})
将方法放入dispatch_async的原因是,一旦执行reloadData,下一行将立即执行,然后在主线程中重新加载。因此要知道tableview何时完成(在完成所有cellforrowindex之后),我们在这里使用GCD。基本上tableview中没有委托会告诉tableview已经完成重新加载。
答案 4 :(得分:4)
要完美滚动到底部解决方案,请使用tableView contentOffset
func scrollToBottom() {
let point = CGPoint(x: 0, y: self.tableView.contentSize.height + self.tableView.contentInset.bottom - self.tableView.frame.height)
if point.y >= 0{
self.tableView.setContentOffset(point, animated: animate)
}
}
在主队列中执行滚动到底部的工作是bcoz,它延迟了执行并导致工作,因为加载viewController并延迟了主队列后,tableView现在知道其内容大小。
我宁愿在将数据填充到tableView之后使用self.view.layoutIfNeeded()
,然后调用我的方法scrollToBottom()
。这对我来说很好。
答案 5 :(得分:2)
这适用于Swift 3.0
let pointsFromTop = CGPoint(x: 0, y: CGFloat.greatestFiniteMagnitude)
tableView.setContentOffset(pointsFromTop, animated: true)
答案 6 :(得分:2)
适用于 Swift 5 或更高版本
import UIKit
extension UITableView {
func scrollToBottom(animated: Bool) {
DispatchQueue.main.async {
let point = CGPoint(x: 0, y: self.contentSize.height + self.contentInset.bottom - self.frame.height)
if point.y >= 0 {
self.setContentOffset(point, animated: animated)
}
}
}
}
答案 7 :(得分:1)
使用 Swift 5 ,您也可以在每次重新加载后执行此操作:
DispatchQueue.main.async {
let index = IndexPath(row: self.itens.count-1, section: 0)
self.tableView.scrollToRow(at: index, at: .bottom, animated: true)
}
答案 8 :(得分:1)
您也可以使用此功能:-
tableView.scrollRectToVisible(CGRect(x: 0, y: tableView.contentSize.height, width: 1, height: 1), animated: true)
答案 9 :(得分:0)
Swift 5 解决方案
extension UITableView {
func scrollToBottom(){
DispatchQueue.main.async {
let indexPath = IndexPath(
row: self.numberOfRows(inSection: self.numberOfSections-1) - 1,
section: self.numberOfSections - 1)
if self.hasRowAtIndexPath(indexPath: indexPath) {
self.scrollToRow(at: indexPath, at: .bottom, animated: true)
}
}
}
func scrollToTop() {
DispatchQueue.main.async {
let indexPath = IndexPath(row: 0, section: 0)
if self.hasRowAtIndexPath(indexPath: indexPath) {
self.scrollToRow(at: indexPath, at: .top, animated: false)
}
}
}
func hasRowAtIndexPath(indexPath: IndexPath) -> Bool {
return indexPath.section < self.numberOfSections && indexPath.row < self.numberOfRows(inSection: indexPath.section)
}
}
答案 10 :(得分:0)
要滚动到TableView的末尾,可以使用以下功能,该功能也适用于ScrollViews。
它还会计算iPhone X和更高版本iPhone底部的安全区域。该呼叫是从主队列进行的,以正确计算高度。
app.post(
'/upload_receipt_img',
upload.single("image"),
function(req, res) {
console.log("File uploaded")
console.log(req);
var file = __dirname + '/images/' + req.file.filename + ".jpeg";
fs.rename(req.file.path, file, function(err) {
if (err) {
console.log(err);
res.send(500);
} else {
res.json({
message: 'File uploaded successfully',
filename: req.file.filename
});
}
});
}
)
答案 11 :(得分:0)
滚动scrollToBottom的最佳方法:
在调用scrollToBottom方法之前 调用以下方法
self.view.layoutIfNeeded()
extension UITableView {
func scrollToBottom(animated:Bool) {
let numberOfRows = self.numberOfRows(inSection: self.numberOfSections - 1) - 1
if numberOfRows >= 0{
let indexPath = IndexPath(
row: numberOfRows,
section: self.numberOfSections - 1)
self.scrollToRow(at: indexPath, at: .bottom, animated: animated)
} else {
let point = CGPoint(x: 0, y: self.contentSize.height + self.contentInset.bottom - self.frame.height)
if point.y >= 0{
self.setContentOffset(point, animated: animated)
}
}
}
}
答案 12 :(得分:0)
这是一个包含补全结束符的函数:
func reloadAndScrollToTop(completion: @escaping () -> Void) {
self.tableView.reload()
completion()
}
并使用:
self.reloadAndScrollToTop(completion: {
tableView.scrollToRow(at: indexPath, at: .top, animated: true))
})
在安全地加载了所有表格单元之后,将执行tableView.scrollTo ...
行。
答案 13 :(得分:0)
在Swift 4+中工作:
self.tableView.reloadData()
let indexPath = NSIndexPath(row: self.yourDataArray.count-1, section: 0)
self.tableView.scrollToRow(at: indexPath as IndexPath, at: .bottom, animated: true)
答案 14 :(得分:0)
@Umair答案的一些更新,以防您的tableView为空
func scrollToBottom(animated: Bool = true, delay: Double = 0.0) {
let numberOfRows = tableView.numberOfRows(inSection: tableView.numberOfSections - 1) - 1
guard numberOfRows > 0 else { return }
DispatchQueue.main.asyncAfter(deadline: .now() + delay) { [unowned self] in
let indexPath = IndexPath(
row: numberOfRows,
section: self.tableView.numberOfSections - 1)
self.tableView.scrollToRow(at: indexPath, at: .bottom, animated: animated)
}
}
答案 15 :(得分:0)
[Swift 3,iOS 10]
我最终使用了类似hacky的解决方案,但它不依赖于行indexpaths(有时会导致崩溃),单元格动态高度或表重新加载事件,因此它看起来非常普遍并且在实践中有效比我发现的其他人更可靠。
使用KVO跟踪表格的contentOffset
KVO观察员内的火焰滚动事件
使用延迟计时器计划滚动调用以过滤多个
观察者触发器
一些ViewController中的代码:
query = "SELECT ..."
df = pd.DataFrame(list(session.execute(query)))
答案 16 :(得分:0)
如果您使用UINavigationBar
高度UITableView
UIView
,请尝试从UINavigationBar
的帧高减去UITableView
高度。导致UITableView
的{{1}}点与top
的{{1}}点相同,这会影响您的UINavigationBar
bottom
项滚动。
Swift 3
UITableView
答案 17 :(得分:-1)
适用于Swift 3+:
self.tableView.setContentOffset(CGPoint(x: 0, y: self.tableView.contentSize.height - UIScreen.main.bounds.height), animated: true)