【问题标题】:fetching data asynchronously and putting it in UICollectionView fails - collection loads before the data and does not update异步获取数据并将其放入 UICollectionView 失败 - 集合在数据之前加载并且不更新
【发布时间】: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()
}

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

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

【问题讨论】:

    标签: ios swift uicollectionview uicollectionviewcell uicollectionreusableview


    【解决方案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 语句移到一个括号中即可。 (如果您不知道,您可以通过双击任何 { 来查看语句的结束位置)
    猜你喜欢
    • 2020-02-20
    • 1970-01-01
    • 2019-04-13
    • 1970-01-01
    • 2020-02-09
    • 1970-01-01
    • 1970-01-01
    • 2012-10-02
    • 2018-09-29
    相关资源
    最近更新 更多