在我的Swift
应用中,我有UITableView
和UITextView
。这个想法很简单,当用户添加文本时 - 它应该出现在表格视图的底部。
所以我有一个对象数组SingleMessage
:
var messages = [SingleMessage]()
当用户向UITextView
添加文字时,我会发送包含Socket.IO
的邮件并收到该邮件:
func messageArrived(_ notification: Notification) {
if let message = (notification as NSNotification).userInfo?["message"] as? SingleMessage {
DispatchQueue.main.async(execute: { () -> Void in
messages.append(message)
self.tview.reloadData()
self.scrollToBottom()
)}
}
}
我的函数scrollToBottom()
包含以下代码:
if(self.messages.count > 0) {
let iPath = IndexPath(row: self.tview.numberOfRows(inSection: 0)-1, section: self.tview.numberOfSections-1)
self.tview.scrollToRow(at: iPath, at: UITableViewScrollPosition.bottom, animated: false)
}
然后我有cellForRow
函数,它可以执行很多操作,例如为每个标签设置字体和文本等。
override func tableView(_ tview: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tview.dequeueReusableCell(withIdentifier: "chat") as! SingleCommentCell
if let msg:SingleMessage = self.messages[(indexPath as NSIndexPath).row] as? SingleMessage {
.
.
.
我的问题是,当我输入内容时,立即按下send
按钮并再次开始输入 - 整个界面冻结了几秒钟,我甚至看不到键盘的反馈。我认为问题是表视图必须完全刷新。
我在聊天组件中使用上面的界面,因此问题不仅发生在用户连续快速输入多条消息时,而且还有许多传入消息时。
有没有办法加速整个界面,例如在表格视图的底部添加新单元格并避免刷新已存在的单元格?
与我的UITableViewController
相关的其他功能是:
override func tableView(_ tview: UITableView, numberOfRowsInSection section: Int) -> Int {
return messages.count
}
override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return UITableViewAutomaticDimension
}
然后我有:
override func viewDidLoad() {
super.viewDidLoad()
tview.separatorStyle = UITableViewCellSeparatorStyle.none
tview.backgroundColor = UIColor.clear
tview.delegate = self
tview.dataSource = self
self.tview.estimatedRowHeight = 100
NotificationCenter.default.addObserver(self, selector: #selector(ChatView.messageArrived(_:)), name: NSNotification.Name(rawValue: incomingMessage), object: nil)
}
答案 0 :(得分:1)
reloadData
是一项非常昂贵的操作。它重建整个表。当您想要执行这些操作并在更改时刷新单个行时,最好使用插入和删除行功能来更好地跟踪模型。
一个好的策略是保留旧模型,生成新模型,然后计算创建,移动或删除的项集,并为每个案例生成单独的表操作。以下是一些示例代码:
- (void) setDevicesForKey: (NSString *) propertyKey
toDevices: (NSArray *) newDevices
{
NSArray *currentDevices = [self valueForKey: propertyKey];
NSUInteger tableSection = [self sectionForKey: propertyKey];
NSIndexSet *indexesOfItemsToRemove = [currentDevices indexesOfObjectsPassingTest: ^BOOL(DeviceItem * itemToCheck, NSUInteger idx, BOOL *stop) {
return ![newDevices containsObject: itemToCheck];
}];
NSIndexSet *indexesOfItemsToAdd = [newDevices indexesOfObjectsPassingTest:^BOOL(DeviceItem *itemToCheck, NSUInteger idx, BOOL *stop) {
return ![currentDevices containsObject: deviceItem];
}];
UITableView *tableView = [self tableView];
[tableView beginUpdates];
{
NSMutableArray *removeIndexPaths = [NSMutableArray array];
[indexesOfItemsToRemove enumerateIndexesUsingBlock: ^(NSUInteger idx, BOOL *stop) {
[removeIndexPaths addObject: [NSIndexPath indexPathForItem: idx inSection: tableSection]];
}];
[tableView deleteRowsAtIndexPaths: removeIndexPaths withRowAnimation: UITableViewRowAnimationAutomatic];
NSMutableArray *insertIndexPaths = [NSMutableArray array];
[indexesOfItemsToAdd enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL *stop) {
[insertIndexPaths addObject: [NSIndexPath indexPathForItem: idx inSection: tableSection]];
}];
[tableView insertRowsAtIndexPaths: insertIndexPaths withRowAnimation: UITableViewRowAnimationAutomatic];
[newDevices enumerateObjectsUsingBlock: ^(DeviceItem *itemToCheck, NSUInteger idx, BOOL *stop) {
if([currentDevices containsObject: itemToCheck])
{
NSUInteger oldIndex = [currentDevices indexOfObject: ticketToCheck];
NSUInteger newIndex = [newDevices indexOfObject: ticketToCheck];
if(oldIndex != newIndex)
{
NSIndexPath *fromIndexPath = [NSIndexPath indexPathForRow: oldIndex inSection: tableSection];
NSIndexPath *toIndexPath = [NSIndexPath indexPathForRow: newIndex inSection: tableSection];
[tableView moveRowAtIndexPath: fromIndexPath toIndexPath: toIndexPath];
}
}
}];
[self setValue: newDevices forKey: propertyKey];
}
[tableView endUpdates];
}
答案 1 :(得分:1)
我建议使用insertRows(at
插入行而不是调用reloadData
,并且仅在单元格不可见时滚动。
func messageArrived(_ notification: Notification) {
if let message = notification.userInfo?["message"] as? SingleMessage {
DispatchQueue.main.async {
// if the index path is created before the item is inserted the last row is self.messages.count
let newIndexPath = IndexPath(row: self.messages.count, section: 0)
self.messages.append(message)
self.tableView.insertRows(at: [newIndexPath], with: .automatic)
if let visiblePaths = self.tableView.indexPathsForVisibleRows, !visiblePaths.contains(newIndexPath) {
self.tableView.scrollToRow(at: newIndexPath, at: .bottom, animated: false)
}
}
}
}
注意:
变量名称限制为8个字符已超过30年
像tview
这样的名字很难读懂。我在代码中使用tableView
。