One click, multiple actions Custom Cell of TableView - ios

What I do
I have a created a custom cell which has a button with a specific image on it. Now depending on some conditions I set that image image1.jpg or image2.jpg.
How I do that
The custom cell is created by creating a protocol where I click that buttons action, I notice in the main interface, that the button is pressed and I pass as a parameter the button instance.
Issue
Now, the problem is when I click that button in a specific cell, I change cell.button's image, but also with that, when I scroll down, I see that there are other cells that have changed their image.
Any idea why is this happening?

Due to the reuse of cells by the UITableView, when the cell moves out of the screen, it is moved to a queue and then reused when there are new cells to load.
If you want to keep each cell's property unique, you will have to reconstruct its values every time. For that you can keep some kind of data array that stores the state/condition of each cell. The array will be ordered by the indexPath of the tableView, and then you can just fetch the state from the array based on the indexPath and update the cell to be the right image.

What is happening is that when you change the image of your cell and later scroll down or up, your cell is being reused to present another information.
To fix that you should check your UITableView data source, see if you're reusing cells in this method:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
I'd recommend to configure the cell before returning it in the previous method, and assign the appropiate image for the button at that time. To achieve that you'd need some mechanism for remembering the state of each cell.
For more information about cell reusing and cell in general you could go to the Apple Docs for UITableViewCell

As described in above answer you can manage button selected and unselected state in array for that you can even add one key in the array that you are using for the data to display.
and on didselect event of table you can change the key of selected field like this
NSMutableDictionary *newDict = [[NSMutableDictionary alloc] init];
NSDictionary *oldDict = self.dict_trans_Details ;
[newDict addEntriesFromDictionary:oldDict];
[newDict setObject:status forKey:#"transactionstatusid"];
[table_transdetails_array replaceObjectAtIndex:self.indexValue withObject:newDict];
Thanks.

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.

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.

Objective-C: Set another UITableViewCell on select

I have two custom models of TableViewCell, one for just basic informations and another for detailed informations.
How can I switch to the detailed one with the didSelectRowAtIndexPath method? (and afterwards if the detailed one is displayed, toggle to the basic one on select)
Thank you.
When the row is selected, toggle a flag indicating that the row needs to switch from one type of cell to another. Then reload the the cell at that index path.
Then your cellForRowAtIndexPath method looks at the flag for the given index path and returns one of the two types of cells.
In whatever kind of object you use to represent cell data, keep a flag for whether it's selected or not. Turn the flag on or off in didSelectRowAtIndexPath and then reload the table view data. When you return a cell for an index path, choose which kind of cell based on the flag value.
You can create an array or set consist of indexPaths of cells type 1. Add or remove from it cells if needed. Return the right cell in
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath
And if you want to reload specific cell, modify your array of cells and just call from tableView:
- (void)reloadRowsAtIndexPaths:(NSArray *)indexPaths
withRowAnimation:(UITableViewRowAnimation)animation

UITableView reusable cell or not

I'm developing an app that represents a huge amount of data using UITableView with a custom UITableViewCell.
When the tableView is set to editing mode, it supports multiple selection for further usage of the selected set of data.
Using -tableView:didSelectRowAtIndexPath: and didDeselect I'm changing the rows UIImageViews image to a little tick and storing the selection into an array. When selecting a cell it gets light blue background (iOS standard).
When I'm now scrolling down and up again, the cells background is still light blue but the image is reset to default and the cells isSelected property is NO. Selecting it again invokes the -tableView:didSelectRowAtIndexPath: method.
After a while debugging it turned out that the reusable identifier was wrong. But as I corrected it, scrolling down repeated the same 12 cells over and over again. And the isSelected property is still reset to NO.
How can I maintain selected rows while scrolling the tableView?! And: Why is the cell blue highlighted (or marked as selected) but the isSelected property gets reset to No?
Thanks for help, with kind regards, Julian
Don't use the cell to hold persistent controller state. Instead keep an NSArray and add the NSIndexPath of each selected cell to your array (and remove them if/when the user deselects a cell).
Then in cellForRowAtIndexPath: just make sure you configure the cell correctly based upon whether or not its index-path is in your array of selected cells.

How can I get the value of a UITableViewCell that is not visible?

I have a table view cell that has many rows with a UITextView. In those UITextView the user can enter values.
The problem that I have is that if I want to get the value of a row when the row is hidden I get nil.
NSString *cellValue = ((UITextField *)[[[self.table cellForRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:1]] contentView] viewWithTag:[self.table cellForRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:1]].tag]).text;
I was thinking to save the value that the user enters in the UITextField, but if the user clicks a row that performs the calculate the delegate textFieldDidEndEditing is not being called.
I don't know how can I get this values in a secure way.
You should target the data source that is behind the UITableView. The UITableView is just a display mechanism and not the primary resource for getting to data. You can get the index for the cell based on the title, but then I would look tot he data source to get your actual data. Better yet I would just handle all operations against the data source itself if that is possible. When you build the table initially your data source is a list or array of some kind I am sure so you can just use it. You may need to create a variable on the view and retain the list/array there to make sure you have it and you should be all set.
Good Luck!
As mentioned, you would generally use the UITableView data source to access this.
That said, you are probably not seeing the data you expect because the you are using dequeued cells. If the number of cells is small, you could consider caching your own cells in an NSDictionary whose keys are some combination of the section and row, and accessing their view hierarchy via your own cache.
You can access rows that are not currently displayed by directly referring the tableView's dataSource with the wanted IndexPath.
You can achieve this by doing something like this (when using Swift 3):
UITableViewCell cell = self.tableView(self.tableView, cellForRowAt: indexPath)
What is happening most likely is your reusing cells. So when your cell with the textfield goes off screen it gets reused so you cannot access it anymore. What you will need to do is grab the value out the the textfield right after they enter it and store it somewhere. You can use – textField:shouldChangeCharactersInRange:replacementString: so that when ever the user enters a character into the textfield you can save the string.

Resources