Reloading the Tableview section after every second is causing flickering - ios

I am trying to reload my Tableview with new datasource every second, but there is a slight flickering in the reloading which is clearly visible to the User.
My requirement for refreshing the datasource every second is because I get the new servertime from the API after every second.
I have used the following different methods to reload my 3 sections of my tableView, but now seems to give a without flicker experience.
Method 1
UIView.performWithoutAnimation {
UIView.setAnimationsEnabled(false)
weakSelf.tradeTableView.reloadSections([0,1,2], with: .none)
}
Method 2
UIView.setAnimationsEnabled(false)
weakSelf.tradeTableView.beginUpdates()
weakSelf.tradeTableView.reloadSections([0,1,2], with: .none)
weakSelf.tradeTableView.endUpdates()
UIView.setAnimationsEnabled(true)
Both methods are giving a flicker experience.

Is it flickering or jumping?
If the scrollview jumps check these answers Jumpy tableview after reload

This this one
let loc = tradeTableView.contentOffset // this line is added to lock offset of tableview
UIView.performWithoutAnimation {
tradeTableView.beginUpdates()
tradeTableView.reloadData()
tradeTableView.endUpdates()
tradeTableView.layer.removeAllAnimations()
tradeTableView.setContentOffset(loc, animated: false)
}

you can try this
UIView.setAnimationsEnabled(false)
self.tradeTableView.beginUpdates()
self.tradeTableView.endUpdates()
UIView.setAnimationsEnabled(true)

Related

Keep timestamp label updated in UITableView

I have a UIViewController that has a UITableView which presents comments fetched form a live Firebase database.
Every time a new comment arrives, I call
tableView.beginUpdates()
tableView.insertRows(at: [IndexPath(row: self.liveComments.count-1, section: 0)], with: .fade)
tableView.endUpdates()
to insert the latest comment with a fade animation. This works fine.
However, each cell has a label that shows when it was posted, in the form of "seconds, minutes or hours ago". The problem is that when many comments arrive, the age label does not get updated, since the existing cells are not updated, and it looks to the user like the comment ages are wrong.
I've tried calling
tableView.reloadRows(at: self.tableView.indexPathsForVisibleRows ?? [], with: .none)
inside my tableView updated block, but the animation is all messed up, since all of the visible cells seem to get animated in a weird, "jumpy" way.
I've also tried getting all of the visible cells, and calling a method on them to update their timestamp labels manually, but I get a crash when I do this, so I guess it's not recommended:
if let visibleCells = self.tableView.visibleCells as? [LiveCommentTableViewCell] {
visibleCells.forEach { cell in
cell.updateCommentAgeLabel()
}
How can I approach this? I just need to reload all visible cells without an animation, and the last cell with a fade in animation. Thank you!
I would just reload all the data, as long the cellForRowAt sets the timestamp label correctly it should work fine:
// still do your nice animation
tableView.beginUpdates()
tableView.insertRows(at: [IndexPath(row: self.liveComments.count-1, section: 0)], with: .fade)
tableView.endUpdates()
// now just refresh the entire table
tableView.reloadData()
of course you're going to want to make sure that whatever collection feeds the numberOfItemsInSection is also updated before calling reloadData() im assuming you're already doing this as well or you'd be running into a lot of bugs and crashes
make sure that code that edits UI is on the main thread too, obviously.
That being said what does your cell.updateCommentAgeLabel() function look like bc that would work in theory as well unless potentially its not being called on the main thread again or the cast isn't working.
Perhaps try telling the system you want it to do a layout pass:
if let visibleCells = self.tableView.visibleCells as? [LiveCommentTableViewCell] {
visibleCells.forEach { cell in
cell.updateCommentAgeLabel()
cell.layoutIfNeeded() // either this
}
tableView.layoutIfNeeded() // OR this at the end, I dont expect you'll need to do both but not sure if both work

How to keep UITableView scroll smooth when "reloadData" finished then scroll to bottom frequently

I build a IM Message List use UITableView to display it。
when receive a new message, i do the 2 step as below:
UITableView reloadData
after step 1 done, scroll to bottom. I use scrollToRowAtIndexPath with animate
but when receive message very frequently then it will frequently call step 1 & 2, so the UITableView look flash in this situation.
I hope one set of step 1 & 2 finished,can only be do the next set step 1 & 2 but i have no idea about this.
pls help.thx
Instead of reloading tableView each time you receives new message, simply insert new message in tableDataArr and insert new row in tableView. Please check following code for the same
tableDataArr.append("New Message")
tableView.beginUpdates()
let indexpath: NSIndexPath = NSIndexPath.init(row: tableDataArr.count-1, section: 0)
tableView.insertRows(at: [indexpath as IndexPath], with: .bottom)
tableView.endUpdates()
Once new row get inserted in tableView scroll tableView to bottom cell with following code
tableView.scrollToRow(at: indexpath as IndexPath, at: .bottom, animated: true)
If you don't want animation then set animated to false.
It will resolved your problem.

Refresh Control Issue with custom cells - Swift 4 IOS 11

I am building an iOS app and I am trying to implement a pull-down refresh control on my project. The data is fetched correctly from an API and displayed on my table. But the problem rises when I do pull down to refresh. The following situations happen:
If I pull down for a long distance from the top, and the tableview.reloadData() function is called, the cells in the non-visible portion of the table come with the default tableview cells on top of them, overlapping...
if I pull down multiple times in quick succession the same issue happens.
I believe that it is because tableview.reloadData() is called multiple times in quick succession. But why are the default cells getting dequeued on top of my custom cells? Here is the section of code in the function to handle the pulldown:
#objc func refreshFunc(){
//let offset = scrollView.contentOffset.y
if myRefreshControl.isRefreshing{
readJson { (activities) in
self.activities = activities
self.tableView.reloadData()
self.myRefreshControl.endRefreshing()
}
}
}
Any help would be appreciated. Thanks in advance
UPDATE:
Changing the code to the code below seems to remove the error, but the problem is that now I need to pull down twice in order to get the results updated on the table:
#objc func refreshFunc(){
readJson { (activities) in
self.activities = activities
}
self.tableView.reloadData()
self.myRefreshControl.endRefreshing()
}
Please note that running the reloadData on the main thread gives the same result, I still need to pull down twice to update.
Please try to give some delay before refresh table view may it resolve your problem.
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + delayTime) { [weak self] in
self?.tableView.reloadData()
}
Hope it works
Cheers :)
DispatchQueue.main.async {
self.tableView.reloadData
}
Try this, always, if u want change UI you have to call on main thread

