I've an avatar image in cells in a table. When I touch a cell (highlight) it shows default avatar image instead of the actual image. I'm not sure what is causing this and how to fix. Any help?
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCell(withIdentifier: "TableViewCell", for: indexPath) as? TableViewCell else {
fatalError("Can't find cell")
}
//...
cell.selectionStyle = .default
self.configCell(cell: cell, indexPath: indexPath)
//...
}
func configCell(cell: TableViewCell, indexPath: IndexPath) {
//...
// Avatar
if let url = URL(string: urlString) {
cell.avatarImageView.image = .defaultImage
cell.tag = indexPath.row
let task = URLSession.shared.dataTask(with: url) { data, response, error in
guard let data = data, error == nil else { return }
DispatchQueue.main.async {
if cell.tag == indexPath.row {
cell.avatarImageView.image = UIImage(data: data)
}
}
}
task.resume()
}
//...
}
If you have an issue only with proper image displaying, I'd suggest working with highlightedImage property of UIImageView.
UITableViewCell has .highlighted property (when the cell is pressed down). Thus, if the cell contains UIImageView inside, then when you select/highlight the cell, the UIImageView will use the .highlightedImage instead of just .image.
So just as a backup and fix to the problem, you could additionally provide/tell UIImageView to display an avatar even when it is highlighted.
Related
I try to update an ImageView witch is part of my TableViewCell. The each cell has other countries, and for each cell I want to download the county's flag and show it in the cell´s ImageView, so I use CellForRowAt, ready the cell´s country and call a function which downloads the image of the flag. But I don't get, how I can update the ImageView in the Cell...
Here is my code:
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! CustomCell
// Configure the cell...
let stock = stocks[indexPath.row]
// Image downloading
loadCountryImage(country: stock.country)
return cell
}
func loadCountryImage(country: String) {
let url = "https://www.countryflags.io/\(country)/shiny/24.png"
guard let imageURL = URL(string: url) else {
print("no URL found")
return
}
let session = URLSession.shared
session.dataTask(with: imageURL) { imageData, _, _ in
let image = UIImage(data: imageData!)
}.resume()
So now the image is downloaded successfully, but how do I get it in the imageView of the cell?
Kind regards from Germany!
Yannik
The short answer is to pass the cell to loadCountryImage so that you can update the cell image in the closure from your data task. This update needs to be dispatched onto the main queue.
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! CustomCell
// Configure the cell...
let stock = stocks[indexPath.row]
cell.imageView.image = somePlaceholderImage
// Image downloading
loadCountryImage(country: stock.country, in: cell)
return cell
}
func loadCountryImage(country: String, in cell:CustomCell) {
let url = "https://www.countryflags.io/\(country)/shiny/24.png"
guard let imageURL = URL(string: url) else {
print("no URL found")
return
}
let session = URLSession.shared
session.dataTask(with: imageURL) { imageData, _, _ in
guard let data = imageData, let image = UIImage(data: data) else {
return
}
DispatchQueue.main.async {
cell.imageView.image = image
}
}.resume()
}
The long answer is that you should consider caching and since cells are reused, what happens when the table view scrolls? You may fetch an image that is out of date. One approach is to store the country in the cell and check in the closure to see it is still what you expect before you set the image.
You can handle some of this yourself using UITableviewDatasourcePrefetching and NSCache
var imageCache = NSCache<NSString,UIImage>()
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! CustomCell
// Configure the cell...
let stock = stocks[indexPath.row]
cell.imageView.image = somePlaceholderImage
cell.country = stock.country
// Image downloading
loadCountryImage(country: stock.country, in: cell)
return cell
}
func tableView(_ tableView: UITableView, prefetchRowsAt indexPaths: [IndexPath]) {
for indexPath in indexPaths {
let stock = stocks[indexPath.row]
if self.cache.object(forKey: stock.country) == nil {
self.loadCountryImage(country: stock.country, in: nil)
}
}
}
func loadCountryImage(country: String, in cell:CustomCell?) {
if let image = self.imageCache.object(forKey: country) {
cell?.imageView.image = image
return
}
let url = "https://www.countryflags.io/\(country)/shiny/24.png"
guard let imageURL = URL(string: url) else {
print("no URL found")
return
}
let session = URLSession.shared
session.dataTask(with: imageURL) { imageData, _, _ in
guard let data = imageData, let image = UIImage(data: data) else {
return
}
DispatchQueue.main.async {
self.imageCache.setObject(image, forKey: country)
if cell?.country == country {
cell?.imageView.image = image
}
}
}.resume()
}
A better answer is probably to look at frameworks like SDWebImage or Kingfisher that do a lot of this for you.
With Existing solution you can pass cell to to your function to set image image when download. but this approach leads to performance issue while scrolling your TableView, because cells are reusing and your cellForRowAtIndexPath called each time when that specific row will be visible in mobile screen window and your download function also got triggered. As cell are reusing you also encounter problem of display same image in multiple cell. the better solution is to use SDWebImage form cocoaPod. you only have to focus on development . rest will be manage by SDWebImage i.e performance ,cache etc
Add SDWebImage image in pod file
pod 'SDWebImage'
Install pod by running
pod install
import SDWebImage image in your viewController
import SDWebImage
set image in cellforRowAt by
cell.yourImageViewInCell.sd_setImage(with: URL(string: "http://youserver.com/path/to/image.jpg"), placeholderImage: UIImage(named: "placeholder.png"))
My JsonData -
let imagestring : String? = (myData as AnyObject).value(forKey: "Post_mid_image") as? String
if imagestring != nil {
let imageTrueString = "https://www.zdoof.com/" + imagestring!
self.imageStringArray.append(imageTrueString )
}
if let NameString = (myData as AnyObject).value(forKey: "Name") as? String {
self.nameStringArray.append(NameString)
}
When i am trying to set it to the table view cell
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.postLableArray.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "reUse", for: indexPath)
let myImage = cell.viewWithTag(30) as! UIImageView
myImage.clipsToBounds = true
if indexPath.row < imageStringArray.count {
if let myImageString = imageStringArray[indexPath.row] as? String {
let ImageUrl = URL.init(string: myImageString)
myImage.kf.setImage(with: ImageUrl)
}
}
return cell
}
The image is repeating in every cell . Why it is happening ? Please help
As per the response you have given, you can show the image like below:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let dict = myData as AnyObject
let cell = tableView.dequeueReusableCell(withIdentifier: "reUse", for: indexPath)
let myImage = cell.viewWithTag(30) as! UIImageView
if dict["Post_mid_image"] != nil {
let imageUrl = URL.init(string: strImageUrl)
myImage.kf.setImage(with: imageUrl)
} else {
//Set placeholder image showing no image available
}
return cell
}
Problem is with cell re-usablity of table view here ,you have to handle it , you can have SDWebImage library for loading images in cell or you can have your own image cache which caches images with key/values , key as in image url , so dynamically checking image url for item at indexpath and load cached image with that image url as key.
Hope it helps!!
This is happening because of using tableView.dequeueReusableCell(withIdentifier: "reUse", for: indexPath).
Basically whenever you use dequeueReusableCell(withIdentifier:,For:), it will use the same cell for all of data. It means the total number of cell which are on screen are only going to load, for all other cell, it will use same cell with different value.
now consider a scenario that you are having 500 cells in tableview, but we can manage at most 10-15 cells in display, so for all other cells it will use same cells just modify the value of cell.
so what you can do here is, whenever you use if statement, don't forgot to add else too.
because for one scenario if cell's background is set to red, than we need to add else for another scenario, as cells are just repeated.
I have a tableView that contains a UIImageView and if the Image has a URL then the image displays and if not then no Image is displayed. The issue I have is that if there is no Image then a big blank spot occurs in the TableView as if there was an image. Is there a way to reduce the blank spot or hide it (Images below) ? The image is the big UIImageView in the center . This is my code when it comes to the Images
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "MyFeed", for: indexPath) as! MyFeed
if stream_image_string[indexPath.row].characters.count > 2 {
let strCellImageURL = self.stream_image_string[indexPath.row]
let imgURL: NSURL = NSURL(string: strCellImageURL)!
let request:NSURLRequest = NSURLRequest(url: imgURL as URL)
let config = URLSessionConfiguration.default
let session = URLSession(configuration: config)
let task = session.dataTask(with: request as URLRequest, completionHandler: {(data, response, error) in
DispatchQueue.main.async(execute: { () -> Void in
cell.post_image.image = UIImage(data: data!)
})
});
task.resume()
} else {
cell.post_image!.isHidden = true
cell.post_image!.image = nil
}
return cell
}
Essentially if the String coming back has 2 or more characters then it's a valid URL and the image is downloaded; the part that I am focused on is the else statement and this code
else {
cell.post_image!.isHidden = true
cell.post_image!.image = nil
}
So obviously if it goes in the else statement then there is no image and I set the Image to null or nil then I try to hide the extra white space by setting the Image to hidden however that does not work . Any idea on how I can hide the white space ? I have also been reading this question but it does not work iOS swift imageView cannot be hidden in TableViewCell
Give outlet of image's width and if there is no image then set constant of that outlet to "0".
e.g.
if(!image)
{
widthOfImage.constant = 0
}
I experienced the same problem myself. You need to create 2 cells for this. Like this:
override func tableView (_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if (stream_image_string[indexPath.row] == "")
{
let cell = tableView.dequeueReusableCell (withIdentifier: "noImageMyFeed", for: indexPath) as! noImageMyFeed
return cell
}
else
{
let cell = tableView.dequeueReusableCell (withIdentifier: "MyFeed", for: indexPath) as! MyFeed
return cell
}
}
this video will help you in detail : https://www.youtube.com/watch?v=FAxtWtqeMIM
adapt the video to its own content, create a cell from scratch by simply deleting the image part
I am using SDWebImage library to download image and load into image view inside a table cell.
I want to resize the table cell after the image is downloaded. Please advice. Many thanks.
Cell is not resize correctly after image set into image view
Cell resize correctly after dequeue and load again
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier(GagListTableViewCell.cellId, forIndexPath: indexPath) as! GagListTableViewCell
cell.layoutMargins = UIEdgeInsetsZero
let gagDataCommand = gagDataSource[indexPath.row]
if let caption = gagDataCommand.caption {
cell.captionLabel.text = caption
}
if let imageUrlStr = gagDataCommand.images?.normal {
if let imageUrl = NSURL(string: imageUrlStr) {
cell.gagImage.sd_setImageWithURL(imageUrl, placeholderImage: UIImage(named: AppConstants.imagePlaceHolderName), completed: { (image, error, cacheType, url) in
//guess I need to redraw the layout here
})
} else {
//fail to get image url, set placeholder image to the cell instead
cell.gagImage.image = UIImage(named: AppConstants.imagePlaceHolderName)
}
}
return cell
}
You could use automatic dimension for tableview cell.
tableView.rowHeight = UITableViewAutomaticDimension
EDITED CODE FROM BELOW ANSWER
WHAT I WANT
The images are correct size and show in the correct positions
WHAT I GET
A bunch of images showing up everywhere randomly when I scroll
I have looked through so many answers and I still haven't found a solution to my problem. It seems quite often it is a different problem for each person, and it doesn't help that most questions here are in Objective-C. I find it difficult converting from one to another.
I will just show my table view functions and explain my code and maybe somebody can identify the problem or help me to identify to solve it myself.
-----------------------------------------------------------------------------------
Explanation
Not all News Items (cells) contain pictures. So as you can see (in the first function below) I loop through the sections and the nested cells and if the news item contains a picture if newslists[i][j] != "None, I display the image. From this point it is familiar code, seen from the tutorial here
In the second function I once again loop through the sections and the cells till I land on one that should contain an image. I then change the height of this cell so the image will fit inside.
That is pretty much it...
any ideas?
-----------------------------------------------------------------------------------
CellForRowAtIndexPath
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("NewsCell", forIndexPath: indexPath) as! UITableViewCell
if newslists[indexPath.section][indexPath.row].imageURL != "None"{
let urlString = newslists[indexPath.section][indexPath.row].imageURL
let imgURL = NSURL(string: urlString)
cell.imageView?.image = UIImage(named: "first") // placeholder
if let img = imageCache[urlString] {
cell.imageView?.image = img
println("loaded from cache")
}
else {
let request: NSURLRequest = NSURLRequest(URL: imgURL!)
let mainQueue = NSOperationQueue.mainQueue()
NSURLConnection.sendAsynchronousRequest(request, queue: mainQueue, completionHandler: { (response, data, error) -> Void in
if error == nil {
let image = UIImage(data: data)
self.imageCache[urlString] = image
dispatch_async(dispatch_get_main_queue(), {
if let cellToUpdate = tableView.cellForRowAtIndexPath(indexPath) {
cellToUpdate.imageView?.image = image
println("succesfully downloaded image")
println("size of cache is \(self.imageCache.count)")
}
})
}
else {
println("Error: \(error.localizedDescription)")
}
})
}
}
cell.backgroundColor = UIColor(red: 52/255, green: 138/255, blue: 169/255, alpha: 1.0)
return cell
}
I have one more function that may be causing problems but I doubt it:
HeightForRowAtIndexPath
func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
if newslists[indexPath.section][indexPath.row].imageURL != "None"{
println("Expanding cell for \(indexPath.section) \(indexPath.row)")
return 190.0
}
return 70.0
}
I'm not sure why you have a double for loop in both method. There are called for every indexPath possible so you can just get you appropriate data with the indexPath section and row.
You can try this code, i don't see any reason for it to not work.
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("NewsCell", forIndexPath: indexPath) as! UITableViewCell
cell.imageView?.image = nil
if newslists[indexPath.section][indexPath.row].imageURL != "None"{
let urlString = newslists[indexPath.section][indexPath.row].imageURL
let imgURL = NSURL(string: urlString)
cell.imageView?.image = UIImage(named: "first") // placeholder
if let img = imageCache[urlString] {
cell.imageView?.image = img
println("loaded from cache")
}
else {
...
}
}
return cell
}
func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
if newslists[indexPath.section][indexPath.row].imageURL != "None"{
println("Expanding cell for \(i) \(j)")
return 190.0
}
return 70.0
}
All we need to do is use the prepareForReuse() function. As discussed in this medium article, This function is called before cell reuse, letting you cancel current requests and perform a reset.
override func prepareForReuse() {
super.prepareForReuse()
imageView.image = nil
}