Why using SDWebImage in a UITableViewCell triggers ImageViews not to render properly? - ios

I have a UITableViewController containing my own cells that I dequeue in cellForRowAtIndexPath.
After dequeuing, I configure the cell and reload, asynchronously, the image for that cell.
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
if let cell = tableView.dequeueReusableCellWithIdentifier("PeopleCell", forIndexPath: indexPath) as? PeopleListViewCell {
cell.configureCell(headImageUrl)
}
}
In my PeopleListView class,
func configureCell(img:NSURL) {
if headImageView == nil { // to avoid allocation memory if not used
headImageView = UIImageView()
addSubView(headImageView)
}
headImageView.sd_cancelCurrentImageLoad()
headImageView.sd_setImageWithURL(headImageUrl)
}
It works fine at the first loading and also while scrolling.
But when I push another viewController after
didSelectRowAtIndexPath
and come back to the list after the
dismissViewController()
I end up with a weird effect on my UIImageView , it's kind of a stacked or ghost image effect..
I'm having a hard time to figure out where is even triggered as when I m coming back from the viewController, cellForRowAtIndexPath is not called.

This actually has nothing to do with asynchronous image loading.
The images I had was displayed within circles, with a cornerRadius.
Somehow, it was displayed without any problem at first load...
The issue here is I simply forgot the
headImageView.layer.maskToBounds = true
The result I got before setting maskToBounds to true was that I had the feeling that multiple images were located within the headImageView (UIImageView). If you ever have some artefacts like that, I hope this question/answer will help you.

Related

How to configure stackview with dynamic content inside UITableViewCell?

I have UITableViewCell which looks like CardView. Inside cell, I should display some dynamic content. That is why I have stackview inside my cell that will have my dynamic content. But the problem is that cellForRowAt method is called every time while scrolling and stackview starts having extra elements.
How should I solve this problem?
How I handled problem by myself?
I found prepareForReuse method of UITableViewCell which is called before re-configuring by cell. In that method, I clean my stackView. And now, my stackview will not have extra views. But, it is too bad for performance. The next way I tried is holding some flag inside my cell that tells me was stackview already configured. But, this approach didn't help (yes, it will not add extra elements, but content inside cells gets incorrect placement) as checking stackview length.
Here is my pseudo-code:
/// called in cellForRowAt
func configure(item: Item) {
item.forEach {
stackView.addArrangedSubview(ItemView(item))
}
}
func prepareForReuse() {
stackView.arrangedSubviews.forEach {
$0.removeFromSuperView()
}
}
If the prepareForReuse and dequeuing methods leads to exceeding 0.0167 sec (60 frames per second) then maybe in your edge case it will be better to create a cell instead of dequeuing it.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = UITableViewCell()
cell.configure(item: item)
return cell
}

Some custom UITableViewCells are not rendering

I have a custom cell UITableViewCell, sometimes when the tableview loads, I am getting missing cells (cells where the content view is not rendering at all. I get this behavior fairly consistently when I use reloadRowsAtIndexPath (when a custom object the cell is using is updated for example). If I call reloadData on the tableview, I usually don't get this behavior.
Here is what it looks like when view debugging:
Here is the cell under that (which rendered fine):
My initialization of the cell in cellForRowAtIndexPath is the usual pattern:
Edit - entire cellForRowAtIndexPath:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let reuseIdentifier = "WorkOrderListCell"
let cell:WorkOrderListCell = tableView.dequeueReusableCell(withIdentifier: reuseIdentifier) as! WorkOrderListCell
If I scroll the tableview so that the cell which did not render is off screen, and then scroll back, the cell will render.
I have also ensured that I am on the main queue by wrapping my reloadRowsAtIndexPath in a main queue closure but that doesn't make a difference.
What am I missing?
Some times there's an issue with table view on first load. So I would suggest reloading tableView data twice. You can use this extension:
extension UITableView {
func reloadDataWithAutoSizingCellWorkAround() {
self.reloadData()
self.setNeedsLayout()
self.layoutIfNeeded()
self.reloadData()
}
}
Which is found in this issue https://github.com/smileyborg/TableViewCellWithAutoLayoutiOS8/issues/10
Or you can call directly:
self.reloadData()
self.setNeedsLayout()
self.layoutIfNeeded()
self.reloadData()
Issue was that I had a ambiguous constraint in the content view of the cell. It only got reported when I added setNeedsLayout in the delegate method to reload the row. Once I found that, it was easy to find using the a breakpoint and debugging the view. When I removed the distance constraint that was causing the issue, all the rendering issues went away. Thanks to #DionizB for putting me on a good path.

How can I clear a WKWebview that is contained in a custom UITableViewCell that is reused?

I have a UITableView that dequeues my custom UITableViewCell. The UITableViewCell contains a WKWebView. In cellForRowAtIndexPath, I set the url that the WKWebView should load and the webview starts to load the page. The problem is that the data of the cell, sometimes displays the "old" data while the new url is still loaded.
I tried to first load about:blank and I also tried webView.loadHTMLString("", baseURL: nil), but it did not work, because it is an async operation.
Here is pieces of my code.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: MyTableViewController.cellIdentifier, for: indexPath) as! MyTableViewCell
cell.url = data[(indexPath as NSIndexPath).row]
return cell
}
In MyTableViewCell, I immediately start to load the html. loadHTML(in:,forUrl:) is a private function that loads the url into the webview.
var url: String? {
didSet {
loadHTML(in:webView!, forURL:url)
}
}
It will be much appreciated if someone knows how to do this properly. Some suggestions that I have seen included, hiding the webView until it is finished loaded. I would actually like my user to see the progress in loading.
I don't think it would be a good idea not to dequeue the UITableViewCell. (The cell contain other controls too.) The best I can think of right now, is to remove the WKWebView and adding it again.
What about covering the content with an empty view as long as it is not reloaded (i guess you can check it with the wk delegate didFinishNavigation)
You may leave out the loading, bar tuning a bit the constraint.
It's very hacky i know, but may work if you look for a quick'n'dirty solution.
Not sure as performances how better it is, but for sure it doesn't involve recomputing the constraint as opposite to removing and re-adding the webview

