我有一个项目数组和用于显示它们的 TableView。项目包含 4 个属性。并且有一个方法可以随机生成项目。最初,数组为空,但在 viewDidLoad 中我有方法,
我有一组项目和用于显示它们的 TableView。项目包含 4 个属性。并且有一个方法可以随机生成项目。最初,数组为空,但在 viewDidLoad 中,我有一个方法,它将 100 个项目附加到数组中,延迟约 1 秒。数组会一直附加,直到其计数达到约 1_000_000 个项目。当我启动应用程序时,它会冻结。
这是我的 tableView 方法:
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return model.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: DealCell.reuseIidentifier, for: indexPath) as! DealCell
guard model.count != 0 else {
cell.instrumentNameLabel.text = "no data"
cell.priceLabel.text = "no data"
cell.amountLabel.text = "no data"
cell.sideLabel.text = "no data"
return cell
}
cell.instrumentNameLabel.text = "\(model[indexPath.row].instrumentName)"
cell.priceLabel.text = "\(model[indexPath.row].price)"
cell.amountLabel.text = "\(model[indexPath.row].amount)"
cell.sideLabel.text = "\(model[indexPath.row].side)"
return cell
}
附加数组的函数:
server.subscribeToUpdate { updates in
self.model.append(contentsOf: updates)
self.tableView.reloadData()
}
如何解决这个问题?可能是将 \'numberOfRowsInSection\' 的计数器设置为 100,然后当滚动到第 100 个项目时将其增加到 200 等。或者有更简洁的解决方案吗?
尝试使用 ReusableCell,但没有任何反应。
我不知道您在这里真正想做什么......您的代码看起来就像是在做一个“压力测试”或类似的事情。
但是,为了帮助您理解为什么您的应用会“冻结”——
您的 for i in 0...dealsCount {
循环将运行得 非常 快。每 100 次迭代可能需要 1 或 2 千分之一 秒。如果您 .reloadData()
每 100 次循环调用一次,您的代码就会尝试不断更新 UI。这意味着您的 UI 将显示为“冻结”。
这是一个您可能会觉得有用的例子...
首先,我们将添加一个“状态标签”和一个进度视图,以显示生成交易时的进度。我们将每创建 1000 个新交易更新一次这些内容。
其次,当我们生成新的交易时,我们会将它们直接附加到控制器的 var model: [Deal] = []
数组中(而不是构建新的数组并定期附加它们)。
第三,我们只会调用 .reloadData()
:
正如我所说的,我不知道你真正在做什么......但是在我们添加接下来的 100,000 条记录之前,不太可能有人会滚动浏览前 1,000 行。
但是,您会发现可以 生成记录 时
它看起来是这样的:
交易结构
struct Deal {
var id: Int64 = 0
var dateModifier: Date = Date()
var instrumentName: String = ""
var price: Double = 0
var amount: Double = 0
}
简单的多行标签单元格类
class DealCell: UITableViewCell {
let 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() {
theLabel.numberOfLines = 0
theLabel.backgroundColor = UIColor(white: 0.95, alpha: 1.0)
theLabel.translatesAutoresizingMaskIntoConstraints = false
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),
])
}
func fillData(_ aDeal: Deal) {
let i = aDeal.id
let d = aDeal.dateModifier
let nm = aDeal.instrumentName
let p = String(format: "%0.2f", aDeal.price)
let a = String(format: "%0.2f", aDeal.amount)
theLabel.text = "\(i): \(nm)\nPrice: \(p) / Amount: \(a)\n\(d)"
}
}
演示视图控制器
class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
var model: [Deal] = []
let instrumentNames: [String] = [
"accordion",
"acoustic guitar",
"bagpipes",
"banjo",
"bass guitar",
"bongo drums",
"bugle",
"cello",
"clarinet",
"cymbals",
"drums",
"electric guitar",
"flute",
"French horn",
"harmonica",
"harp",
"keyboard",
"maracas",
"organ",
"pan flute (pan pipes)",
"piano",
"recorder",
"saxophone",
"sitar",
"tambourine",
"triangle",
"trombone",
"trumpet",
"tuba",
"ukulele",
"violin",
"xylophone",
]
let tableView = UITableView()
let progressView = UIProgressView()
let statusLabel = UILabel()
override func viewDidLoad() {
super.viewDidLoad()
[statusLabel, progressView, tableView].forEach { v in
v.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(v)
}
let g = view.safeAreaLayoutGuide
NSLayoutConstraint.activate([
statusLabel.topAnchor.constraint(equalTo: g.topAnchor, constant: 8.0),
statusLabel.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 8.0),
statusLabel.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -8.0),
progressView.topAnchor.constraint(equalTo: statusLabel.bottomAnchor, constant: 8.0),
progressView.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 20.0),
progressView.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -20.0),
tableView.topAnchor.constraint(equalTo: progressView.bottomAnchor, constant: 8.0),
tableView.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 0.0),
tableView.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: 0.0),
tableView.bottomAnchor.constraint(equalTo: g.bottomAnchor, constant: 0.0),
])
tableView.register(DealCell.self, forCellReuseIdentifier: "c")
tableView.dataSource = self
tableView.delegate = self
statusLabel.textAlignment = .center
statusLabel.textColor = .systemRed
subscribeToDeals()
}
func updateProgress(currentCount: Int64, futureCount: Int64) {
// update the status Lable and progress bar
statusLabel.text = "Generated \(currentCount) of \(futureCount)"
progressView.progress = Float(currentCount) / Float(futureCount)
// only reload the table if we're at
// the first 1_000, or
// every 100_000, or
// we're finished generating Deals
// if it's the first update
if currentCount == 1_000 {
tableView.reloadData()
}
// else if we're at an even 100_000
else if currentCount % 100_000 == 0 {
tableView.reloadData()
}
// else if we've generated all
else if currentCount == futureCount {
tableView.reloadData()
}
}
func subscribeToDeals() {
let bkgQueue = DispatchQueue(label: "subscribing", qos: .background)
bkgQueue.async{
let dealsCount = Int64.random(in: 1_000_000..<1_001_000)
for i in 0...dealsCount {
let currentTimeStamp = Date().timeIntervalSince1970
let timeStampRandomizer = Double.random(in: 50_000...50_000_000)
let deal = Deal (
id: i,
dateModifier: Date(timeIntervalSince1970: Double.random(in: currentTimeStamp - timeStampRandomizer...currentTimeStamp)),
instrumentName: self.instrumentNames.shuffled().first!,
price: Double.random(in: 60...70),
amount: Double.random(in: 1_000_000...50_000_000)
)
// append directly to data
self.model.append(deal)
// if we're at a 1_000 point
if i % 1_000 == 0 {
DispatchQueue.main.async {
self.updateProgress(currentCount: i, futureCount: dealsCount)
}
}
}
// we've generated all deals
DispatchQueue.main.async {
self.updateProgress(currentCount: dealsCount, futureCount: dealsCount)
}
print("Done generating \(dealsCount) Deals!")
}
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return model.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let c = tableView.dequeueReusableCell(withIdentifier: "c", for: indexPath) as! DealCell
c.fillData(model[indexPath.row])
return c
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if model.count > 1_000_000 {
tableView.scrollToRow(at: IndexPath(row: 900_000, section: 0), at: .middle, animated: false)
}
}
}
注意: 这 仅仅是示例代码! 它不打算、也不应被视为“可用于生产”。