Why is my custom collection view cell unexpectedly nil after selecting cell in code?

So I have a colectionView of images, and when something happens in the background, I might try to select a specific custom collectionViewCell using the method:
self.collectionView.selectItemAtIndexPath(indexPathToReload, animated: true, scrollPosition: UICollectionViewScrollPosition.CenteredVertically), which works fine, the collectionView scrolls to the desired location.
However, if I then try to actually update the appearance of the cell as it's been updated by calling self.collectionView(self.collectionView, didSelectItemAtIndexPath: indexPathToReload) I get an unexpectedly nil cell when I then try to create the cell in didSelectItemAtIndexPath.
I partially understand why this method of updating cells is unsafe (as I've read elsewhere in researching this question like here in one of the answers.)
Thus, the crash makes me assume that cells are not part of the visible cells on the screen, which is why the cell is nil when I try to create it. But this doesn't make sense as I also assume that the cells have to be created in order to be scrolled to, which as I said works fines because they are created as expected and can be interacted with without issue.
So why is my cell nil? Or why is my collection view not thinking that the cell that was scrolled to not part of the visible cells? And if the reason is obvious, then how can I make a cell update it's appearance when I select it in code?
EDIT: Code in context
dispatch_async(dispatch_get_main_queue()) {
self.collectionView.selectItemAtIndexPath(indexPathToReload, animated: true, scrollPosition: UICollectionViewScrollPosition.CenteredVertically)
self.collectionView(self.collectionView, didSelectItemAtIndexPath: indexPathToReload)
return
}
As I've said, this pretty much is the context. In the first line I may scroll to an index that is not visible on the screen. If I do this, and then the second line of code executes, the cell that is created in the delegate method that is called is unexpectedly nil.
In order to fix this issue, I had to use kind of a hacky workaround, which although works seems almost too dirty, and I don't know why this issue hasn't been addressed by Apple (i.e why selectItemAtIndexPath doesn't call the delegate method didSelectItemAtIndexPath). Anyways, what I ended up doing was when I needed to update my selected cell in background, I first got the index and set a bool to show a cell was selected in code:
dispatch_async(dispatch_get_main_queue()) {
self.collectionView.selectItemAtIndexPath(indexPathToReload, animated: true, scrollPosition: UICollectionViewScrollPosition.CenteredVertically)
let cell = self.collectionView.cellForItemAtIndexPath(indexPathToReload) as? ListingCollectionViewCell
if cell != nil {
self.collectionView(self.collectionView, didSelectItemAtIndexPath: indexPathToReload)
return
} else {
self.buttonSelectedInCode = true
self.indexPathSelectedInCode = indexPathToReload
return
}
}
Above, I had to try to create the cell for the specified index path. If the cell isn't nil then I know that the cell is visible, and it's safe to call on the delegate didSelectItemAtIndexPath. However, if the cell is nil, then I have to set up my bool and index, and wait for the scroll view to call the delegate method, as shown below.
Then, I further implemented scrollViewDidEndScrollAnimation and used the call of this delegate method to then select my cell in code, as follows:
func scrollViewDidEndScrollingAnimation(scrollView: UIScrollView) {
if buttonSelectedInCode {
self.collectionView(self.collectionView, didSelectItemAtIndexPath: self.indexPathSelectedInCode)
}
}

UITableView section index titles too wide for list

Is there a way to increase the width of the section index titles displayed along the edge of a table view? In my current code some of the titles are being clipped on the left and right:
I'm not doing anything special in creating the list, just returning an NSArray filled with sorted NSStrings.
I had the same problem with my section index titles.
Not sure if this is the correct way, what worked for me was adding:
[self.tableView layoutIfNeeded];
before calling
[self.tableView reloadData];
jb
Fixed.
Turns out it's enough to return a single empty string the first time sectionIndexTitlesForTableView: is called, even if the real titles (when the data eventually arrives and the table is reloaded) will be wider.
Only if the initial call returns an empty list does the column for section index titles fail to adjust its width to fit the titles when they arrive.
Reload TableView and it should fix the issue,
override func viewDidLoad()
{
super.viewDidLoad()
self.tableView.reloadData()
}
iOS 8
I have that problem in iOS8 too.
This seems to be a bug in iOS8, which is not corrected at the time this post is written (10/2014).
Searching around I found two workarounds.
The first one is to force a reload of the data in the viewDidAppear. This will also correct the problem of self-sizing cells not working on first load.
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
self.tableView.reloadData()
}
However in my case it was not solving the issue because I used to change the section index titles after viewDidAppear
So the second workaround is simply to realoadData() twice
self.tableView.reloadData()
self.tableView.reloadData()

Resources