Multiple user taps in a tableview in main thread in iOS - ios

I have implemented a table view with multiple threads. Currently when a user taps on a cell a different thread starts downlading the content in the background and the user is free to choose a different cell before the content is loaded.
Currently the app will show the content of the first tapped cell and then immediately switch to the content of the 2nd cell.
My question is if a user selects a cell and then before the app can switch to the detailview they select another cell, how do I only show the contents of the second cell and forget about the 1st cell?

You have different ways to do this; speaking generally you'd have a callback of your asyncronous computation. In that callback, simply look at the last indexPath selected and match whether or not it's the same indexPath who triggered that computation.

Related

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.

Reload data in table view without harming animation

I have a UITableView-based in-game shop.
Every cell has a "BUY" button which is mostly enabled and can be switched to "BOUGHT" if the item is a one-time purchase or can be disabled if there are not enough money.
Right now what I do is calling reloadData every time buy button is being pressed in order to update visible cells and the current cell itself. (I have to update all cells, because after purchase it is possible that there wont be enough money for visible item cells).
But it causes weird animation glitches, like when I click on one cell's buy button and animation finishes on another one.
I think this happens due to reusability of cells. So what I want to know is how to reload data in the whole table view without harming native animation.
The only thing I can think of is not to use reusable cells and cache them all, but I dont think this is a good programming practice.
First, make sure that your view layer and model layer are separate. There should be some non-view object that knows about each item; we'll call it Item.
Now, create an ItemCell (you probably have one already). That's your reusable cell. Hand it the Item. It should configure itself based on the data in there.
Use KVO, delegation, or notifications to let the cell observe its item. When the Item changes its status, the cell should update its own button.
When your cell is reused, you'll pass a new item to it. It should stop observing the previous one, and start observing the new one (and of course reconfigure itself to match the current status).
By separating the views (which are reusable and transitory) from the model (which is stable and long-lived), you get the performance benefits of cell reuse with correct animations and no need to call reloadData.
You could have a reloadCell method in the cell class and loop through the table's visibleCells and update their UI that way. That way they are not recreated (or re-used), they just have their relevant UI pieces that could have changed due to the new data updated.

Progress indicator in table cell while app fetches and parses data

I have an app that displays a table of places, that when a cell is tapped, it fetches data from a web API and parses it. However, sometimes this request takes 3-5 seconds to process. I've tried to display a spinning progress indicator in the content view of the cell, but I haven't been able to get it totally correct.
I was able to get the indicator to appear when the cell was tapped, however when I return to the table, the indicator is still visible. What's the best way to do do this?
Here is one way to approach this issue:
when the cell is clicked, store the indexPath (or some other reference) so you know which cell was clicked
add the spinner
make an async call to get the data from your data source
when the async call returns, update the appropriate data in your array (or wherever you store the data for the table)
call reloadRowsAtIndexPaths to reload just the cells that changed, and remove your spinner.

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.

Load a cell below data upon the users choice in the first cell

Have a specific question.
I'm building a choice-based TableView app.
Let's say if the user clicks on the second button in the first cell - he is shown the second cell data (if he clicks A it would be data 1, if B - data 2). The first cell then becomes inactive.
In the second cell again he has two options - and then the third cell ... and so on.
Browsed internet all over for this and have no clue.
Thank you!
Appreciate any ideas!
I'm not sure to understand your question quite well, should the flow be like this :
1 ) The user sees only one cell containing, let's say, two pictures corresponding to two different choices
2 ) The user selects one of the pictures
3) A second cell appears below the first one ( same type of cell, with two pictures ). User interaction with the first cell is then disabled.
If that's it, a solution would be to implement a custom table view cell with two custom buttons ( UIButton ) corresponding to your choices.
These custom buttons could have an NSString property called "choice" accessible from your table view controller.
You can then add a counter on your table view controller to know how many cells should be displayed. It starts at 1 with the first cell.
When the user would touch one of the buttons, this counter would be incremented by one.
You can manage your data with the choice the user picked up and then reload your tableview.
I didn't write code, because it would represent dozens of lines and you're just asking for ideas.
Hope it helps !

Resources