我正在创建聊天机器人应用程序,它模仿真实用户的“输入”状态,因此在每条消息之后必须有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秒钟,然后它们都会立刻出现。为什么呢?
答案 0 :(得分:1)
您的问题是您正在主线程上运行,并且永远不会让系统有机会更新表。您违反了规则:永远不会在主线程上调用sleep
。
您可以通过以下几种方式解决此问题:
1)在后台线程上运行循环,并返回主线程以调用self.sendMessage
和print
。
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 {
}
}
答案 1 :(得分: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 {
}
}
此代码:
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");
}
答案 2 :(得分:1)
不要睡觉主线。
相反,您在之后设置了一个函数调用。
你应该在每个单元格内创建一个计时器,然后每个单元格都有单独的计时器。
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
}