UITableView:reloadRows(at:) 需要两次点击 table 才能更新并每次滚动
UITableView: reloadRows(at:) takes two hits for table to be updated and scroll every time
我有一个从 CoreData 数据库更新的 table 视图(控制器:MetricsViewController)。我使用了原型单元格 (MetricsViewCell),这是我根据自己的需要定制的。它包含一个分段控件,一个UIView(metricsChart,用于显示图表-animatedCircle),以及一些UILabels。
MetricsViewCell:
class MetricsViewCell: UITableViewCell {
var delegate: SelectSegmentedControl?
var animatedCircle: AnimatedCircle?
@IBOutlet weak var percentageCorrect: UILabel!
@IBOutlet weak var totalPlay: UILabel!
@IBOutlet weak var metricsChart: UIView! {
didSet {
animatedCircle = AnimatedCircle(frame: metricsChart.bounds)
}
}
@IBOutlet weak var recommendationLabel: UILabel!
@IBOutlet weak var objectType: UISegmentedControl!
@IBAction func displayObjectType(_ sender: UISegmentedControl) {
delegate?.tapped(cell: self)
}
}
protocol SelectSegmentedControl {
func tapped(cell: MetricsViewCell)
}
MetricsViewController:
class MetricsViewController: FetchedResultsTableViewController, SelectSegmentedControl {
func tapped(cell: MetricsViewCell) {
if let indexPath = tableView.indexPath(for: cell) {
tableView.reloadRows(at: [indexPath], with: .none)
}
}
var container: NSPersistentContainer? = (UIApplication.shared.delegate as? AppDelegate)?.persistentContainer { didSet { updateUI() } }
private var fetchedResultsController: NSFetchedResultsController<Object>?
private func updateUI() {
if let context = container?.viewContext {
let request: NSFetchRequest<Object> = Object.fetchRequest()
request.sortDescriptors = []
fetchedResultsController = NSFetchedResultsController<Object>(
fetchRequest: request,
managedObjectContext: context,
sectionNameKeyPath: "game.gameIndex",
cacheName: nil)
try? fetchedResultsController?.performFetch()
tableView.reloadData()
}
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Object Cell", for: indexPath)
if let object = fetchedResultsController?.object(at: indexPath) {
if let objectCell = cell as? MetricsViewCell {
objectCell.delegate = self
let request: NSFetchRequest<Object> = Object.fetchRequest()
...
...
}
}
}
return cell
}
当用户选择某个部分的分段控件中的一个分段时,MetricsViewController 应重新加载该特定行中的数据。 (有两个部分,每个部分一行)。因此,我在 MetricsViewCell 中定义了一个协议来通知我的控制器用户操作。
正在使用 FetchedResultsTableViewController
更新数据 - 它基本上充当 CoreData 和 TableView 之间的委托。一切都很好,这意味着我正在将正确的数据输入到我的 TableView 中。
有两个问题:
我必须点击分段控件的段两次才能重新加载点击分段控件的行中的数据。
每次从分段控件中选择一个段时,table 向上滚动然后向下滚动。
非常感谢您的帮助。我在开发过程中遇到的很多问题都依赖这个社区,我已经很感激了:)
例如,在动物识别部分,我必须点击“中级”两次才能重新加载它的行(如果你仔细观察,我第一次点击中级时,它被选中只有几分之一秒,然后它回到“基本”或首先选择的任何部分。第二次当我达到中级时,它会进入中级)。另外,table 上下滚动,这是我不想要的。
编辑:添加了更多关于我使用 CoreData 和持久容器的上下文。
而不是使用 indexPathForRow(at: <#T##CGPoint#>)
函数来获取单元格的 indexPath 对象,您可以直接使用 indexPath(for: <#T##UITableViewCell#>)
,因为您正在接收到 func tapped(cell: MetricsViewCell) {}
的单元格对象,并尝试更新您的数据UI 总是在主线程中,如下所示。
func tapped(cell: MetricsViewCell) {
if let lIndexPath = table.indexPath(for: <#T##UITableViewCell#>){
DispatchQueue.main.async(execute: {
table.reloadRows(at: lIndexPath, with: .none)
})
}
}
您的 UISegmentedControl
正在重用 [UITableView 的默认行为]。
为避免这种情况,请保留 dictionary
以获取和存储值。
另一件事,在 UIViewController
本身中尝试 outlet connection as Action
for UISegmentedControl
,而不是你的 UITableViewCell
当您点击 UISegmentedControl
时,以下代码不会重新加载您的表格视图。你可以避免,代表也打电话。
以下代码是 UISegmentedControl
的基本演示。根据您的需要进行定制。
var segmentDict = [Int : Int]()
override func viewDidLoad() {
super.viewDidLoad()
for i in 0...29 // number of rows count
{
segmentDict[i] = 0 //DEFAULT SELECTED SEGMENTS
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! SOTableViewCell
cell.mySegment.selectedSegmentIndex = segmentDict[indexPath.row]!
cell.selectionStyle = .none
return cell
}
@IBAction func mySegmentAcn(_ sender: UISegmentedControl) {
let cellPosition = sender.convert(CGPoint.zero, to: tblVw)
let indPath = tblVw.indexPathForRow(at: cellPosition)
segmentDict[(indPath?.row)!] = sender.selectedSegmentIndex
print("Sender.tag ", indPath)
}
我有一个从 CoreData 数据库更新的 table 视图(控制器:MetricsViewController)。我使用了原型单元格 (MetricsViewCell),这是我根据自己的需要定制的。它包含一个分段控件,一个UIView(metricsChart,用于显示图表-animatedCircle),以及一些UILabels。
MetricsViewCell:
class MetricsViewCell: UITableViewCell {
var delegate: SelectSegmentedControl?
var animatedCircle: AnimatedCircle?
@IBOutlet weak var percentageCorrect: UILabel!
@IBOutlet weak var totalPlay: UILabel!
@IBOutlet weak var metricsChart: UIView! {
didSet {
animatedCircle = AnimatedCircle(frame: metricsChart.bounds)
}
}
@IBOutlet weak var recommendationLabel: UILabel!
@IBOutlet weak var objectType: UISegmentedControl!
@IBAction func displayObjectType(_ sender: UISegmentedControl) {
delegate?.tapped(cell: self)
}
}
protocol SelectSegmentedControl {
func tapped(cell: MetricsViewCell)
}
MetricsViewController:
class MetricsViewController: FetchedResultsTableViewController, SelectSegmentedControl {
func tapped(cell: MetricsViewCell) {
if let indexPath = tableView.indexPath(for: cell) {
tableView.reloadRows(at: [indexPath], with: .none)
}
}
var container: NSPersistentContainer? = (UIApplication.shared.delegate as? AppDelegate)?.persistentContainer { didSet { updateUI() } }
private var fetchedResultsController: NSFetchedResultsController<Object>?
private func updateUI() {
if let context = container?.viewContext {
let request: NSFetchRequest<Object> = Object.fetchRequest()
request.sortDescriptors = []
fetchedResultsController = NSFetchedResultsController<Object>(
fetchRequest: request,
managedObjectContext: context,
sectionNameKeyPath: "game.gameIndex",
cacheName: nil)
try? fetchedResultsController?.performFetch()
tableView.reloadData()
}
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Object Cell", for: indexPath)
if let object = fetchedResultsController?.object(at: indexPath) {
if let objectCell = cell as? MetricsViewCell {
objectCell.delegate = self
let request: NSFetchRequest<Object> = Object.fetchRequest()
...
...
}
}
}
return cell
}
当用户选择某个部分的分段控件中的一个分段时,MetricsViewController 应重新加载该特定行中的数据。 (有两个部分,每个部分一行)。因此,我在 MetricsViewCell 中定义了一个协议来通知我的控制器用户操作。
正在使用 FetchedResultsTableViewController
更新数据 - 它基本上充当 CoreData 和 TableView 之间的委托。一切都很好,这意味着我正在将正确的数据输入到我的 TableView 中。
有两个问题:
我必须点击分段控件的段两次才能重新加载点击分段控件的行中的数据。
每次从分段控件中选择一个段时,table 向上滚动然后向下滚动。
非常感谢您的帮助。我在开发过程中遇到的很多问题都依赖这个社区,我已经很感激了:)
例如,在动物识别部分,我必须点击“中级”两次才能重新加载它的行(如果你仔细观察,我第一次点击中级时,它被选中只有几分之一秒,然后它回到“基本”或首先选择的任何部分。第二次当我达到中级时,它会进入中级)。另外,table 上下滚动,这是我不想要的。
编辑:添加了更多关于我使用 CoreData 和持久容器的上下文。
而不是使用 indexPathForRow(at: <#T##CGPoint#>)
函数来获取单元格的 indexPath 对象,您可以直接使用 indexPath(for: <#T##UITableViewCell#>)
,因为您正在接收到 func tapped(cell: MetricsViewCell) {}
的单元格对象,并尝试更新您的数据UI 总是在主线程中,如下所示。
func tapped(cell: MetricsViewCell) {
if let lIndexPath = table.indexPath(for: <#T##UITableViewCell#>){
DispatchQueue.main.async(execute: {
table.reloadRows(at: lIndexPath, with: .none)
})
}
}
您的 UISegmentedControl
正在重用 [UITableView 的默认行为]。
为避免这种情况,请保留 dictionary
以获取和存储值。
另一件事,在 UIViewController
本身中尝试 outlet connection as Action
for UISegmentedControl
,而不是你的 UITableViewCell
当您点击 UISegmentedControl
时,以下代码不会重新加载您的表格视图。你可以避免,代表也打电话。
以下代码是 UISegmentedControl
的基本演示。根据您的需要进行定制。
var segmentDict = [Int : Int]()
override func viewDidLoad() {
super.viewDidLoad()
for i in 0...29 // number of rows count
{
segmentDict[i] = 0 //DEFAULT SELECTED SEGMENTS
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! SOTableViewCell
cell.mySegment.selectedSegmentIndex = segmentDict[indexPath.row]!
cell.selectionStyle = .none
return cell
}
@IBAction func mySegmentAcn(_ sender: UISegmentedControl) {
let cellPosition = sender.convert(CGPoint.zero, to: tblVw)
let indPath = tblVw.indexPathForRow(at: cellPosition)
segmentDict[(indPath?.row)!] = sender.selectedSegmentIndex
print("Sender.tag ", indPath)
}