异步获取数据并将其放入 UICollectionView 失败 - 集合在数据之前加载并且不更新

Posted

技术标签:

【中文标题】异步获取数据并将其放入 UICollectionView 失败 - 集合在数据之前加载并且不更新【英文标题】:fetching data asynchronously and putting it in UICollectionView fails - collection loads before the data and does not update 【发布时间】:2016-04-09 11:47:58 【问题描述】:

我的问题与我的其他问题部分相关Strange behavior when user scrolls the list of UIViewCollectionCells in my app (the data in each cell changes randomly)

在我的应用中,我使用的是UICollectionView。每个单元格都包含一张照片和一个用户名。

但首先要做的是——我从我的网络服务中获取一个带有该用户名和照片链接的 json 文件。我将这两个值都存储在一个名为SingleUser 的对象中。它看起来像这样:

class SingleUser: NSObject 

let username: String
let photo: String

var image: UIImage? = UIImage()

init(username: String, photo: String) 
    self.username = username
    self.photo = photo


class func fromJSON(json: JSON) -> SingleUser? 
    let username = json["username"].string
    let photo = json["photo"].string

    return SingleUser(username: username!, photo: photo!)


根据我上一个问题的建议,我修改了我的代码,我的主类使用UICollectionView 如下所示:

class UserList: UIViewController, UICollectionViewDataSource, UICollectionViewDelegate 

@IBOutlet weak var tview: UICollectionView!
let reuseIdentifier = "cell" 
var items = NSMutableArray()

override func viewWillAppear(animated: Bool) 
    super.viewWillAppear(animated)

    fetchAllUsers()
    tview.delegate = self
    tview.dataSource = self



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

    let cell = tview.dequeueReusableCellWithReuseIdentifier(reuseIdentifier, forIndexPath: indexPath) as! MyCollectionViewCell

    let user:SingleUser =  self.items[indexPath.item] as! SingleUser   

    cell.username.text = user.name

    if user.image == nil

        if let checkedUrl = NSURL(string: user.photo) 
            cell.userImg.contentMode = .ScaleAspectFit

            getDataFromUrl(checkedUrl)  (data, response, error)  in
                dispatch_async(dispatch_get_main_queue())  () -> Void in
                    guard let data = data where error == nil else  return 

                    let image  = UIImage(data: data)
                    user.image = image

                    if collectionView.cellForItemAtIndexPath(indexPath) == cell
                    
                        cell.userImg.image = image
                    

                
            

        else
            cell.userImg.image = user.image
        

    return cell

所以现在 - 希望 - 每次用户滚动视图时数据都不会改变。但是这里有一个主要问题 - 当用户进入面板时,collectionView 变得可见,但所有照片都无法快速加载。所以用户只看到用户名和照片的地方是空的。

这是因为我使用在viewWillAppear中调用的方法fetchAllUsers异步加载数据引起的:

func fetchAllUsers()

    Alamofire.request(.GET, "http://mywebservice")
        .responseJSON  response in

            switch response.result 
            case .Success:
                dispatch_async(dispatch_get_main_queue(),
                    self.items.removeAllObjects()
                    if let jsonData = response.result.value as? [[String: AnyObject]] 
                        for requestJSON in jsonData 
                            if let request = SingleUser.fromJSON(JSON(requestJSON))
                                self.items.addObject(request)
                                self.tview.reloadData()


                            
                        
                    
                    self.tview.reloadData()
                )

            case .Failure(let error):
                print("SWITCH ERROR")
                print(error)

            

    

另外,我上面使用的另一个异步方法是:

func getDataFromUrl(url:NSURL, completion: ((data: NSData?, response: NSURLResponse?, error: NSError? ) -> Void)) 
    NSURLSession.sharedSession().dataTaskWithURL(url)  (data, response, error) in
        completion(data: data, response: response, error: error)
        .resume()

所以这里有很多异步调用会导致在视图出现之前显示缺少数据的问题。

我想避免这种情况,而是 - 例如,当尚未加载每张照片时 - 显示加载指示符,该指示符会在获取照片时消失。我知道可能有必要在我的代码中进行大量修改,但是我可以在这里向您寻求任何建议吗?谢谢!

【问题讨论】:

【参考方案1】:

实际上,每当下载新数据时,您都需要重新加载集合数据。

执行此操作的最佳位置可能是在“getDataFromURL”的闭包中,在此闭包中执行以下操作:

dispatch_async(dispatch_get_main_queue()) 
  tview.reloadData()

要获得加载图像,只需将您需要的图像放在用户图像后面的原型单元格中。在显示用户的图像之前,这将是可见的。

【讨论】:

hm 我不太确定代码中的位置,你的意思是把它放在这里:completion(data: data, response: response, error: error) dispatch_async(dispatch_get_main_queue()) self.tview.reloadData() .resume() 它应该在那里工作,但显然你想确保首先检查错误 好的,我这样做了,但是我刚刚意识到在我的代码中我只输入了这个语句:else cell.userImg.image = user.image ,而不是 if user.image == nil 块,即使我在 @987654325 中定义了 UIImage @作为一个空的var image: UIImage? = UIImage()。你知道为什么我根本不输入if 语句而是总是转到else 吗? 你可能把 else 放在了错误的地方。您的 else 语句实际上连接到“ if let checkedUrl = NSURL(string: user.photo) ” 而不是“if user.image == nil”。要更正此问题,您只需将 else 语句移到一个括号中即可。 (如果您不知道,您可以通过双击任何 来查看语句的结束位置)

以上是关于异步获取数据并将其放入 UICollectionView 失败 - 集合在数据之前加载并且不更新的主要内容,如果未能解决你的问题,请参考以下文章

如何仅获取一列 Pandas 数据框(无索引)并将其放入双端队列?

Python:如何创建一个函数来从 url 获取数据并将其异步发送到其他位置?

使用 Afnetworking 获取 UIImageView 并将其放入数组中

Pandas 如何从 JSON 索引列表并将其放入数据框中?

一次获取数据并放入两个不同的 div

获取IP地址并将其放入socket -java