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!