0

My problem is partially related to my other question here Strange behavior when user scrolls the list of UIViewCollectionCells in my app (the data in each cell changes randomly)

In my app I'm using UICollectionView. Each cell contains a photo and an username.

But first things first - I'm fetching a json file from my webservice with that username and a link to a photo. I store both values in an object called SingleUser. It looks like this:

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!)
}
}

Following advice from my previous question I modified my code and my main class with UICollectionView looks as follows:

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
}

so now - hopefully - the data does not change each time user scrolls the view. But there is a major problem here - when user enters the panel, collectionView gets visible, but all photos don't manage to load that fast. So user only sees usernames and places for photos are empty.

It is caused because I load data asynchronously with a method fetchAllUsers called in viewWillAppear:

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)

            }

    }
}

Also, the other async method that I'm using above is:

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()
}

So it's a lot of async calls here that causes problems with displaying lack of data before the view appears.

I want to avoid this situation and instead - for example when each photo is not loaded yet - show for example loading indicator that disappears when the photo gets fetched. I'm aware it might be necessary to put lots of modification in my code, but could I ask you for any kind of advice here? Thanks!

Community
  • 1
  • 1
user3766930
  • 5,629
  • 10
  • 51
  • 104

1 Answers1

2

Effectively you need reload the collection data whenever new data is downloaded.

The best place to do this would likely be in the closure of the "getDataFromURL", within this closure do the following:

dispatch_async(dispatch_get_main_queue()) {
  tview.reloadData()
}

To have a loading image, simply put the image you require in the prototype cell behind the user image. This will be visible until the user's image is displayed.

Ben Sullivan
  • 2,134
  • 1
  • 18
  • 32
  • hm I'm not exactly sure about the place in the code, do you mean to put this here: `completion(data: data, response: response, error: error) dispatch_async(dispatch_get_main_queue()) { self.tview.reloadData() } }.resume()` ? – user3766930 Apr 09 '16 at 12:14
  • It should work there but obviously you want to make sure you check for errors first – Ben Sullivan Apr 09 '16 at 12:16
  • ok, I did so, however I just realized that in my code I'm only entering this statement: `else{ cell.userImg.image = user.image }` and never the `if user.image == nil{` block, even though I defined the UIImage in `SingleUser` as an empty one `var image: UIImage? = UIImage()`. Do you have any clue why am I not entering the `if` statement at all and always go to the `else`? – user3766930 Apr 09 '16 at 12:22
  • 1
    You may have the else in the wrong place. Your else statement is actually connected to " if let checkedUrl = NSURL(string: user.photo) {" rather than "if user.image == nil{". To correct this you can just move the else statement down one bracket. (if you weren't aware you can see where the statements end by double clicking on any { ) – Ben Sullivan Apr 09 '16 at 13:25