2017-01-28 23 views
4

我正在爲我的應用程序實現一個簡單的信使,用戶可以在它們自己之間聊天。該Messenger是基於UICollectionView(JSQMessagesViewController),其中每個消息由一個UICollectionView行表示。每封郵件還有一個頂部標籤,用於顯示郵件的發送時間。這個標籤最初是隱藏的(height = 0),當用戶點擊特定的消息(行)時,通過相應地設置高度來顯示標籤。 (高度= 25)JSQMessagesView動畫行高變化的問題

我面臨的問題是顯示所述標籤的實際動畫。 (高度變化)。行的一部分在達到其位置之前將行下方的行覆蓋幾個像素。同樣,當隱藏標籤時,動畫首先將高度設置爲零,然後文本淡出覆蓋部分消息,該消息看起來非常糟糕。

所以基本上我試圖實現的是擺脫這兩個前面提到的問題。

代碼:

override func collectionView(_ collectionView: JSQMessagesCollectionView!, layout collectionViewLayout: JSQMessagesCollectionViewFlowLayout!, heightForCellTopLabelAt indexPath: IndexPath!) -> CGFloat { 
     if indexPath == indexPathTapped { 
      return 25 
     } 

     let messageCurrent = messages[indexPath.item] 
     let messagePrev: JSQMessage? = indexPath.item - 1 >= 0 ? messages[indexPath.item - 1] : nil 

     if messageCurrent.senderId == messagePrev?.senderId || messagePrev == nil { 
      return 0 
     } 
     else{ 
      return 25 

     } 
    } 

    override func collectionView(_ collectionView: JSQMessagesCollectionView!, didTapMessageBubbleAt indexPath: IndexPath!) { 
     if let indexPathTapped = indexPathTapped, indexPathTapped == indexPath { 
      self.indexPathTapped = nil 
     } 
     else{ 
      indexPathTapped = indexPath 
     } 

     collectionView.reloadItems(at: [indexPath]) 

//  UIView.animate(withDuration: 0.5, delay: 0.0, options: .curveLinear, animations: { 
//   collectionView.performBatchUpdates({ 
//    collectionView.reloadItems(at: [indexPath]) 
//   }, completion: nil) 
//  }, completion: nil) 

    } 

演示:(很抱歉的質量)

enter image description here

我真的很感激,如果有人可以幫助我,因爲我已經花了幾個小時試圖找出沒有得到任何地方。

預先感謝您!

編輯:

我嘗試作爲繼@jamesk提出的解決方案:

override func collectionView(_ collectionView: JSQMessagesCollectionView!, didTapMessageBubbleAt indexPath: IndexPath!) { 
     if let indexPathTapped = indexPathTapped, indexPathTapped == indexPath { 
      self.indexPathTapped = nil 
     } 
     else{ 
      indexPathTapped = indexPath 
     } 

     UIView.animate(withDuration: 0.25) { 
      collectionView.performBatchUpdates(nil) 
     } 
} 

,並覆蓋applyJSQMessagesCollectionViewCell的:

extension JSQMessagesCollectionViewCell { 

    override open func apply(_ layoutAttributes: UICollectionViewLayoutAttributes) { 
     super.apply(layoutAttributes) 
     layoutIfNeeded() 
    } 
} 

但是這些變化導致:

enter image description here

我也試圖與無效的佈局的第二溶液:

override func collectionView(_ collectionView: JSQMessagesCollectionView!, didTapMessageBubbleAt indexPath: IndexPath!) { 
     if let indexPathTapped = indexPathTapped, indexPathTapped == indexPath { 
      self.indexPathTapped = nil 
     } 
     else{ 
      indexPathTapped = indexPath 
     } 

     var paths = [IndexPath]() 
     let itemsCount = collectionView.numberOfItems(inSection: 0) 

     for i in indexPath.item...itemsCount - 1 { 
      paths.append(IndexPath(item: i, section: 0)) 
     } 


     let context = JSQMessagesCollectionViewFlowLayoutInvalidationContext() 
     context.invalidateItems(at: paths) 

     UIView.animate(withDuration: 0.25) { 
      self.collectionView?.collectionViewLayout.invalidateLayout(with: context) 
      self.collectionView?.layoutIfNeeded() 
     } 

    } 

這導致了以下內容:

enter image description here

回答

2

似乎有兩個問題。第一個問題是,對reloadItems(at:)的調用限於舊的單元格和新的單元格之間的交叉淡入淡出 - 它不會在舊單元格的佈局屬性和新單元格的佈局屬性之間進行插值。第二個問題是,在應用新的佈局屬性時,如果需要的話,似乎沒有任何代碼指示您選擇的單元格執行佈局傳遞。

JSQMessagesViewController框架使用UICollectionViewFlowLayoutUICollectionViewFlowLayoutInvalidationContext的子類,因此我們可以在更新和動畫項目時利用流佈局的失效行爲。所有需要的是使佈局屬性(即位置)無效,併爲受單元高度變化影響的項目委託指標(即大小)。

下面是爲與雨燕示例項目使用編寫的代碼包含在JSQMessagesViewController的release_7.3分支:

override func collectionView(_ collectionView: JSQMessagesCollectionView!, didTapMessageBubbleAt indexPath: IndexPath!) { 
    // Determine the lowest item index affected by the change in cell size. 
    // Lesser of previous tapped item index (if any) and current tapped item index. 
    let minItem = min(tappedIndexPath?.item ?? indexPath.item, indexPath.item) 

    // Update tapped index path. 
    tappedIndexPath = (tappedIndexPath == indexPath ? nil : indexPath) 

    // Prepare invalidation context spanning all affected items. 
    let context = JSQMessagesCollectionViewFlowLayoutInvalidationContext() 
    let maxItem = collectionView.numberOfItems(inSection: 0) - 1 
    let indexPaths = (minItem ... maxItem).map { IndexPath(item: $0, section: 0) } 

    context.invalidateItems(at: indexPaths) // Must include all affected items. 
    context.invalidateFlowLayoutAttributes = true // Recompute item positions (for all affected items). 
    context.invalidateFlowLayoutDelegateMetrics = true // Recompute item sizes (needed for tapped item). 

    UIView.animate(withDuration: 0.25) { 
     collectionView.collectionViewLayout.invalidateLayout(with: context) 
     collectionView.layoutIfNeeded() // Ensure layout pass for visible cells. 
    } 
} 

上面的代碼應該是合理的高性能。

雖然受影響的項目的位置必須始終重新計算,但沒有必要像上面所做的那樣重新計算所有受影響項目的大小。僅重新計算輕敲物品的尺寸就足夠了。但由於invalidateFlowLayoutDelegateMetrics屬性的效果總是應用於每個無效項目,爲了實現這種較窄的方法,您需要使用兩個流佈局無效上下文並將它們之間的項目分開(或者實現具有相應無效行爲的自定義無效上下文) 。除非儀器另有說明,否則這可能是不值得的。

+0

我試過了你提出的兩種解決方案,但是他們都沒有工作(請看編輯問題)。你有什麼想法出了什麼問題? – user3559787

+0

我嘗試過這些東西,但它仍然無法正常工作。 – user3559787

+0

我認爲最簡單的方法就是,如果你只是提供一個鏈接到你的例子(你已經有)。我會看到我做了什麼不同,並重新上傳到這裏。 – user3559787

0

插入數據嘗試添加這段代碼後。

collectionView.reloadItems(at: [indexPath]) 

UIView.animate(withDuration: 0.6) { 
    self.view.layoutIfNeeded() 
} 
+0

謝謝你的建議,但它沒有什麼區別。動畫仍然表現相同。 – user3559787