How to display a placeholder before the tableview cells get displayed? - ios

This questions is referencing just before a tableview fetches its data and displays its cells.
I've seen a few apps lately display a rough outline type image of the tableview cells for the brief moments before the populated cells get displayed.
How is this done?
Is a placeholder image used for the entire tableview or are placeholder type images rendered for each cell until the cell is dequeued?
Here are examples from Facebook and the fiverr app

Create a separate UITableViewCell class where the content of the cell is a UIImageView that has some kind of placeholder image of what your cells will look like. Populate the UITableView with those cells while your background request is being made. When the request completes, start a table update in which you remove all the placeholder cells, then insert all the "real" cells.

According to me it would be better to add backgroundView to tableView.
write:
while searching /fetching data:
if results.count == 0{
tableview.backroundView = emptyBlurView
}
once data is received so before reload :
tableview.backroundView = nil

when start requesting on server show the place holder cell and network response are received show the data container cell. using a placeholder cell same as activity indicator.
FaceBook are using simmer effect for its placeholder cell.
Pod
https://github.com/malkouz/ListPlaceholder
https://github.com/Juanpe/SkeletonView

Related

Cancel request for Alamofire image

Basic Information:
I have a tableView and each tableView cell containing an image and few labels.
I use tableView prefetch data method to make API calls to fetch data as the tableView scrolls.
Issue:
Sometimes I see a particular cell showing an image of some other cell.
This happens when I scroll a little faster than normal speed.
Note: In order to overcome from this issue I just need to scroll the tableView up/down
Things tried:
I have set the imageView to nil in prepare for reuse method.
The conclusion I have reached:
By debugging the issue I understood that once a cell is visible I make an image request using Alamofire Image but I scroll it before we received the response.
So, what might be happening is that on receiving a response it is setting an image for the cell but that cell is not visible as I am reusing the cells. The cell contains some other data.
Please let me know how can I cancel the request if the cell is not visible.
Let me know if I am missing something in the question.
What I do in this situation is set the cell's tag to match the row number.
When the image requests complete, I check if it still has the same tag (if it was reused, tag would be changed).
Something like this, in cellForRowAt indexPath:
cell.tag = indexPath.row
ImageService.shared.getImage(completion: { (image) in
if let image = image, cell.tag == indexPath.row {
//apply image
}
}
This doesn't cancel the request, but it ignores the result if the cell is not in the same position anymore.
This is happening because tableView reuse the cells and if it having a image downloading in queue and you scroll and the downloading is complete, it shows the downloaded image for few second until correct image download.
use placeholder in af_setImage method. it will solve your problem
imageView.af_setImage(withURL: URL(string: img)!, placeholderImage: UIImage(named: "product_placeholder")

Avoid UITableViewCell updating content when scrolled

I've found some similar questions already on SO, but nothing that seems to address this specific problem.
I'm using a UITableView with around 25 dynamic cells. Each cells contains a hidden UIProgressView. When the user taps on the download button within the cell to download that item, the UIProgressView is displayed and it indicates the progress of the download.
I've achieved this by assigning a tag to each cell which is equivalent to its corresponding downloadItemID. Each instance of MyCell has its own progressBar property.
This works fine as long as the table view is not scrolled during the download. It works also when the user is downloading multiple items at the same time. The code looks like this:
UIProgressView *theProgressBar;
for (MyCell *cell in self.tableView.visibleCells)
{
if (cell.tag == downloadItemID) theProgressBar = cell.progressBar;
}
float progressPercentage = (float)completedResources / expectedResources;
[theProgressBar setProgress:progressPercentage animated:YES];
The problem is that when the user scrolls the table view, the cell and progress view are transferred to another cell. It's simple enough to reset and hide the progress view for the new cell, but when the original/downloading cell is scrolled back into view, no progress is visible.
I've tried caching active progress bars into a dictionary and reallocating them back to the original cell in cellForRowAtIndexPath, but this is giving me the same result: no visible progress after scrolling the cell off and on the screen. Even if I can get them to show up, I'm doubtful I can get this to work seamlessly by rolling my own caching method.
What I need is to keep cells in memory. But can I work around dequeueReusableCellWithIdentifier? This whole problem has arisen because I had to switch to a dynamic system of allocating the cells, but it is too dynamic. Can I create the cells dynamically, but keep them in memory all the time once created, or at least keep the ones that are currently downloading?
(I understand the reasons why cell reuse is the preferred way to go with table views.)
You are working against the framework. As vadian says in the comment, you need to separate the logic and state information from the cells and put them elsewhere.
Here is one way of doing it.
Create a class to hold each download state, like a download ongoing flag, download progress, title, URL, etc.
In your view controller, create and add all your download objects to an array when the view controller is created. The first object corresponds to the first row in the table.
Whenever you dequeue a cell, populate it with data from the array. The NSIndexPath row property serves as the index into the array.
All your updates (download progress) updates the download objects in the array, then update the cell content using the updated object. You can use UITableView cellForRowAtIndexPath to get the cell for a specific array index, if you get nil there is no need to update it.

Updating label in a cell of UITableView

My UITableViewCell has a cell template which is created from another nib file. The cell has a UIlabel object. Now, once the UITableView has loaded and the text has been displayed, and if I want to change its value by clicking a button from another cell, How should I do it ?
I have updated the text of the UIlabel but how to show it on the screen? Should I reload the entire table? Kindly let me know if there is any good way to do it.
You can use KVO for this purpose. Each cell observes the model, and when it changes, update some fields.

Passing data between UICollectionViewCells

I am using a UICollectionView, in which one UICollectionViewCell covers the entire screen. Inside my UICollectionViewCell, I give people the opportunity to add text (UILabel), images (UIImageView) and color (UIColor). Now when the user navigates to the next cell i want the color, labels and image views to be displayed exactly as they were added on the previous cell. The users also have the option to pinch and pan the labels and images. In short, how can i pass on data from one cell to another?
I see several ways how to do this depending on your realization:
use global properties/ivars to store the selected data from user input.
In either case you probably handle UITextFiledDelegate methods in your controller or extract the cell by indexPath and copy values from the current cell to the next one.
And when the user presses the "Continue" button you:
1) If you create all you collectionViewCells at once in cellForItemAtIndexPath, then you should only reload the necessary cell via - (void)reloadItemsAtIndexPaths:(NSArray *)indexPaths and set the values you have saved previously.
2) If you create cells but the next one is not ready (for instance you save the memory) - all almost the same - you add a new cell to the collectionView and read the data from your properties.
3) If you have not a "Continue" button or/and user can swipe the cell in every moment - so you can reload the cell in scrollViewDidScroll(or scrollViewWillBeginDragging) or extract the existent by indexPath and modify it without reloading.

