How to manage custom cell label value in iOS? - ios

I’m building an iOS app using storyboards.I have used UITableViewController which has 6 custom cells each of which contains three IBOutlet buttons and one IBOutlet label.
When the user clicks on any button in one particular custom cell, then the value of only that particular cell label should change.
But what happens is, values of all labels in each custom cell get changed.
Here is my code:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *Cellidentifier1 = #"List";
Cell *cell1 = [tableView dequeueReusableCellWithIdentifier:Cellidentifier1 forIndexPath:indexPath];
cell1.selectionStyle = UITableViewCellSelectionStyleNone;
// Configure the cell...
long row = [indexPath row];
cell1.First.tag=row;//button iboutlet in cell
cell1.Second.tag=row;//button iboutlet in cell
cell1.Third.tag=row;//button iboutlet in cell
cell1.Level.tag=row;
cell1.Level.text=Skill;//label iboutlet in cell
return cell1;
}
-(IBAction)FirstAction:(id)sender{
Skill=#"first";
[self.tableView reloadData];
}
-(IBAction)SecondAction:(id)sender{
Skill=#"Second";
[self.tableView reloadData];
}
-(IBAction)ThirdAction:(id)sender{
Skill=#"Third";
[self.tableView reloadData];
}

A couple of issues:
You should have a model that reflects what's in your table view. Specifically, right now you have a single Skill value, but you have six rows. You want to maintain a model that is an array of values, something like:
#property (nonatomic, strong) NSMutableArray *values;
And
- (void)viewDidLoad {
[super viewDidLoad];
self.values = [#[#"first", #"first", #"first", #"first", #"first", #"first"] mutableCopy];
}
And
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *Cellidentifier1 = #"List";
Cell *cell = [tableView dequeueReusableCellWithIdentifier:Cellidentifier1 forIndexPath:indexPath];
NSString *value = self.values[indexPath.row];
cell.level.text = value;
return cell;
}
Note, no tag numbers needed.
When you tap on a button you must (a) identify what row in the table that corresponds to; (b) update that row in your model; and (c) reload that single row in the table (which will look up the value in the model):
- (void)updateCell:(UIView *)cell withValue:(NSString *)value {
NSParameterAssert(cell);
NSIndexPath *indexPath = [self.tableView indexPathForCell:(UITableViewCell *)cell];
if (cell) {
self.values[indexPath.row] = value;
[self.tableView reloadRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationFade];
}
}
- (IBAction)didTapFirstButton:(id)sender {
[self updateCell:[[sender superview] superview] withValue:#"first"];
}
- (IBAction)didTapSecondButton:(id)sender{
[self updateCell:[[sender superview] superview] withValue:#"second"];
}
- (IBAction)didTapThirdButton:(id)sender{
[self updateCell:[[sender superview] superview] withValue:#"third"];
}
Note, the sender is the button. So I get the table view cell by grabbing the superview (which is the cell's content view) and then grab its superview (the cell itself). If your view hierarchy is different, then change that as appropriate, but hopefully this illustrates the idea.

First you have to do is that use delegate method selectRowAtIndexPath when the row is selected then the indexPath selected row buttons can be used...
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath){
}
when the row is selected then that desired rows button functions will be performed

Try below code.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *Cellidentifier1 = #"List";
Cell *cell1 = [tableView dequeueReusableCellWithIdentifier:Cellidentifier1 forIndexPath:indexPath];
cell1.selectionStyle = UITableViewCellSelectionStyleNone;
// Configure the cell...
long row = [indexPath row];
cell1.First.tag=row;//button iboutlet in cell
cell1.Second.tag=row;//button iboutlet in cell
cell1.Third.tag=row;//button iboutlet in cell
cell1.Level.tag=row;
cell1.Level.text=Skill;//label iboutlet in cell
[cell1.First addTarget:self action:#selector(FirstAction:) forControlEvents:UIControlEventTouchUpInside];
[cell1.Second addTarget:self action:#selector(SecondAction:) forControlEvents:UIControlEventTouchUpInside];
[cell1.Third addTarget:self action:#selector(ThirdAction:) forControlEvents:UIControlEventTouchUpInside];
return cell1;
}

In your IBOutlet methods add this to find the indexPath for the row
CGPoint hitPoint = [sender convertPoint:CGPointZero toView:self.tableView];
NSIndexPath *hitIndex = [self.tableView indexPathForRowAtPoint:hitPoint];
instead of reloading the entire tableview you only want to reload the text for that cell at that specified indexPath
To reload only one cell, do this instead of [self.tableView reloadData];
[self.tableView reloadRowsAtIndexPaths:#[hitIndex] withRowAnimation:UITableViewRowAnimationNone];

There's a few ways to do it. But, from looking at your code I would make sure you get the right indexPath from the cell you selected. Assuming you have the target action already set up, you could rewrite your IBAction method like so:
-(IBAction)firstAction:(UIButton *)sender{
int row = sender.tag;
NSIndexPath *indexPath = [NSIndexPath indexPathForItem:row inSection:0];
Cell *cell = [self.tableView cellForRowAtIndexPath:indexPath];
Skill = #"first";
[self.tableView reloadRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationNone];
}
this is going to get the right cell and index path. Update that cell's label only and then reload it.

Related

Hide Label from prototype cell in TableView

I am working on Prototype TableView Cell where I have two Label over cell and one button behind the cell. I want to hide the second label on Hide Button click. I have tried a lot but did not get any appropriate info, Here is my code for displaying the Labels in Cell:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *simpleTableIdentifier = #"TableCell";
TableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:simpleTableIdentifier];
// Configure the cell...
int row = [indexPath row];
cell.EnglishLable.text = _English[row];
cell.UrduLable.text = _Urdu[row];
return cell;
}
Everything is fine, I just need to hide 'UrduLabel' in my Hide IBAction method.
- (IBAction)Hide:(id)sender {
static NSString *simpleTableIdentifier = #"TableCell";
UITableView *tableView = [[UITableView alloc]init];
CGPoint location = [gestureRecognizer locationInView:self.tableView];
NSIndexPath *indexPath = [self.tableView indexPathForRowAtPoint:location];
UITableViewCell *cell = [tableView cellForRowAtIndexPath:IndexPath];
BOOL *hideLabel;
hideLabel = YES;
[self.tableView reloadData];
UILabel *subtitle = (UILabel *)[cell.contentView viewWithTag:1];
subtitle.hidden = hideLabel;
}
Whats wrong in my Button method?
Try this code
- (IBAction)Hide:(id)sender {
CGPoint location = [gestureRecognizer locationInView:self.tableView];
NSIndexPath *indexPath = [self.tableView indexPathForRowAtPoint:location];
TableViewCell *cell = [self.tableView cellForRowAtIndexPath:indexPath];
cell.UrduLable.hidden = YES;
[tableView reloadRowsAtIndexPaths:#[indexPath] withRowAnimation:<any row animation you need>];
UILabel *subtitle = (UILabel *)[cell.contentView viewWithTag:1];
subtitle.hidden = hideLabel;
}
You just need to get the particular cell of which the button is clicked, hide the required label and reload the particular cell. And main thing is you need to get an instance of your prototype cell class, not of UITableViewCell
IN CASE ONE BUTTON IN THE VIEWCONTORLLER FOR ALL CELL
Incase of single button toggling the visibility of the UrduLabel, declare the -(IBAction)hide:(sender)id in the yourviewcontroller.m. connect the IBAction of the button from nib/storyboard to the -(IBAction)hide:(sender)id method.
Then follow the following steps
declare a BOOL property variable or ivar inside the view controller. named willShowUrdu.
implement the hide as following:
-(IBAction)hide:(id)sender
{
willShowUrdu = !(willShowUrdu);
[yourTableView reloadData];
}
inside the cellForRowAtIndexPath:
TableViewCell *cell = //whatever the way you get the cell
cell.UrduLabel.hidden = !willShowUrdu;
Thats how toggle the visibility of all UrduLabel under one IBAction.
IN CASE OF EACH BUTTON IN EACH CELL
There is a better way to de-centralize the control to each cell. Move the -(IBAction)hide:(sender)id method to the implementation in the TableViewCell's implementation.
Add the IBAction from nib/storyboard to the moved -(IBAction)hide:(sender)id method.
Take a IBOutlet of the UrduLabel, as seen in above code, it's already taken.
Then the implementation of -(IBAction)hide:(sender)id would be following:
-(IBAction)hide:(sender)id
{
//put your other logic here (if any)
self.UrduLabel.hidden = YES;
}
Welcome to IOS, Happy coding.

How can I create a 30 row UITableView, without doing it statically, and accept UITextField input on each row?

Normally, if I wanted to have a UITextField as a part of a UITableViewCell, I would likely use either a) static rows or b) I would create the cell in the storyboard, outlet the cell and outlet the field to my ViewController, and then drag the cell outside of the "Table View", but keeping it in the scene.
However, I need to create a View where I accept input from 28 various things. I don't want to outlet up 28 different UITextField's.
I want to do this dynamically, to make it easier. So I've created a custom UITableViewCell with a Label and UITextField.
My ViewController has two arrays.
#property (nonatomic, strong) NSArray *items;
#property (nonatomic, strong) NSArray *itemValues;
My cellForRowAtIndexPath looks something like this...
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
NSString *cellIdentifier = #"ItemCell";
MyItemTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier forIndexPath:indexPath];
if (!cell) {
cell = [[MyItemTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier];
[cell.itemValue addTarget:self action:#selector(textFieldDidChange:) forControlEvents:UIControlEventEditingChanged];
} else {
if (![cell.itemValue actionsForTarget:self forControlEvent:UIControlEventEditingChanged]) {
[cell.itemValue addTarget:self action:#selector(textFieldDidChange:) forControlEvents:UIControlEventEditingChanged];
}
}
cell.item.text = [self.items objectAtIndex:indexPath.row];
cell.itemValue.text = [self.itemValues objectAtIndex:indexPath.row];
return cell;
}
- (void)textFieldDidChange:(id)sender
{
NSLog(#"textFieldDidChange: %zd", [self.tableView indexPathForSelectedRow].row);
}
This is proving to be problematic. textFieldDidChange always returns [self.tableView indexPathForSelectedRow].row as 0, as the cell of course, has never been selected. I'm stumped on how I could even find out which row's UITextField has been edited, so I can update the corresponding itemValues array.
UITableView has a neat method that converts a point in the tableView to an indexPath, indexPathForRowAtPoint:.
First you have to convert the origin of your textField to the frame of the UITableView.
- (void)textFieldDidChange:(UITextField *)sender
{
CGPoint textFieldOriginInTableView = [sender convertPoint:CGPointZero toView:self.tableView];
NSIndexPath *indexPath = [self.tableView indexPathForRowAtPoint:textFieldOriginInTableView];
if (indexPath) {
NSLog(#"TextField at indexPath %# did change", indexPath);
}
else {
NSLog(#"Error: Can't calculate indexPath");
}
}
The easiest way to do this is to just tag the textfield with indexPath.row and get it back via [sender tag] in the delegate method.

Is it possible to refresh one view inside UITableViewCell

I have a standard table view. Inside each cell UITableViewCell I have a label. At any given time I need to constantly updating one label for the whole tableView (imagine a clock ticking for example).
In the cellForRowAtIndexPath I recognize which cell I need to update and assign weak property to the label inside such cell.
Then later, I call
label.text = #"New value";
and expect for that particular cell to refresh that particular label. I checked the weak reference and it's valid and text is changing, however cell is not getting refreshed.
I don't want to call reloadRowsAtIndexPaths, I just need to refresh one subview inside cell view. Is it possible?
UPDATE:
Here is my code:
This is property definition inside view controller .m file:
#property (nonatomic, weak) UILabel* runningTimerLabel;
I assign this property inside cellForIndex call:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
MKSlidingTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"container"];
TimerCell *foregroundCell = [tableView dequeueReusableCellWithIdentifier:kTimerCellId];
UITableViewCell *backgroundCell = [tableView dequeueReusableCellWithIdentifier:#"background"];
BOOL runningCell = (indexPath.row == self.runningTimer);
if (runningCell) {
self.runningTimerLabel = foregroundCell.time;
}
cell.foregroundView = foregroundCell;
cell.drawerView = backgroundCell;
cell.drawerRevealAmount = 146;
cell.forwardInvocationsToForegroundView = YES;
cell.delegate = self;
cell.tag = indexPath.row;
return cell;
}
Here is the method that updates the label:
- (void)refreshRunningTimerCell
{
self.runningTimerLabel.text = #"New text";
}
This method is getting called from a background thread:
- (void)refreshRunningTimer
{
while (1) {
sleep(_sleepTime);
[self performSelectorOnMainThread: #selector(refreshRunningTimerCell)
withObject:nil waitUntilDone: YES];
}
}
I really don't understand why you trying to get an reference on UIlabel.
You have self.runningTimer so you have a index row in your tableView, so just get
TimerCell *cell by cellForItemAtIndexPath and just update this cell.
I think you can do this:
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:0 inSection:0];
UITableViewCell *cell = [self.tableView cellForItemAtIndexPath:indexPath];
[cell updateLabel:#"New string"];

Radio button logic in UItableViewCells

Hey I'm working on a screen where user have option groups for example "Drink" which is title of section in my tableView, and their choices are "7up", "coke", etc which are cells of my table.
Now every Option Group choice (every cell in order words) has one radio button. I want to implement this. I'm facing problem if user selects any cell's radio button then other radio buttons should be deselected but how?
any help please
You should create a function to check your radio button from your custom cell and implements a delegate method to inform your TableViewController that your button on that cell was selected.
Your TableViewController needs to implements that delegate (dont forget to set each cell.delegate = self).
Then in your delegate method you create a loop to uncheck all of the radio buttons of the cells in the section except the cell you just checked.
Something like that :
This is a custom UITableViewCell with a button.
The images checked and uncheck need to look like a radio button checked and uncheked
Here is the .h file :
//RadioCell.h
#protocol RadioCellDelegate <NSObject>
-(void) myRadioCellDelegateDidCheckRadioButton:(RadioCell*)checkedCell;
#end
#interface RadioCell : UITableViewCell
-(void) unCheckRadio;
#property (nonatomic, weak) id <RadioCellDelegate> delegate;
#end
This is the .m file of RadioCell
//RadioCell.m
#property (nonatomic, assign) UIButton myRadio;
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString*)reuseIdentifier
_myRadio = [UIButton buttonWithType:UIButtonTypeCustom];
[_myRadio setImage:[UIImage imageNamed:#"uncheck"] forState:UIControlStateNormal];
[_myRadio setImage:[UIImage imageNamed:#"check"] UIControlStateSelected];
[_myRadio addTarget:self action:#selector(radioTouched)orControlEvents:UIControlEventTouchUpInside];
_myRadio.isSelected = NO;
//don't forget to set _myRadio frame
[self addSubview:_myRadio];
}
-(void) checkRadio {
_myradio.isSelected = YES;
}
-(void) unCheckRadio {
_myradio.isSelected = NO;
}
-(void) radioTouched {
if(_myradio.isSelected == YES) {
return;
}
else {
[self checkRadio]
[_delegate myRadioCellDelegateDidCheckRadioButton:self];
}
}
Now just adapt your tableview controller with RadioCell (in .m file)
//MyTableViewController.m
#interface MyTableViewController () <RadioCellDelegate>
#end
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"RadioCell";
RadioCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[RadioCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
cell.textLabel = #"Coke"; //or whatever you want
cell.delegate = self;
return cell;
}
-(void) myRadioCellDelegateDidCheckRadioButton:(RadioCell*)checkedCell {
NSIndexPath *checkPath = [self.tableView indexPathForCell:checkedCell];
for (int section = 0; section < [self.tableView numberOfSections]; section++) {
if(section == checkPath.section) {
for (int row = 0; row < [self.tableView numberOfRowsInSection:section]; row++) {
NSIndexPath* cellPath = [NSIndexPath indexPathForRow:row inSection:section];
RadioCell* cell = (CustomCell*)[tableView cellForRowAtIndexPath:cellPath];
if(checkPath.row != cellPath.row) {
[cell unCheckRadio];
}
}
}
}
}
Simple solution for a 2-option radio button UITableView (but you get the idea):
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[tableView deselectRowAtIndexPath:indexPath animated:YES];
NSIndexPath *newIP;
if (!indexPath.row)
newIP = [NSIndexPath indexPathForRow:indexPath.row+1 inSection:0];
else
newIP = [NSIndexPath indexPathForRow:indexPath.row-1 inSection:0];
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
if (cell.accessoryType == UITableViewCellAccessoryCheckmark)
cell.accessoryType = UITableViewCellAccessoryNone;
else{
cell.accessoryType = UITableViewCellAccessoryCheckmark;
UITableViewCell *newCell = [tableView cellForRowAtIndexPath:newIP];
newCell.accessoryType = UITableViewCellAccessoryNone;
}
}
One solution would be to make use of the table views native selection capabilities.
In a standard UITableView it's only possible to have one row selected at a time and you can use this to your advantage. By setting "Selection" in storyboard to "None" the selection of a row will not be visible.
Now you can implement your own selection display. You can override the method -(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath to update your cell when it gets selected.
And you can override the method -(void)tableView:(UITableView *)tableView didDeselectRowAtIndexPath:(NSIndexPath *)indexPath to change the cell when it's no longer selected.
UITableViewDelegate automatically calls didSelectRowAtIndexPath on the old selection, when a new selection is made, keeping the selection unique like radio buttons.
I put together a little sample project for you to try, you can download it here.
Hopefully, I have been at least a bit helpful.
Cheers!

UITableView indexPath.row issue

I am using a tableView which loads a custom UITableViewCell with a "Tap" button inside it. A method is called when user clicks the button.
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{...
[btnRowTap addTarget:self action:#selector(didButtonTouchUpInside:) forControlEvents:UIControlEventTouchDown];
...
return cell;
}
In the didButtonTouchUpInside method, Im trying to retrieve the value of row selected in the following way:
-(IBAction)didButtonTouchUpInside:(id)sender{
UIButton *btn = (UIButton *) sender;
UITableViewCell *cell = (UITableViewCell *)btn.superview;
NSIndexPath *indexPath = [matchingCustTable indexPathForCell:cell];
NSLog(#"%d",indexPath.row);
}
The problem is that on clicking button at any row, I am getting the same value of 0 every time.
Where am I going wrong?
You must NOT rely on the view hierarchy of a UITableViewCell. This approach will fail in iOS7, because iOS7 changes the view hierarchy of the cell. There will be an additional view between your button and the UITableViewCell.
There are better ways to handle this.
Convert the button frame so it is relative to the tableview
ask the tableView for the indexPath at the origin of the new frame
.
-(IBAction)didButtonTouchUpInside:(id)sender{
UIButton *btn = (UIButton *) sender;
CGRect buttonFrameInTableView = [btn convertRect:btn.bounds toView:matchingCustTable];
NSIndexPath *indexPath = [matchingCustTable indexPathForRowAtPoint:buttonFrameInTableView.origin];
NSLog(#"%d",indexPath.row);
}
set Button tag in to cellForRowAtIndexPath method Befor setting Method like
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{...
btnRowTap.tag=indexPath.row
[btnRowTap addTarget:self action:#selector(didButtonTouchUpInside:) forControlEvents:UIControlEventTouchDown];
...
return cell;
}
and your tapped cell getting like this:-
-(IBAction)didButtonTouchUpInside:(id)sender{
{
UIButton *button = (UIButton*)sender;
NSIndexPath *indPath = [NSIndexPath indexPathForRow:button.tag inSection:0];
//Type cast it to CustomCell
UITableViewCell *cell = (UITableViewCell*)[tblView1 cellForRowAtIndexPath:indPath];
NSLog(#"%d",indPath.row);
}
try like this,if you are adding button to cell content view then use below code.
UITableViewCell *buttonCell = (UITableViewCell *)sender.superview.superview;
UITableView* table1 = (UITableView *)[buttonCell superview];
NSIndexPath* pathOfTheCell = [table1 indexPathForCell:buttonCell];
int rowOfTheCell = [pathOfTheCell row];
int sectionOfTheCell = [pathOfTheCell section];
btn.superview is contentView of UITableviewCell. Use btn.superview.superview instead.
if you already knew the value inside the cell, then
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
UITableViewCell *currentCell = [self tableView:tableView cellForRowAtIndexPath:indexPath];
if ([currentCell.textLabel.text isEqualToString:#"Your Cell Text value" ]){
//Do theStuff here
}
}
here is the code for your ibAction.you dont need to set any tag or anything else
-(IBAction)didButtonTouchUpInside:(id)sender{
NSIndexPath *indexPath =
[tbl
indexPathForCell:(UITableViewCell *)[[sender superview] superview]];
}

Resources