download image in tableView(_:, cellForRowAt:)

Viewed 1058

when i segue to a viewController with tableView, the tableviewcell immediately send a request to the server using the method fetchUserAvatar(avatarName: handler: (String) -> Void). and this method returns a url that link to the image. download it and cache the image cacheImage is an object of NSCache<NSString, UIImage>. this object cacheImage was initalised in previous view controller and being assigned from pervious to this viewContoller using prepare(for segue: UIStoryboardSegue, sender: Any?). when this viewController shows up, I can't see the image in the cell. but i pop the viewController out and segue to this viewController with tableView again. the image will show. I think(I guess) because the images weren't all fully downloaded yet. so, i can't see the images. but if i pop out the viewController and load an object of viewController and the viewController gets the images from cache. therefore, the images could be shown.

I want to know how to avoid this problem? thanks.

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: "MessageCell", for: indexPath) as! MessageCell
    let row = indexPath.row

    cell.content.text = messages[row].content
    cell.date.text = messages[row].createdDateStrInLocal
    cell.messageOwner.text = messages[row].user

    if let avatar = cacheImage.object(forKey: messages[row].user as NSString){
        cell.profileImageView.image = avatar
    } else {
        fetchUserAvatar(avatarName: messages[row].user, handler: { [unowned self] urlStr in
            if let url = URL(string: urlStr), let data = try? Data(contentsOf: url), let avatar = UIImage(data: data){
                self.cacheImage.setObject(avatar, forKey: self.messages[row].user as NSString)

                cell.profileImageView.image = avatar
            }
        })
    }
    return cell
}

fileprivate func fetchUserAvatar(avatarName: String, handler: @escaping (String) -> Void){
    guard !avatarName.isEmpty, let user = self.user, !user.isEmpty else { return }
    let url = URL(string: self.url + "/userAvatarURL")
    var request = URLRequest(url: url!)
    let body = "username=" + user + "&avatarName=" + avatarName
    request.httpMethod = "POST"
    request.setValue("application/x-www-form-urlencoded; charset=utf-8", forHTTPHeaderField: "Content-Type")
    request.httpBody =  body.data(using: .utf8)
    defaultSession.dataTask(with: request as URLRequest){ data, response, error in
        DispatchQueue.main.async {
            if let httpResponse = response as? HTTPURLResponse {
                if 200...299 ~= httpResponse.statusCode {
                    print("statusCode: \(httpResponse.statusCode)")
                    if let urlStr = String(data: data!, encoding: String.Encoding.utf8), urlStr != "NULL" {
                        handler(urlStr)
                    }
                } else {
                    print("statusCode: \(httpResponse.statusCode)")
                    if let unwrappedData = String(data: data!, encoding: String.Encoding.utf8) {
                        print("POST: \(unwrappedData)")
                        self.warning(title: "Fail", message: unwrappedData, buttonTitle: "OK", style: .default)
                    } else {
                        self.warning(title: "Fail", message: "unknown error.", buttonTitle: "OK", style: .default)
                    }
                }
            } else if let error = error {
                print("Error: \(error)")
            }
        }
        }.resume()
}

I modified it, and move the download code in viewdidload and reload the tableview, the result is the same.

1 Answers
Related