2017-05-03 44 views
0

在我的Swift應用程序中,我有一個UITableViewUITextView。這個想法很簡單,當用戶添加文本時 - 它應該出現在表格視圖的底部。追加新內容和刷新UITableView凍結我的應用程序在Swift中幾秒鐘

所以,我有我的目標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) 
} 
+1

誠實的問題。爲什麼要把這個標記爲Swift?這聽起來像很多**更廣泛。沒有什麼特別的語言。 – dfd

+0

這是一個快速的問題。沒有錯。 –

+0

不相關,但如果'messages'顯然是非可選的,爲什麼你可以將它綁定在'cellForRowAt'中?簡單地寫'讓msg = self.messages [indexPath.row]'不帶大括號。並且不要從(表格)視圖獲取行數,從'messages'數量中獲取。最後'DispatchQueue'閉包不會被編譯。 – vadian

回答

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]; 
} 
0

我建議用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

+0

中增加了更多的代碼感謝您的建議,我用你的代碼替換了我的方法'messageArrived'並運行了代碼。這是它現在的工作原理:https://gfycat.com/SlushyPoshHapuku我在這裏寫的非常快,一個'''''''''''''''''''''''''''等等,看看它是如何仍然掛在視圖:(你認爲我可能會改變一些其他的東西嗎?我也在'cell for row'中刪除了可選的綁定,並且留下了你在之前評論中提出的建議:'let msg = self.messages [indexPath.row]' – user3766930

+0

吊死的原因必須如果收到消息,立即插入表視圖行 – vadian

+0

vadian,你知道,現在我評論這個'if':'if let visiblePaths = self.tview.indexPathsForVisibleRows, !visiblePaths.contains(newIndexPath){'現在它運行平穩......唯一的問題是,當有新消息時,表格不滾動。所以問題似乎是滾動到底部 - 也許有一些其他方式呢? – user3766930