添加新内容并刷新UITableView会在Swift中冻结我的应用程序几秒钟

时间:2017-05-03 15:38:56

标签: ios swift uitableview swift3

在我的Swift应用中,我有UITableViewUITextView。这个想法很简单,当用户添加文本时 - 它应该出现在表格视图的底部。

所以我有一个对象数组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)
}

2 个答案:

答案 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