UITableViewCell custom, update UILabel - objective-c

I have custom UITableView and UIImageView, UILabel in it. On some notification I need to update only UILabel not UIImageView. If I do like this:
[self.tableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:[NSIndexPath indexPathForRow:idxCell inSection:0]] withRowAnimation:UITableViewRowAnimationFade];
All content of the cell is updating and I have flicker effect for UIImageView.
I've done like this:
MyTableViewCell *cell = (MyTableViewCell *)[self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:idx inSection:0]];
cell.counterLabel.text = [NSString stringWithFormat:#"%#", counter];
But I think that it is not good, because it is not corresponds to the MVC pattern. I guess I should update my Model object. I need advice, How I can do this?

You should update your NSArray or NSDictionary from where u fetch data. because when u reload row it again call the same dictionary or array and that has same value.
didSelectRowAtIndexPath at update the dictionary or array
and then reload the row.
cellForRowAtIndexPath update that NSArray or NSDictionary which fetch data for row.
May be it works

Related

How to find a cell textfield outside of a tableView delegate method?

I have a custom tableView cell with a textfield. Now, I want to recognise the textField outside of the tableView delegate method. I tried this-
UIView *cell = textField;
[cell isKindOfClass:[UITableViewCell class]];
but it's not working.
To get concrete cell from table view you need to know index path of this cell. Then you need to do:
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:0 inSection:0]; //you should set your indexes of row and section instead of zeros
MyCustomCell *cell = [myTableView cellForRowAtIndexPath:indexPath];
Now if text field is a property of your custom cell you can get it using cell.myTextField
For example:
UITextField *textField = cell.myTextField;
Now you can do what you want with it.

update a UILabel when the cell in UITableView is selected

