我正在为我的应用程序实现一个简单的信使,用户可以在其中聊天。信使基于UICollectionView (JSQMessagesViewController),其中每个消息都由一个UICollectionView行表示。每个邮件都有一个顶部标签,用于发送邮件时显示。这个标签最初是隐藏的(height=0),当用户点击特定的消息(行)时,标签通过设置相应的高度来显示。(height=25)
我面临的问题是显示标签的实际动画。(高度变化)。行的一部分在到达它的位置之前,将行下面的部分覆盖几个像素。同样,当隐藏标签回来时,动画首先将高度设置为零,然后文本淡出,覆盖了部分看起来非常糟糕的消息。
因此,基本上,我想要达到的是摆脱这两个前面提到的问题。
代码:
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)
}
演示:(对质量感到抱歉)
如果有人能帮我解决这个问题,我会非常感激的,因为我已经花了好几个小时试图找出答案,却什么也没有得到。
提前谢谢你!
编辑:
我尝试了@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)
}
}
并覆盖apply
of JSQMessagesCollectionViewCell
extension JSQMessagesCollectionViewCell {
override open func apply(_ layoutAttributes: UICollectionViewLayoutAttributes) {
super.apply(layoutAttributes)
layoutIfNeeded()
}
}
然而,这些变化导致:
我还尝试了第二个解决方案,使布局无效:
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()
}
}
其结果如下:
发布于 2017-02-01 19:49:00
似乎有两个问题。第一个问题是,对reloadItems(at:)
的调用仅限于旧小区和新小区之间的交叉衰落--它不会在旧小区的布局属性和新小区的布局属性之间进行插值。第二个问题是,似乎没有任何代码指示您选择的单元格在应用新的布局属性时执行布局传递。
JSQMessagesViewController框架使用了UICollectionViewFlowLayout和UICollectionViewFlowLayoutInvalidationContext的子类,因此在更新和动画项时,我们可以利用流布局的失效行为。所需做的就是使受单元高度变化影响的项的布局属性(即位置)和委托度量(即大小)失效。
下面的代码是为用于release_7.3
分支JSQMessagesViewController中包含的Swift示例项目而编写的:
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
属性的效果总是应用于每个失效项,为了实现更窄的方法,您需要使用两个流布局失效上下文并在它们之间进行划分(或者实现具有相应失效行为的自定义失效上下文)。除非仪器告诉你,否则这可能不值得。
发布于 2017-02-01 06:52:50
插入数据后,尝试添加这段代码。
collectionView.reloadItems(at: [indexPath])
UIView.animate(withDuration: 0.6) {
self.view.layoutIfNeeded()
}
https://stackoverflow.com/questions/41910371
复制