使用超时填充 UITableView



我正在创建聊天机器人应用程序,它模仿真实用户的"打字"状态,因此在每条消息之后必须有 1 秒(例如(延迟,然后应用程序才会发布第二条消息。

我的消息传递逻辑是

let size = dictionary[key]!.count;
let pointer = random(lower: 0, upper: UInt32(size) - 1);
let part = dictionary[key]![pointer];
for row in part {
if let message = row as? Message {
self.sendMessage(message: message);
print("MESSAGE TRIGGERED");
usleep(1 * 1000 * 1000)
}
if let question = row as? Question {
}
}

在这里,我随机获取消息数组(实际上是字符串(,并在循环中将它们添加到我的表中,如下所示

func sendMessage(message: Message) {
self.tableView.beginUpdates();
let indexPath = IndexPath.init(row: self.log.count, section: 0);
self.log.insert(
message,
at: self.log.count
);
self.tableView.insertRows(at: [indexPath], with: UITableViewRowAnimation.fade);
self.tableView.endUpdates()
self.tableView.scrollToRow(at: indexPath, at: .bottom, animated: true)
}

问题是,如果消息数组中有 10 个字符串,我会在控制台中看到 10 个"MESSAGE TRIGGERED"日志,每秒 1 个新行,就像我计划的那样,但在表中,我没有看到任何行 10 秒,然后它们都立即出现。为什么?

您的问题是您在主线程上运行并且从未给系统更新表的机会。 您违反了规则:永远不要在主线程上调用sleep

您可以通过多种方式解决此问题:

1(在后台线程上运行循环,然后返回主线程调用self.sendMessageprint

let size = dictionary[key]!.count;
let pointer = random(lower: 0, upper: UInt32(size) - 1);
let part = dictionary[key]![pointer];
DispatchQueue.global().async {
for row in part {
if let message = row as? Message {
DispatchQueue.main.async {
self.sendMessage(message: message);
print("MESSAGE TRIGGERED");
}
usleep(1 * 1000 * 1000)
}
if let question = row as? Question {
}
}
}

2( 使用间隔为1秒的重复计时器,并在每次计时器触发时向表中添加一个项目。

let size = dictionary[key]!.count;
let pointer = random(lower: 0, upper: UInt32(size) - 1);
let part = dictionary[key]![pointer];
var idx = 0
Timer.scheduledTimer(withTimeInterval: 1, repeats: true) { timer in
if idx >= part.count {
timer.invalidate()
} else {
let row = part[idx]
if let message = row as? Message {
self.sendMessage(message: message);
print("MESSAGE TRIGGERED");
}
if let question = row as? Question {
}
}
idx += 1
}

3( 延迟每个表添加的延迟越来越大:

let size = dictionary[key]!.count;
let pointer = random(lower: 0, upper: UInt32(size) - 1);
let part = dictionary[key]![pointer];
var delay = 0.0
for row in part {
if let message = row as? Message {
DispatchQueue.main.asyncAfter(deadline: .now() + delay) {
self.sendMessage(message: message);
print("MESSAGE TRIGGERED");
}
delay += 1.0
}
if let question = row as? Question {
}
}

将你的代码替换为:

let size = dictionary[key]!.count;
let pointer = random(lower: 0, upper: UInt32(size) - 1);
let part = dictionary[key]![pointer];
for row in part {
if let message = row as? Message {
self.sendMessage(message: message);
print("MESSAGE TRIGGERED");
usleep(1 * 1000 * 1000)
}
if let question = row as? Question {
}
}

此代码:

let size = dictionary[key]!.count;
let pointer = random(lower: 0, upper: UInt32(size) - 1);
let part = dictionary[key]![pointer];
var timer = Timer!
for row in part {
if let message = row as? Message {
timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(runTimedCode), userInfo: nil, repeats: true)
}
if let question = row as? Question {
}
}
timer.invalidate()

同时添加此函数:

@objc func runTimedCode() {
self.sendMessage(message: message);
print("MESSAGE TRIGGERED");
}

不要睡主线程。

相反,您可以在一秒钟后设置函数调用。

您应该在每个单元格内创建一个计时器,然后每个单元格都有单独的计时器。

class MyCustomCell: UITableViewCell {
@IBOutlet weak private var myLabel: UILabel!
private var timer: Timer?
private var timeCounter: Double = 0
var expiryTimeInterval: TimeInterval? {
didSet {
startTimer()
}
}
private func startTimer() {
if let interval = expiryTimeInterval {
timeCounter = interval // should be 1 for 1 second.
if #available(iOS 10.0, *) {
timer = Timer(timeInterval: 1.0,
repeats: true,
block: { [weak self] _ in
guard let strongSelf = self else {
return
}
strongSelf.onComplete()
})
} else {
timer = Timer(timeInterval: 1.0,
target: self,
selector: #selector(onComplete),
userInfo: nil,
repeats: true)
}
}
}
@objc func onComplete() {
guard timeCounter >= 0 else {
timer?.invalidate()
timer = nil
return
}
myLabel.text = String(format: "%d", timeCounter)
timeCounter -= 1
}
}

你可以这样使用;

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell =  tableView.dequeueReusableCell(withIdentifier: "cell") as! MyCustomCell
cell.expiryTimeInterval = 10
return cell
}

最新更新