A really simple question here. I have a label on one view and a UITableView on the previous view. I have got a segue triggered when the user selects the row and I want the label to be updated with the text from that row. Here's one example, the code is pretty obvious.
- (void)tableView:(UITableView *)tableView
didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
NSString *countrySelection;
switch (indexPath.section) {
case kFirstSection:
countrySelection = [[NSString alloc]
initWithFormat:#"The country you have chosen is %#",
[self.MyCountries objectAtIndex: indexPath.row]];
[self performSegueWithIdentifier:#"doneResults" sender:self];
self.countryResult.text = countrySelection;
break;
The label isn't updated and I just don't know what should be done.
Thanks in advance!
These kind of things really need to be set on the View Controller that owns them. Use a public property to pass the value of the selected country to that view controller as outlined below:
First, create a property called something like:
#property(non atomic,strong) NSString *countryChosen;
in the destination View Controller, and make sure to #synthesize it
No reason to create another property for the IndexPath. Just use
// Pass along the indexPath to the segue prepareForSegue method, since sender can be any object
[self performSegueWithIdentifier:#"doneResults" sender:indexPath];
in the didSelectRowAtIndexPath method.
Then in the prepareForSegueMethod:
MyDestinationViewController *mdvc = segue.destinationViewController;
NSIndexPath *indexPath = (NSIndexPath *)sender;
mdvc.countryChosen = [self.MyCountries objectAtIndex: indexPath.row]];
On the viewDidLoad event of the Destination VC, just use:
self.countryResult.text = countryChosen;
* EDIT *
To deal with a datasource that has multiple sections, just use the same logic that you have in the cellForRowAtIndexPath.
NSDictionary *selRow = [[self.countriesIndexArray valueForKey:[[[self.countriesIndexArray allKeys] sortedArrayUsingSelector:#selector(localizedCaseInsensitiveCompare:)] objectAtIndex:indexPath.section]] objectAtIndex:sindexPath.row];
Change this to suit your needs, but basically you are implementing the same logic that you would to display a cell, except you are specifying the indexPath (both section and row) that you want.
Then something like the following to set that property on the destination VC:
self.countryResult.text = [selRow valueForKey#"Country"];
In your current view controller create a new property for the indexPath of the cell the user selected, like this:
#property(strong,nonatomic) NSIndexPath *path;
#synthesize it and then when a user selects a row, set it by using
self.path = indexPath;
When you perform a segue, it will always call
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
So what you can do now when prepareForSegue: gets called, is the following:
/* if this is not the only segue you are performing you want to check on the identifier first to make sure this is the correct segue */
NSString *countrySelection = [[NSString alloc]
initWithFormat:#"The country you have chosen is %#",
[self.MyCountries objectAtIndex: self.path.row]];
segue.destinationViewController.countryResult.text = countrySelection;
/* after creating the text, set the indexPath to nil again because you don't have to keep it around anymore */
self.path = nil;
For this to work the view controller you want to show after selecting the cell must have a property for the UILabel, on which you are trying to set the text.

UITableViewController canceling Edit mode animation when calling [table reloadData] inside (void)setEditing

I'm having a little problem:
i made my setEditing method:
- (void)setEditing:(BOOL)editing animated:(BOOL)animate {
[super setEditing:editing animated:animate];
[mainTableView reloadData];
}
i'm using the reloadData to call: cellForRowAtIndexPath, and then, if the table is in edit mode, i'll change the appearance of my cell (hiding some labels, for example);
The problem is when i call [mainTableView reloadData] the Edit animation (the red circle slides from left to right and my cell slides to the right) doesn't exist. If don't call it, everything works ok, but i can't customize my cell, since cellForRowAtIndexPath is not called again.
Any suggestion to make it work ??
Thanks!
Maybe you will try to update your table with [tableView beginUpdates] and [tableView endUpdates]? Do you need to reload all the cells or only some of them?
EDIT: Here's the code for reloading all the cells:
[tableView beginUpdates];
NSMutableArray *updatedPaths = [NSMutableArray array];
for (NSNumber *row in yourArray) {
NSIndexPath *updatedPath = [NSIndexPath indexPathForRow:[row intValue] inSection:0];
[updatedPaths addObject:updatedPath];
}
[tableView reloadRowsAtIndexPaths:updatedPaths withRowAnimation:UITableViewRowAnimationFade];
[tableView endUpdates];
yourArray is NSArray instance where you store your cell.textLabel values or something like that...

Is it possible to refresh a single UITableViewCell in a UITableView?

I have a custom UITableView using UITableViewCells.
Each UITableViewCell has 2 buttons. Clicking these buttons will change an image in a UIImageView within the cell.
Is it possible to refresh each cell separately to display the new image?
Any help is appreciated.
Once you have the indexPath of your cell, you can do something like:
[self.tableView beginUpdates];
[self.tableView reloadRowsAtIndexPaths:[NSArray arrayWithObjects:indexPathOfYourCell, nil] withRowAnimation:UITableViewRowAnimationNone];
[self.tableView endUpdates];
In Xcode 4.6 and higher:
[self.tableView beginUpdates];
[self.tableView reloadRowsAtIndexPaths:#[indexPathOfYourCell] withRowAnimation:UITableViewRowAnimationNone];
[self.tableView endUpdates];
You can set whatever your like as animation effect, of course.
I tried just calling -[UITableView cellForRowAtIndexPath:], but that didn't work. But, the following works for me for example. I alloc and release the NSArray for tight memory management.
- (void)reloadRow0Section0 {
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:0 inSection:0];
NSArray *indexPaths = [[NSArray alloc] initWithObjects:indexPath, nil];
[self.tableView reloadRowsAtIndexPaths:indexPaths withRowAnimation:UITableViewRowAnimationNone];
[indexPaths release];
}
Swift:
func updateCell(path:Int){
let indexPath = NSIndexPath(forRow: path, inSection: 1)
tableView.beginUpdates()
tableView.reloadRowsAtIndexPaths([indexPath], withRowAnimation: UITableViewRowAnimation.Automatic) //try other animations
tableView.endUpdates()
}
reloadRowsAtIndexPaths: is fine, but still will force UITableViewDelegate methods to fire.
The simplest approach I can imagine is:
UITableViewCell* cell = [self.tableView cellForRowAtIndexPath:indexPath];
[self configureCell:cell forIndexPath:indexPath];
It's important to invoke your configureCell: implementation on main thread, as it wont work on non-UI thread (the same story with reloadData/reloadRowsAtIndexPaths:). Sometimes it might be helpful to add:
dispatch_async(dispatch_get_main_queue(), ^
{
[self configureCell:cell forIndexPath:indexPath];
});
It's also worth to avoid work that would be done outside of the currently visible view:
BOOL cellIsVisible = [[self.tableView indexPathsForVisibleRows] indexOfObject:indexPath] != NSNotFound;
if (cellIsVisible)
{
....
}
If you are using custom TableViewCells, the generic
[self.tableView reloadData];
does not effectively answer this question unless you leave the current view and come back. Neither does the first answer.
To successfully reload your first table view cell without switching views, use the following code:
//For iOS 5 and later
- (void)reloadTopCell {
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:0 inSection:0];
NSArray *indexPaths = [[NSArray alloc] initWithObjects:indexPath, nil];
[self.tableView reloadRowsAtIndexPaths:indexPaths withRowAnimation:UITableViewRowAnimationNone];
}
Insert the following refresh method which calls to the above method so you can custom reload only the top cell (or the entire table view if you wish):
- (void)refresh:(UIRefreshControl *)refreshControl {
//call to the method which will perform the function
[self reloadTopCell];
//finish refreshing
[refreshControl endRefreshing];
}
Now that you have that sorted, inside of your viewDidLoad add the following:
//refresh table view
UIRefreshControl *refreshControl = [[UIRefreshControl alloc] init];
[refreshControl addTarget:self action:#selector(refresh:) forControlEvents:UIControlEventValueChanged];
[self.tableView addSubview:refreshControl];
You now have a custom refresh table feature that will reload the top cell. To reload the entire table, add the
[self.tableView reloadData]; to your new refresh method.
If you wish to reload the data every time you switch views, implement the method:
//ensure that it reloads the table view data when switching to this view
- (void) viewWillAppear:(BOOL)animated {
[self.tableView reloadData];
}
Swift 3 :
tableView.beginUpdates()
tableView.reloadRows(at: [indexPath], with: .automatic)
tableView.endUpdates()
Here is a UITableView extension with Swift 5:
import UIKit
extension UITableView
{
func updateRow(row: Int, section: Int = 0)
{
let indexPath = IndexPath(row: row, section: section)
self.beginUpdates()
self.reloadRows(at: [indexPath], with: .automatic)
self.endUpdates()
}
}
Call with
self.tableView.updateRow(row: 1)
Just to update these answers slightly with the new literal syntax in iOS 6--you can use Paths = #[indexPath] for a single object, or Paths = #[indexPath1, indexPath2,...] for multiple objects.
Personally, I've found the literal syntax for arrays and dictionaries to be immensely useful and big time savers. It's just easier to read, for one thing. And it removes the need for a nil at the end of any multi-object list, which has always been a personal bugaboo. We all have our windmills to tilt with, yes? ;-)
Just thought I'd throw this into the mix. Hope it helps.
I need the upgrade cell but I want close the keyboard.
If I use
let indexPath = NSIndexPath(forRow: path, inSection: 1)
tableView.beginUpdates()
tableView.reloadRowsAtIndexPaths([indexPath], withRowAnimation: UITableViewRowAnimation.Automatic) //try other animations
tableView.endUpdates()
the keyboard disappear

Getting a specific UITableViewCell out of a UITableView

I was wondering, is there any methods to retrieve a specific cell from an UITableView?
For example, from where I am I can access my UITableView, so I would like to call something like cellForRowAtInteger:3 and that would return a cell so I could manipulate it.
Any ideas?
Thanks!
You can use -cellForRowAtIndexPath: method from UITableView. But remember that it will return nil if the cell is not visible.
Make your own function to create an NSIndexPath from your NSInteger.
-(UITableViewCell *) getCellAt:(NSInteger)index{
NSUInteger indexArr[] = {0,index}; // First one is the section, second the row
NSIndexPath *myPath = [NSIndexPath indexPathWithIndexes:indexArr length:2];
return [self tableView:[self tableView] cellForRowAtIndexPath:myPath];
}
You can then call it anywhere using:
UITableViewCell *hello = [self getCellAt:4]; // replace 4 with row number
If you have more than one section, then you need to change the 0 accordingly to the section.