Removing all subviews from a UITableViewCell except imageView

I am creating a custom cell and I add subviews to it for text mostly. At first the subviews were not getting deleted when the cell was reused, so the text would overwrite and blot really bad. Then I added this for loop to the function and it solved my problem except that it also completely removes my imageView and it never gets re-added, so my whole tableview is missing the image that is supposed to be associated with each cell.
The if statement didn't work, but I didn't explicitly use cell.addSubView() as i did for the cell labels, so I am wondering if that has something to do with it. I have googled and found that most people are saying this worked for them, but I just cannot seem to figure it out.
Why does this remove my images, why won't they come pack like the other subviews, and how can I fix this?
I have the following code:
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("cell") as! UITableViewCell
for subView in cell.subviews {
if subView as? NSObject != cell.imageView {
subView.removeFromSuperview()
}
}
var statusImage = UIImage(named:imageConfigration(indexPath.row))!
cell.imageView?.image = statusImage
// ...
}
Give your imageView a tag in storyboard or xib or programmatically what you are using. Now in the for loop of subviews check if tag of the subview is equal to tag to imageview dont remove it.
Also another option to solve this if it fits your requirement is to hide all the elements added to cell except imageview. That will be much faster then looping in subviews and removing it you never know what all subview are attached to cell other then you added by iOS but thats if it fits your req. Let me know in case of any concern
This how you can do this
cell.textLabel.hidden = YES;

UITableViewCell flashing on resize or delete

I am building a card type layout with a UITableView, the "cards" need to be able to resize and be swiped to remove them. I implemented the delegate calls for both actions and the resizing is done as follows:
The "preferred height" is set right before the call to actually resize.
//supply card heights to cell
override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
//if no cards are in memory put them in memory.
if (cards == nil){
populateCardData()
}
let card = cards![indexPath.row]
return card.preferedHeight
}
And the call to resize:
func resizeCardWithIndexPath(inIndexPath: NSIndexPath) {
//simply reloads the data at the given index path
shouldAnimate = [inIndexPath:false]//DONT PAY ATTENTION TO THIS(for something else)
tableView.beginUpdates()//THIS IS THE UPDATE PART
tableView.endUpdates()
shouldAnimate = [inIndexPath:true]
}
And The removing of cards:
func removeCardWithIndexPath(inIndexPath:NSIndexPath){
//if no cards are in memory put them in memory.
if (cards == nil){
populateCardData()
}
//remove card from the array
cards!.removeAtIndex(inIndexPath.row)
//delete row from tableview
tableView.deleteRowsAtIndexPaths([inIndexPath], withRowAnimation: UITableViewRowAnimation.Automatic)
}
Now...
All of this works. The cell gets resized appropriately and cells are removed correctly. But when resizing or deleting is done, sometimes irritatingly enough, some of the other cells flash while moving in the Table View.
Am I just doing something stupid or is there a better way?
I did a lot of research and tried a few solutions to no avail.
I did find one way to fix the problem is to make the cell background a solid color and not transparent, this fixed the flashing, but is not a solution because the cards need a transparent border so the background can be seen.
If anyone has any insight help will be a life saver!

Resources