如何在用户可见之前将 UITableView 滚动到所需的行?
How to scroll UITableView to desired row even before visible to user?
我想知道,是否可以将 UITableView
滚动到所需的行,甚至在它对用户可见之前,这样用户就不知道这种滚动操作?
目前,这是我执行滚动到所需行的代码。
class ThemeTableViewController: UITableViewController {
private var firstTime = true
private func scrollRectToVisible() {
let theme = WeNoteOptions.theme
if let index = Theme.allCases.firstIndex(of: theme) {
let indexPath = IndexPath(row: index, section: 0)
let rect = tableView.rectForRow(at: indexPath)
tableView.scrollRectToVisible(rect, animated: true)
}
}
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
if firstTime {
firstTime.toggle()
scrollRectToVisible()
}
}
请注意,在以下函数中放置scrollRectToVisible
将不起作用。到目前为止,viewDidAppear
是 scrollRectToVisible
唯一可以工作的地方。
viewDidLoad
- scrollRectToVisible
在这里不起作用。
viewWillAppear
- scrollRectToVisible
在这里不起作用。
这是目前的结果。
用户会注意到自动滚动操作。
我们希望用户不知道滚动动作。
是否有可能当用户第一次看到 UITableView
时,最后一行(绿色鳄梨)已经可见?
里面viewDidLayoutSubviews
if tableView.numberOfRows(inSection: 0) != 0 {
scrollRectToVisible()
}
和animated: false)
tableView.scrollRectToVisible(rect, animated: false)
我用 animated
作为 false
从 viewDidAppear
方法调用了 tableView.scrollRectToVisible(rect, animated: false)
,结果符合预期。
我已经根据您上面的代码进行了复制和更改。在这里,我在您的方法中添加了 animated:bool
作为参数 scrollRectToVisible
以根据需要控制动画部分。
class ThemeTableViewController: UITableViewController {
private var firstTime = true
// added new parameter `animated`
private func scrollRectToVisible(_ animated: Bool = true) {
let theme = WeNoteOptions.theme
if let index = Theme.allCases.firstIndex(of: theme) {
let indexPath = IndexPath(row: index, section: 0)
let rect = tableView.rectForRow(at: indexPath)
// replaced true with `animated`
tableView.scrollRectToVisible(rect, animated: animated)
}
}
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
if firstTime {
firstTime.toggle()
// animation = false.
scrollRectToVisible(false)
}
}
}
如果您的单元格可能具有动态高度,您可能会发现这更可靠:
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
if firstTime {
if tableView.numberOfRows(inSection: 0) != 0 {
firstTime = false
tableView.scrollToRow(at: IndexPath(row: tableView.numberOfRows(inSection: 0) - 1, section: 0), at: .bottom, animated: false)
}
}
}
使用以下示例对其进行测试...标签中的单元格将包含 2 到 6 行文本。注释/取消注释 viewDidLayoutSubviews()
:
中的不同方法
class MyTableVC: UITableViewController {
private var firstTime = true
override func viewDidLoad() {
super.viewDidLoad()
tableView.register(MyCell.self, forCellReuseIdentifier: "myCell")
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
if firstTime {
if tableView.numberOfRows(inSection: 0) != 0 {
firstTime = false
// note the difference between this
tableView.scrollToRow(at: IndexPath(row: tableView.numberOfRows(inSection: 0) - 1, section: 0), at: .bottom, animated: false)
// and this
//scrollRectToVisible()
}
}
}
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 30
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let c = tableView.dequeueReusableCell(withIdentifier: "myCell", for: indexPath) as! MyCell
let n = indexPath.row % 5
var s = "Row \(indexPath.row)"
for i in 0...n {
s += "\n\(i)"
}
c.theLabel.text = s
return c
}
private func scrollRectToVisible() {
let index = tableView.numberOfRows(inSection: 0) - 1
let indexPath = IndexPath(row: index, section: 0)
let rect = tableView.rectForRow(at: indexPath)
tableView.scrollRectToVisible(rect, animated: false)
}
}
class MyCell: UITableViewCell {
var theLabel = UILabel()
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
commonInit()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
commonInit()
}
func commonInit() -> Void {
theLabel.translatesAutoresizingMaskIntoConstraints = false
theLabel.numberOfLines = 0
theLabel.backgroundColor = .yellow
contentView.addSubview(theLabel)
let g = contentView.layoutMarginsGuide
NSLayoutConstraint.activate([
theLabel.topAnchor.constraint(equalTo: g.topAnchor),
theLabel.leadingAnchor.constraint(equalTo: g.leadingAnchor),
theLabel.trailingAnchor.constraint(equalTo: g.trailingAnchor),
theLabel.bottomAnchor.constraint(equalTo: g.bottomAnchor),
])
}
}
Here consider itemCount = last item of your array
Add code in viewDidload method after your array contains data.
tableView.reloadData()
let indexPath = IndexPath(item: itemCount, section: 0)
print("indexPath",indexPath)
tableView.scrollToRow(at: indexPath, at: .none, animated: false)
我想知道,是否可以将 UITableView
滚动到所需的行,甚至在它对用户可见之前,这样用户就不知道这种滚动操作?
目前,这是我执行滚动到所需行的代码。
class ThemeTableViewController: UITableViewController {
private var firstTime = true
private func scrollRectToVisible() {
let theme = WeNoteOptions.theme
if let index = Theme.allCases.firstIndex(of: theme) {
let indexPath = IndexPath(row: index, section: 0)
let rect = tableView.rectForRow(at: indexPath)
tableView.scrollRectToVisible(rect, animated: true)
}
}
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
if firstTime {
firstTime.toggle()
scrollRectToVisible()
}
}
请注意,在以下函数中放置scrollRectToVisible
将不起作用。到目前为止,viewDidAppear
是 scrollRectToVisible
唯一可以工作的地方。
viewDidLoad
-scrollRectToVisible
在这里不起作用。viewWillAppear
-scrollRectToVisible
在这里不起作用。
这是目前的结果。
用户会注意到自动滚动操作。
我们希望用户不知道滚动动作。
是否有可能当用户第一次看到 UITableView
时,最后一行(绿色鳄梨)已经可见?
里面viewDidLayoutSubviews
if tableView.numberOfRows(inSection: 0) != 0 {
scrollRectToVisible()
}
和animated: false)
tableView.scrollRectToVisible(rect, animated: false)
我用 animated
作为 false
从 viewDidAppear
方法调用了 tableView.scrollRectToVisible(rect, animated: false)
,结果符合预期。
我已经根据您上面的代码进行了复制和更改。在这里,我在您的方法中添加了 animated:bool
作为参数 scrollRectToVisible
以根据需要控制动画部分。
class ThemeTableViewController: UITableViewController {
private var firstTime = true
// added new parameter `animated`
private func scrollRectToVisible(_ animated: Bool = true) {
let theme = WeNoteOptions.theme
if let index = Theme.allCases.firstIndex(of: theme) {
let indexPath = IndexPath(row: index, section: 0)
let rect = tableView.rectForRow(at: indexPath)
// replaced true with `animated`
tableView.scrollRectToVisible(rect, animated: animated)
}
}
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
if firstTime {
firstTime.toggle()
// animation = false.
scrollRectToVisible(false)
}
}
}
如果您的单元格可能具有动态高度,您可能会发现这更可靠:
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
if firstTime {
if tableView.numberOfRows(inSection: 0) != 0 {
firstTime = false
tableView.scrollToRow(at: IndexPath(row: tableView.numberOfRows(inSection: 0) - 1, section: 0), at: .bottom, animated: false)
}
}
}
使用以下示例对其进行测试...标签中的单元格将包含 2 到 6 行文本。注释/取消注释 viewDidLayoutSubviews()
:
class MyTableVC: UITableViewController {
private var firstTime = true
override func viewDidLoad() {
super.viewDidLoad()
tableView.register(MyCell.self, forCellReuseIdentifier: "myCell")
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
if firstTime {
if tableView.numberOfRows(inSection: 0) != 0 {
firstTime = false
// note the difference between this
tableView.scrollToRow(at: IndexPath(row: tableView.numberOfRows(inSection: 0) - 1, section: 0), at: .bottom, animated: false)
// and this
//scrollRectToVisible()
}
}
}
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 30
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let c = tableView.dequeueReusableCell(withIdentifier: "myCell", for: indexPath) as! MyCell
let n = indexPath.row % 5
var s = "Row \(indexPath.row)"
for i in 0...n {
s += "\n\(i)"
}
c.theLabel.text = s
return c
}
private func scrollRectToVisible() {
let index = tableView.numberOfRows(inSection: 0) - 1
let indexPath = IndexPath(row: index, section: 0)
let rect = tableView.rectForRow(at: indexPath)
tableView.scrollRectToVisible(rect, animated: false)
}
}
class MyCell: UITableViewCell {
var theLabel = UILabel()
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
commonInit()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
commonInit()
}
func commonInit() -> Void {
theLabel.translatesAutoresizingMaskIntoConstraints = false
theLabel.numberOfLines = 0
theLabel.backgroundColor = .yellow
contentView.addSubview(theLabel)
let g = contentView.layoutMarginsGuide
NSLayoutConstraint.activate([
theLabel.topAnchor.constraint(equalTo: g.topAnchor),
theLabel.leadingAnchor.constraint(equalTo: g.leadingAnchor),
theLabel.trailingAnchor.constraint(equalTo: g.trailingAnchor),
theLabel.bottomAnchor.constraint(equalTo: g.bottomAnchor),
])
}
}
Here consider itemCount = last item of your array
Add code in viewDidload method after your array contains data.
tableView.reloadData()
let indexPath = IndexPath(item: itemCount, section: 0)
print("indexPath",indexPath)
tableView.scrollToRow(at: indexPath, at: .none, animated: false)