我有一个CollectionView
,它根据message
类型(例如,文本,图像)使一个单元出队。
我遇到的问题是,当我向上/向下滚动时,滚动确实非常不稳定,因此用户体验不是很好。这仅在第一次加载单元格之后发生滚动很流畅。
有什么想法可以解决此问题吗?这可能与在显示单元格之前获取数据的时间有关吗?
我对在后台线程等上运行任务不太熟悉,并且不确定为完成数据预取/获取等可以进行哪些更改。请帮忙!
加载视图时,Gif显示向上滚动,当我尝试向上滚动时,它显示单元格/视图不连贯。
这是我的func loadConversation()
,它加载了messages
数组
func loadConversation(){
DataService.run.observeUsersMessagesFor(forUserId: chatPartnerId!) { (chatLog) in
self.messages = chatLog
DispatchQueue.main.async {
self.collectionView.reloadData()
if self.messages.count > 0 {
let indexPath = IndexPath(item: self.messages.count - 1, section: 0)
self.collectionView.scrollToItem(at: indexPath, at: .bottom , animated: false)
}
}
}//observeUsersMessagesFor
}//end func
这是我的cellForItemAt,它使单元出队
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let message = messages[indexPath.item]
let uid = Auth.auth().currentUser?.uid
if message.fromId == uid {
if message.imageUrl != nil {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "ConversationCellImage", for: indexPath) as! ConversationCellImage
cell.configureCell(message: message)
return cell
} else {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "ConversationCellSender", for: indexPath) as! ConversationCellSender
cell.configureCell(message: message)
return cell
}//end if message.imageUrl != nil
} else {
if message.imageUrl != nil {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "ConversationCellImageSender", for: indexPath) as! ConversationCellImageSender
cell.configureCell(message: message)
return cell
} else {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "ConversationCell", for: indexPath) as! ConversationCell
cell.configureCell(message: message)
return cell
}
}//end if uid
}//end func
这是我的ConversationCell
类,它配置用于通过cellForItemAt
出队的自定义单元格(注意:此外,还有另一个ConversationCellImage
自定义单元格类,用于配置图像消息):
class ConversationCell: UICollectionViewCell {
@IBOutlet weak var chatPartnerProfileImg: CircleImage!
@IBOutlet weak var messageLbl: UILabel!
override func awakeFromNib() {
super.awakeFromNib()
chatPartnerProfileImg.isHidden = false
}//end func
func configureCell(message: Message){
messageLbl.text = message.message
let partnerId = message.chatPartnerId()
DataService.run.getUserInfo(forUserId: partnerId!) { (user) in
let url = URL(string: user.profilePictureURL)
self.chatPartnerProfileImg.sd_setImage(with: url, placeholderImage: #imageLiteral(resourceName: "placeholder"), options: [.continueInBackground, .progressiveDownload], completed: nil)
}//end getUserInfo
}//end func
override func layoutSubviews() {
super.layoutSubviews()
self.layer.cornerRadius = 10.0
self.layer.shadowRadius = 5.0
self.layer.shadowOpacity = 0.3
self.layer.shadowOffset = CGSize(width: 5.0, height: 10.0)
self.clipsToBounds = false
}//end func
override func preferredLayoutAttributesFitting(_ layoutAttributes: UICollectionViewLayoutAttributes) -> UICollectionViewLayoutAttributes {
//toggles auto-layout
setNeedsLayout()
layoutIfNeeded()
//Tries to fit contentView to the target size in layoutAttributes
let size = contentView.systemLayoutSizeFitting(layoutAttributes.size)
//Update layoutAttributes with height that was just calculated
var frame = layoutAttributes.frame
frame.size.height = ceil(size.height) + 18
layoutAttributes.frame = frame
return layoutAttributes
}
}//end class
时间配置文件结果:
编辑:布局代码
if let flowLayout = self.collectionView.collectionViewLayout as? UICollectionViewFlowLayout,
let collectionView = collectionView {
let w = collectionView.frame.width - 40
flowLayout.estimatedItemSize = CGSize(width: w, height: 200)
}// end if-let
编辑:我的自定义单元格类中的preferredLayoutAttributesFitting
函数
override func preferredLayoutAttributesFitting(_ layoutAttributes: UICollectionViewLayoutAttributes) -> UICollectionViewLayoutAttributes {
//toggles auto-layout
setNeedsLayout()
layoutIfNeeded()
//Tries to fit contentView to the target size in layoutAttributes
let size = contentView.systemLayoutSizeFitting(layoutAttributes.size)
//Update layoutAttributes with height that was just calculated
var frame = layoutAttributes.frame
frame.size.height = ceil(size.height) + 18
layoutAttributes.frame = frame
return layoutAttributes
}
解决方案:
extension ConversationVC: UICollectionViewDelegateFlowLayout{
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
var height: CGFloat = 80
let message = messages[indexPath.item]
if let text = message.message {
height = estimateFrameForText(text).height + 20
} else if let imageWidth = message.imageWidth?.floatValue, let imageHeight = message.imageHeight?.floatValue{
height = CGFloat(imageHeight / imageWidth * 200)
}
let width = collectionView.frame.width - 40
return CGSize(width: width, height: height)
}
fileprivate func estimateFrameForText(_ text: String) -> CGRect {
let size = CGSize(width: 200, height: 1000)
let options = NSStringDrawingOptions.usesFontLeading.union(.usesLineFragmentOrigin)
return NSString(string: text).boundingRect(with: size, options: options, attributes: [NSAttributedStringKey.font: UIFont.systemFont(ofSize: 16)], context: nil)
}
}//end extension
答案 0 :(得分:1)
首先,让我们尝试查找出现此问题的确切位置。
尝试1:
评论此行
//self.chatPartnerProfileImg.sd_setImage(with: url, placeholderImage: #imageLiteral(resourceName: "placeholder"), options: [.continueInBackground, .progressiveDownload], completed: nil)
并运行您的应用以查看结果。
尝试2:
将该行放入异步块以查看结果。
DispatchQueue.main.async {
self.chatPartnerProfileImg.sd_setImage(with: url, placeholderImage: #imageLiteral(resourceName: "placeholder"), options: [.continueInBackground, .progressiveDownload], completed: nil)
}
尝试3:注释设置角半径的代码
/*self.layer.cornerRadius = 10.0
self.layer.shadowRadius = 5.0
self.layer.shadowOpacity = 0.3
self.layer.shadowOffset = CGSize(width: 5.0, height: 10.0)
self.clipsToBounds = false*/
分享您尝试1、2和3的结果,然后我们可以更好地了解问题所在。
希望通过这种方式,我们可以了解闪烁的原因。
答案 1 :(得分:0)
始终确保不要将图像或GIF之类的数据下载到主线程上。
这就是您的滚动不流畅的原因。使用GCD或NSOperation队列在后台在单独的线程中下载数据。然后始终在主线程上显示下载的图像。
使用AlamofireImage窗格,它将自动在后台处理下载的任务。
import AlamofireImage
extension UIImageView {
func downloadImage(imageURL: String?, placeholderImage: UIImage? = nil) {
if let imageurl = imageURL {
self.af_setImage(withURL: NSURL(string: imageurl)! as URL, placeholderImage: placeholderImage) { (imageResult) in
if let img = imageResult.result.value {
self.image = img.resizeImageWith(newSize: self.frame.size)
self.contentMode = .scaleAspectFill
}
}
} else {
self.image = placeholderImage
}
}
}
答案 2 :(得分:0)
我认为您看到的断断续续是因为给了单元格一个大小,然后覆盖了它们给定的大小,这导致布局四处移动。您需要做的是在第一次创建布局期间进行计算。
我有一个像这样使用过的功能...
func height(forWidth width: CGFloat) -> CGFloat {
// do the height calculation here
}
然后,布局可使用它来首次确定正确的尺寸,而不必更改它。
您可以将其作为静态方法放在单元格或数据上……或其他东西。
它需要做的是创建一个单元(而不是出队,只创建一个单元),然后填充其中的数据。然后做调整大小的东西。然后在进行第一遍布局时在布局中使用该高度。
您之所以动摇,是因为该集合将高度为20的单元格布局(例如),然后根据该位置计算出一切所需的位置……然后您开始……“实际上,应该是38”现在您必须为集合赋予不同的高度,然后它才能移动所有东西。然后,每个单元格都会发生这种情况,因此会导致断断续续。
如果我能看到您的布局代码,我也许可以提供更多帮助。
编辑
应该使用委托方法preferredAttributes
来代替使用func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize
方法。
做这样的事情...
此方法进入视图控制器。
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let message = messages[indexPath.item]
let height: CGFloat
if let url = message.imageURL {
height = // whatever height you want for the images
} else {
height = // whatever height you want for the text
}
return CGSize(width: collectionView.frame.width - 40, height: height)
}
您可能需要添加此内容,但这会给您一个想法。
完成此操作后...从单元格中删除所有代码,以更改框架和属性等。
还要...将您的影子代码放入awakeFromNib
方法中。