I have a TableView with a custom cell. One of the subviews in the cell is a UIButton. When a user clicks on the button, I want the background to change. I get all of that working. But the problem is I cannot see the change until after I scroll the affected cell off screen and then return it on screen. But I want to see the change immediately, without the onscreen offscreen bit. How might I do that?
For a bit more about my implementation:
Inside the method (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath I have the line
....
[cell.myBtn addTarget:self action:#selector(onMyBtnTapped:) forControlEvents:UIControlEventTouchUpInside];
And then inside the onMyBtnTapped method is where I effect the color change.
So perhaps what I need to do is to redraw a specific cell from the parent view controller (?).
a bit more
I have gotten as far as getting the cell using [self.tableView cellForRowAtIndexPath:indexPath];. But now that I have the cell, I don't know how to get it to redraw itself. I do this on android all the time. I am not sure how to do it on iOS.
You can reload the UITableViewCell on button click:
[self.tableView reloadRowsAtIndexPaths:#[indexPathOfGivenCell] withRowAnimation:UITableViewRowAnimationNone];
You should be able to place the following in the method.
[self.tableView beginUpdates];
[self.tableView reloadRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationNone];
[self.tableView endUpdates];
You can ask to redraw cell at visible rows:
NSArray *visibleIndexPaths = [tableView indexPathsForVisibleRows];
[self.tableView reloadRowsAtIndexPaths:visibleIndexPaths withRowAnimation: UITableViewRowAnimationFade];
Related
I want to change a specific UIImageView inside a UITableViewCell with this:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[self.tableView beginUpdates];
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
UYLTextCell *textCell = (UYLTextCell *)cell;
textCell.selected = YES;
//get and show the image at selected cell
textCell.testImage.image = [UIImage imageNamed:#"image280280.jpg"];
//[tableView reloadRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
[self.tableView endUpdates];
}
But what happened is some cell had their image View even though it has not been clicked.
Table views re-use cells. When a cell is scrolled offscreen it is added to a queue, and will be re-used for the next cell to be scrolled onscreen. That means your cell configuration code in tableView:cellForRowAtIndexPath: should be enough to properly configure any cell, including cells that were previously selected at a different indexPath. In tableView:didSelectRowAtIndexPath: store the selected index path in a property and use that property to configure your cell in tableView:cellForRowAtIndexPath:. The image will need to be set in tableView:cellForRowAtIndexPath: whether the cell is selected or not (unless you implement prepareForReuse in your custom cell).
I need to create an application that have the same behavior as built-in Reminder App. There are some problems to create this:
Cells that have dynamic height and "grow" as the content of the UITextView
Select the touched cell
Refresh the height of the cell when the user has edited the content(on the fly)
I've already solved the dynamic height, using some trick.
The problem that remains is:
How to know which cell the user has selected if the cell is "fully" with the UITextView ?
Right Now i've used the method textViewDidBeginEditing to know the cell and scroll the UITableView to it:
- (void)textViewDidBeginEditing:(UITextView*)textView
{
MemoViewCell* cell = (MemoViewCell *)[self parentCellFor:textView];
if (cell)
{
NSIndexPath* indexPath = [self.tableView indexPathForCell:cell];
currentIndexPath = indexPath;
[self.tableView scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionMiddle animated:YES];
}
}
- (UITableViewCell*)parentCellFor:(UIView*)view
{
if (!view)
return nil;
if ([view isMemberOfClass:[MemoViewCell class]])
return (UITableViewCell*)view;
return [self parentCellFor:view.superview];
}
How to refresh the cell height without lost the focus?
To this, i've used this method:
-(void)textViewDidChange:(UITextView *)textView
{
NSMutableDictionary *dataSourceItem = [self.model.dataSource objectAtIndex:currentIndexPath.row];
[dataSourceItem setObject:textView.text forKeyedSubscript:#"body"];
[self.model.dataSource setObject:dataSourceItem atIndexedSubscript:currentIndexPath.row];
[self.tableView beginUpdates];
[self.tableView endUpdates];
}
When the text change, i get the new text, i change it in the Model and i call beginUpdated and endUpdates.
Sure, this works, but all is extremly slow... do you have some idea to to this in a more elegant way and maybe... efficient?
If i call reloadRowsAtIndexPaths: i lost the focus on the cell.
First of all, if you use the delegate method - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath; in your dynamic height trick, remember to implement this method along - (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath;, it helps a lot in terms of performance.
Then to focus on your specific project, if I correctly understood your goal, what you really want is to update the layout of your table view only when your text view is modified. Currently you're updating it at each text change. You might consider using a condition inside -(void)textViewDidChange:(UITextView *)textView, such as :
if ([textView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize].height !=
textView.frame.size.height) {
[self.tableView beginUpdates];
[self.tableView endUpdates];
}
The condition given as example will work only if your existing cell layout respects the intrinsic content size of the text view, otherwise you will need to adapt the condition.
I have a UITableView with custom cells. When I rotate the device, the visible cells do not resize. Scrolling to new cells works fine and when I scroll back, all is well, but is there a way to get the initial, visible cells to smoothly resize on rotate?
[self.tableview reloadData] from the UITableViewController; works, but I don't think that is the optimal solution, as it requires a network call for data retrieval.
I am already calling the following code from the UITableViewController in order to layout some gradient layers I use, but this does nothing to resize the ImageView.
-(void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation
{
for (ProjectCell *cell in [[self.tableView.subviews firstObject] subviews]) {
cell.laidOut = NO;
[cell layoutSubviews];
}
}
Thanks
I believe this will do what you want:
[self.tableView beginUpdates];
[self.tableView reloadRowsAtIndexPaths:[self.tableView indexPathsForVisibleRows] withRowAnimation:UITableViewRowAnimationAutomatic];
[self.tableView endUpdates];
As long as the cells you want to reload are the visible cells.
I'm creating an app which contains a screen that shows a table view with custom cells. Each cell contains two labels and a subview, which further contains other subviews. I'm handling the click event on the cell to hide/show the subviews within the subview in the cell. How can I make it so that when I click on a single cell, the subview of all the cells will change?
It is like the Stock application in iPhone (using iOS 7), here is a screenshot:
As in the image above, when you click on any of the green box, all the boxes change to reflect the same type of value.
Please let me know if this approach is fine, or how this can be implemented.
There are a couple ways of doing this. The first that comes to mind would be to handle the different states within the UITableViewCell subclass, and just reload the visible cells:
[self.tableView reloadRowsAtIndexPaths:[self.tableView indexPathsForVisibleRows] withRowAnimation:UITableViewRowAnimationAutomatic];
If you're looking for more control over the process though, this process could also be achieved by changing the state future cells should load into, and then calling a method on every visible cell. This would provide you with an easy way to have complete control over how the contents of the cell update.
// Change flag for cell state then...
for (NSIndexPath *indexPath in [self.tableView indexPathsForVisibleRows]) {
if (condition) {
MyCellSubclass *cell = (MyCellSubclass *)[self.tableView cellForRowAtIndexPath:indexPath];
[cell someMethodWithArg:(id)state];
}
}
To do something as in Stock app you should handle two method cellForRowAtIndexPath: and click action method.
In cellForRowAtIndexPath: you should do the check which cell/button was pressed and display value base on it:
//Pseudo code
//cellForRowAtIndexPath
if (cellNo3Pressed)
{
//set up text with the right value.
}
else if (otherCell)
{
//set up text with the right value.
}
This will handle the cell which are not visible on the screen.
The next action method should handle nice animation on all of the visible cell:
NSArray *paths = [tableView indexPathsForVisibleRows];
for (NSIndexPath *path in paths)
{
UITableViewCell *cell = (UITableViewCell *)[self.tableView cellForRowAtIndexPath:path];
//Animate changes for cell
}
I am having a problem with the animation that UITableView provides when deleting and inserting a cell at the same time.
I have a list of cells lets call them questions. When one question is tapped it should add a cell beneath itself to display the answer to that question. If another answer is already being displayed that answer should be removed from the table.
The issue arises when the cell being inserted is very tall. If it is so tall that it's eventual bounds encroach into the space that the cell to be deleted takes up then during the animation we see the through the answer cell that we are deleting to see the cell that is being added
(see link to video of problem)
the is what my code looks like to move around the cells in the table view
[tableView beginUpdates];
if (deleteIndex) {
[tableView deleteRowsAtIndexPaths:#[deleteIndex] withRowAnimation:UITableViewRowAnimationTop];
}
if (addIndex) {
[tableView insertRowsAtIndexPaths:#[addIndex] withRowAnimation:UITableViewRowAnimationTop];
}
[tableView endUpdates];
I have tried
[tableView beginUpdates];
if (deleteIndex) {
[tableView deleteRowsAtIndexPaths:#[deleteIndex] withRowAnimation:UITableViewRowAnimationTop];
}
[tableView endUpdates];
//do stuff to update data source
[tableView beginUpdates];
if (addIndex) {
[tableView insertRowsAtIndexPaths:#[addIndex] withRowAnimation:UITableViewRowAnimationTop];
}
[tableView endUpdates];
But because there's no callback confirming that the table view did complete the first set update before starting the second block pretty much the same problem occurs. I know I could use perform selector with delay, but this seems like a bandaid.
Second I tried to encompass the animation in a block like this
[UIView animateWithDuration:0.0 animations:^{
[tableView beginUpdates];
if (deleteIndex) {
[tableView deleteRowsAtIndexPaths:#[deleteIndex] withRowAnimation:UITableViewRowAnimationTop];
}
[tableView endUpdates];
//do stuff to update data source
} completion:^(BOOL finished) {
[tableView beginUpdates];
if (addIndex) {
[tableView insertRowsAtIndexPaths:#[addIndex] withRowAnimation:UITableViewRowAnimationTop];
}
[tableView endUpdates];
}];
Again, because the completion block is fired after we call endUpdates not after the updates actually complete this does not resolve the use.
I also went into storyboard to be sure that clip subviews to bounds is selected for the cells. Again, this does not resolve the issue because we are not seeing a subview of the cell expand beyond it's expected height.
Looking closer at the animation by slowing it down it looks like apple inserts the cell to be added under the cells that won't be changed in the table and then moves the cells that will remain into their new positions. As a result the cell that was deleted becomes a transparent window where we see what they are actually doing under the hood.
The right approach would be to first remove the old answer and then after the animation is over add the new answer. There is a UITableViewDelegate method that is triggered after the cell animation is complete.
tableView:didEndDisplayingCell:forRowAtIndexPath:
Inserting the new answer row within this delegate method will result in the correct animation.
There are a few details to keep in mind- You'll need some logic to ensure that the correct cell height is returned and that the correct number of expected rows in the section is returned. Those data source methods are called after we remove our old answer in and again when we add the new one when we call
endUpdates
on the table view
Using anything other than UITableViewRowAnimationTop results in some strange animation behavior. This is because the content view of the cell is not what is being animated.
i solve same problem in my project by hide control like:
in ~cell.m have
(void)setSelected:(BOOL)selected animated:(BOOL)animated;
{
if(selected == NO)
{
self.selectedView.hidden = YES;
self.descriptionLabel.hidden = YES;
}
else
{
self.selectedView.hidden = NO;
self.descriptionLabel.hidden = NO;
}
}
may it still helpful