UICollectionView 在重新加载数据时滚动不平滑

Posted

技术标签:

【中文标题】UICollectionView 在重新加载数据时滚动不平滑【英文标题】:UICollectionView is not scrolling smooth on reload data 【发布时间】:2016-12-09 10:30:43 【问题描述】:

在我的 UICollecitonView 中,我正在使用 API 附加数据

self.DataTableCollectionView.reloadData()

但是随着我收藏的数据越来越多,它的滚动变得粗糙,并且在加载了一些页面后,向上或向下滚动变得非常困难。卡住了。

最终,当我尝试一次加载 1000 行时,滚动工作顺利,但当我尝试使用 API 一次分页和加载 50 行时,滚动变得困难。

谁能帮帮我解决什么问题?

我无法弄清楚出了什么问题? self.DataTableCollectionView.reloadData() 对此负责吗?

这是在滚动上加载数据的代码:

func scrollViewDidScroll(scrollView: UIScrollView) 

     let offsetY = scrollView.contentOffset.y
     let contentHeight = scrollView.contentSize.height

     if(offsetY > (contentHeight - scrollView.frame.size.height))              

         let seconds = 3.0
         let delay = seconds * Double(NSEC_PER_SEC)
         let dispatchTime = dispatch_time(DISPATCH_TIME_NOW, Int64(delay))

         self.loadMoreData()
         dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), 
             self.DataTableCollectionView.reloadData()

         )                
              


===========

我正在重复使用单元格

self.DataTableCollectionView .registerNib(UINib(nibName: "DCollectionViewCell", bundle: nil), forCellWithReuseIdentifier: dCellIdentifier)
self.DataTableCollectionView .registerNib(UINib(nibName: "CCollectionViewCell", bundle: nil), forCellWithReuseIdentifier: cCellIdentifier) 

以及重复使用:

func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell 

let dCell : DCollectionViewCell = collectionView .dequeueReusableCellWithReuseIdentifier(dCellIdentifier, forIndexPath: indexPath) as! DCollectionViewCell

let cCell : ContentCollectionViewCell = collectionView .dequeueReusableCellWithReuseIdentifier(cCellIdentifier, forIndexPath: indexPath) as! CCollectionViewCell       

=========

添加更具体的代码:

现在我已经更新了滚动功能,

func scrollViewDidScroll(scrollView: UIScrollView) 
    let offsetY = scrollView.contentOffset.y
    let contentHeight = scrollView.contentSize.height
    if(offsetY > (contentHeight - scrollView.frame.size.height))
       var returnTableData = NSMutableArray()

       self.InspectPageId = self.InspectPageId+1
       self.dataSource.populateData(PageId:self.InspectPageId)
       returnTableData = self.dataSource.DTableDataArray

       if(returnTableData.count>0)
          for i in 0..<returnTableData.count 
             self.TableDataArray.addObject(returnTableData[i])
          
       
       self.NumberOfRows = self.TableDataArray.count

       dispatch_async(dispatch_get_main_queue(),
          self.DataTableCollectionView.reloadData()
       )
   

以下是手动创建集合视图的代码:

if indexPath.row == 0 
//Here Header Cell is creating
                let dateCell : DateCollectionViewCell = collectionView .dequeueReusableCellWithReuseIdentifier(dateCellIdentifier, forIndexPath: indexPath) as! DateCollectionViewCell
                dateCell.dateLabel.font = UIFont(name:"HelveticaNeue", size: 12)
                dateCell.dateLabel.textColor = UIColor.blackColor()
                dateCell.dateLabel.textAlignment = .Left
                dateCell.dateLabel.text = UMIDDataArray[rowIndex-1] as? String
                dateCell.backgroundColor = UIColor.whiteColor()
                dateCell.layer.shouldRasterize = true
                dateCell.layer.rasterizationScale = UIScreen.mainScreen().scale
                return dateCell
             else 
//Here Data Cell is Creating
                let contentCell : ContentCollectionViewCell = collectionView .dequeueReusableCellWithReuseIdentifier(contentCellIdentifier, forIndexPath: indexPath) as! ContentCollectionViewCell
                contentCell.contentLabel.font = UIFont(name:"HelveticaNeue", size: 12)
                contentCell.contentLabel.textColor = UIColor.blackColor()
                contentCell.contentLabel.textAlignment = .Left
                contentCell.bottomBorder.hidden  = false
                contentCell.layer.shouldRasterize = true
                contentCell.layer.rasterizationScale = UIScreen.mainScreen().scale

                let LensData:NSArray =  TableDataArray[rowIndex-1] as! NSArray
                let ColumnValue = LensData[colIndex-1] as? String
                contentCell.contentLabel.text = ColumnValue
                contentCell.backgroundColor = UIColor.whiteColor()

                return contentCell
            

Following is the code which is use to take data from API:

//function of DataSource Class.
func populateData(PageId:Int)
var returnData = NSMutableArray()
let OtherColobjects = NSMutableArray()

let Urlpath = SetApiUrl()
let url: NSURL = NSURL(string: Urlpath)!
let urlRequest = NSURLRequest(URL: url)