set height of outer TableViewCell from inner nested UITableViewCell

I'm trying to implement accordion view using UITableView. I want nesting of the view up to 2 levels.
(Example : There are multiple Regions under Each Region there will be zero/multiple Locations and under Each Location there will zero/multiple Users).
For implementing this scenario I used nested UITableView. i.e Outer TableView (i.e Region TableView) will have its cell as another UITableView (i.e Location TableView) and Each cell of LocationTableView will have another UITableView(i.e TableView Showing users list under each location) as its cell.
I have attached the images for more clarification.
In below image Regions are sections of the tableview. (note : not a single row for now in tableview)
After Tapping 2nd section i.e section with name Region I dynamically reload that section and add the cell(i.e the cell that contains the UITableview showing the locations)
But, it shows the empty space below the location tableview.
Instead I want something like this as below :
Again after tapping the 0th section(Location 0) of the inner tableview (i.e tableview containing location list) I dynamically reload that section of tableview and add the cell(i.e the cell that contains the UITableview showing the list of users under that location)
Have a look at below image
It works perfectly.
Now my issue is that I have calculated the height of the outer UITableviewCell in *- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath )indexPath method depending on the number Location within That Region(i.e Cell) and number of Users under each Location under that Particular Region cell. Issue is that I need to hide/show the locations and users list(All the locations and users list is not shown at once)as according to accordionView. So the height of the inner tableView increase/decrease, hence leaving the blank space at the bottom. Also I have created custom UITableViewCell (i.e creating separate nib file for the cell) for each inner cell containing tableview as ContentView and implemented all the UITableView Data Source and Delegate methods in its corresponding .m file.
What I want is not to show that empty blank space.
When control goes in the inner tableview cells I can't set the height of the outer UITableView from there.
Hope you understood my problem. Please help me. Thank you!!!
I had some similar requirements like this, I didnt use the nested tableview as it brought some complexities to me, so I tried playing around with indentation and indexpaths of the tableview to achieve what I wanted. I just did a small sample app with that concept with some dummy data. I have never tried this approach with Custom cells. See if this helps.
I know I am not answering to the exact question that you asked but I am trying to give a different approach which for me looked simpler.
I just modified my base code to match your data
How to create an Accordion with UItableview under a UItableview?
The source can be found here
Accordion
Screenshot
-anoop

Resources