let config = NSURLSessionConfiguration.defaultSessionConfiguration()
let session = NSURLSession(configuration: config)

let semaphore = dispatch_semaphore_create(0)
let task = session.dataTaskWithRequest(urlRequest) 
(data, response, error) in

guard error == nil else 
return


guard let responseData = data else 
return


do 
guard let Data = try NSJSONSerialization.JSONObjectWithData(responseData, options: []) as? [String: AnyObject] else 
return


for anItem in Data["result"]!["DATA"] as! [Dictionary<String, AnyObject>] 
let d1 = self.checkForNull(anItem["data1"]!)
let d2 = self.checkForNull(anItem["data2"]!)
let d3 = self.checkForNull(anItem["data3"]!)
let d4 = self.checkForNull(anItem["data4"]!)
...... // Total 20 Columns

let obj = [ d1, d2, d3, d4,.............,d20 ]

OtherColobjects.addObject(obj)

dispatch_semaphore_signal(semaphore)
 catch 
return


task.resume()

dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER)
self.NumberOfRows = returnData[0].count
self.DTableDataArray = returnData[1] as! NSMutableArray

【问题讨论】:

你在重复使用你的collectionview单元格吗? 是的,我正在重用您的收藏视图。 我在问题中添加了代码。 你在缓存图片吗?? 【参考方案1】:

您应该只从主线程更新您的 UI,所以改为这样做...

Swift 2.x

           dispatch_async(dispatch_get_main_queue()), 
                self.DataTableCollectionView.reloadData()
            

斯威夫特 3

DispatchQueue.main.async 
    self.DataTableCollectionView.reloadData()

如果这不能解决问题,那么您可能还需要在后台线程上调用 loadMoreData()...

Swift 2.x

        dispatch_async(dispatch_get_global_queue(QOS_CLASS_UTILITY, 0))
             self.loadMoreData()
        

斯威夫特 3

DispatchQueue.global(qos: DispatchQoS.QoSClass.default).async 
     self.loadMoreData()

由于您没有向我们展示您的 loadMoreData() 函数,因此不清楚您的代码究竟是如何工作的。实际上,这似乎就是您可能打算从 loadMoreData 内部调用 DataTableCollectionView.reloadData() ,但如果没有看到该功能,我无法判断。请添加附加代码

【讨论】:

您还没有添加 'loadMoreData()' 函数,并且从 'scrollViewDidScroll' 中删除了对 'loadMoreData()` 的调用 是的,我已经更新了我的问题,我已经附加了更新的“scrollViewDidScroll()”函数,现在没有更多的“loadMoreData()”函数和 loadMoreData() 函数的代码被添加“scrollViewDidScroll()”函数【参考方案2】:

您应该改为在MainThread 上发送reloadData()。根据苹果文档,操作 UI 应始终在 MainThread 中完成。

【讨论】:

【参考方案3】:

在出列单元格后添加以下 2 行,

cell.layer.shouldRasterize = YES;
cell.layer.rasterizationScale = [UIScreen mainScreen].scale;

【讨论】:

我已经添加了这两行以及collectionview的prefetch方法。谢谢朋友的回答。 :)【参考方案4】:

如果 self.loadMoreData() 不是异步的,它会阻塞你的 UI。 self.DataTableCollectionView.reloadData() 应该在主线程上从 self.loadMoreData() 调用。

即使这没有阻塞,您也会在每页多次调用loadMoreData()。 你应该添加一个布尔值来检查你是否已经在加载数据,我们称之为isLoading。 初始化视图控制器时将 isLoading 设置为 false。 在self.loadMoreData() 中将其设置为true,并在scrollViewDidScroll(scrollView: UIScrollView) 中更改检查,如下所示:

if(offsetY > (contentHeight - scrollView.frame.size.height)) && !isLoading  
    self.loadMoreData()

【讨论】:

【参考方案5】:

在主队列中重新加载搭配

dispatch_async(dispatch_get_main_queue(),
   self.DataTableCollectionView.reloadData()

【讨论】:

【参考方案6】:

尝试将你的重载 collectionview 从 scrollviewdidscroll 移动到 loadmoredata()

函数加载更多数据()

/* 1.列表项 2.最后再调用realoadata 3.如果单元格中有图像,则使用图像缓存,您可以对图像缓存进行编程或使用SDWebImage之类的库 */

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), self.DataTableCollectionView.reloadData())

【讨论】:

以上是关于UICollectionView 在重新加载数据时滚动不平滑的主要内容,如果未能解决你的问题,请参考以下文章

UICollectionView 不重新加载数据

在 UICollectionView 中重新加载数据的正确方法是啥?

如何在 UICollectionView 中加载更多数据而不重新加载所有数据

在 UICollectionView 中重新加载数据后保持 collectionview 速度

重新加载数据时的 UICollectionView 奇怪的动画(幽灵单元、flash)

使用 UICollectionViewDiffableDataSource 时如何重新加载 UICollectionView 中的部分数据?