Highlight selected tableViewCell to match with UITextfield.text - ios

I have a tableview with values that could be the same.
When selecting the cell, the textfield value will be populated with the selected cell.
I want to only highlight the row that was selected rather than highlight all the values that are the same as the textfield.
My approach currently:
where data is an array of possible values
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:#"identifierCell"];
}
NSString *value = ([self.data objectAtIndex:indexPath.row]);
NSString *selectedValue = textField.text;
if ([value floatValue] == [selectedValue floatValue]) {
cell.backgroundColor = [UIColor colorWithRed:220.0/255.0 green:220.0/255.0 blue:220.0/255.0 alpha:1.0];
cell.textLabel.textColor = [UIColor whiteColor];
}
else {
cell.backgroundColor = [UIColor clearColor];
cell.textLabel.textColor = [UIColor blackColor];
}
Side note: I have a couple of these drop down fields that can change the textfield text depending on which row was selected. (i.e. if row 1 was selected in the other dropdown, then 1.391 would be populated in the textfield text.
Not sure how relevant that will be.
How would I check only the row that was selected and populate the textfield per the image below?
Update: Thanks to #k06a using indexPath is a step forward, however the problem now arises where when if row A is selected, the indexPath for B should not change. I was thinking of setting 2 different indexPath variables and on didSelectRowAt only set those for the methods I'm calling to change the other respective values

You can enumerate all cells within a loop to select and deselect rows based on selectedValue and value equality. This loop will deselect previous row, and select a new one. And not forget to setup initial state after dequeing reusable cell. All cells can be fetched with: self.tableView.visibleCells
Or you can just deselect previous cell by remembering its indexPath. And select a new one and remember its indexPath. This is the most effective variant.
Or you can just call [self.tableView reloadData] when wanna change selection. This will be less effective, but shorter and easier to implement.
UPDATE:
Just use different condition of cell selection not basing on value but basing on indexPath. And remind me why do you need manual selection of cell? I mean manual setting backgroundColor and textColor can be automatic by [UITableViewCell setSelected:] selection.

My solution thanks to #k06a
NSIndexPath *selectedIndexPathA;
NSIndexPath *selectedIndexPathB;
In didSelectRowAtIndexPath I pass in the indexPath to the method that will set these values:
[self setOtherFieldsWithIndexPath:indexPath];
and in setOtherFieldsWithIndexPath method
if (rowA) { selectedIndexPathA = indexPath; }
else if (rowB) { selectedIndexPathB = indexPath; }
else {...}
then finally in cellForRowAtIndexPath
if (selectedIndexPathA.row == indexPath.row) {
cell.backgroundColor = [UIColor colorWithRed:220.0/255.0 green:220.0/255.0 blue:220.0/255.0 alpha:1.0];
cell.textLabel.textColor = [UIColor whiteColor];
}
else if (selectedIndexPathB.row == indexPath.row) {
cell.backgroundColor = [UIColor colorWithRed:220.0/255.0 green:220.0/255.0 blue:220.0/255.0 alpha:1.0];
cell.textLabel.textColor = [UIColor whiteColor];
}
else {
cell.backgroundColor = [UIColor clearColor];
cell.textLabel.textColor = [UIColor blackColor];
}
Not sure if this is the best solution but, it currently works the way I want it to work.

Related

Dynamically changing textColor of a UITableViewCell's text

I have a custom UITableViewController that has one row, and only two UITableViewCells.
I'm trying to dynamically set the color of my UITableViewCell's text (cell.textLabel.textColor) based upon a few things:
1) If this is the first time launching, the first cell's text color should be [UIColor whiteColor], and the second cell's text color should be [UIColor grey1Color]
2) If the user selects a cell and leaves the screen and then returns to the table-view, the last selected cell's text color should be [UIColor whiteColor], and text color of the cell that was not selected should be [UIColor grey1Color].
Whenever a cell is selected, a property is updated; myCellTextValue. This is done to make a few API calls outside of this particular table-view.
My idea for implementing the logic above was to use this property for determining what color the cell's text should be. My code attempt below is in cellForRowAtIndexPath:
if (cell.textLabel.text == self.myCellTextValue) {
cell.textLabel.textColor = [UIColor whiteColor];
} else {
cell.textLabel.textColor = [UIColor grey1Color];
}
}
However, both cell's text color is always grey. I'm sure this mostly has to do with misunderstanding UITableViewCell creation in someway. Does anyone have any pointers on how to implement this properly? Thank you!
EDIT: Following #Gismay's comment below, I tried the code below; but got the same result:
if ([cell.textLabel.text isEqualToString:self.myCellTextValue]) {
cell.textLabel.textColor = [UIColor whiteColor];
} else {
cell.textLabel.textColor = [UIColor grey1Color];
}
EDIT 2: I also tried wrapping the code above in a check to make sure we're only looking at one cell at a time, but this had no effect either:
if((indexPath.section==0)&&(indexPath.row==0)){
if ([cell.textLabel.text isEqualToString:self.myCellTextValue]) {
cell.textLabel.textColor = [UIColor whiteColor];
} else {
cell.textLabel.textColor = [UIColor grey1Color];
}
} else if((indexPath.section==0)&&(indexPath.row==1)){
if ([cell.textLabel.text isEqualToString:self.myCellTextValue]) {
cell.textLabel.textColor = [UIColor whiteColor];
} else {
cell.textLabel.textColor = [UIColor grey1Color];
}
}
You can do something like:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
//change your cell text color here
cell= [tableView cellForRowAtIndexPath:indexPath]
cell.textLabel.textColor = [UIColor whiteColor];
}
- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
{
if (cell.isSelected == YES)
{
cell.textLabel.textColor = [UIColor grey1Color];
}
else
{
cell.textLabel.textColor = [UIColor whiteColor];
}
}
Another way is to subclass the tableview cell and implement the following methods:
- (void)setSelected:(BOOL)selected animated:(BOOL)animated
{
[super setSelected:selected animated:animated];
[self updateTextColor:selected];
}
- (void)setHighlighted:(BOOL)highlighted animated:(BOOL)animated {
[super setHighlighted:highlighted animated:animated];
[self updateTextColor:highlighted];
}
- (void)updateTextColor:(BOOL)isSelected {
labelA= //get reference of the cell textlabel
if (labelA) {
if (isSelected) {
[labelA setTextColor:[UIColor whiteColor]];
} else {
[labelA setTextColor:[UIColor greyColor]];
}
}
}
you could use selectedRowIndex as a class-level variable, and just keep updating that every time you select a row. Initially - whether selected or not - this should be 0, so that the first row is different
I think you don't want to use the text value because it may not be unique
What happens when you make multiple selections without leaving the view? Presumably you need to clear the white text from the old row, and set it again on the new one?
Easiest way to implement that is going to be reloading the tableView on each selection - but if that takes too long, you could always reload the individual rows - on row selection set selectedRowIndexPrevious before you update selectedRowIndex, and reload both of those rows. The previous row will redraw in grey, and the new one in white
here's how you might implement some of this
class MyViewController : UIViewController
{
// define the variables to keep track of row selection here
var selectedRowIndex : Int = 0
var selectedRowIndexPrevious : Int = -1
// the rest of your code
and then you need to update the selectedRow variables
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
// Uncheck the previous checked row
selectedRowIndexPrevious = selectedRowIndex
// **UPDATED** need to set the selectedRowIndex
selectedRowIndex = indexPath.row
// **UPDATED**
// reload needs an array of indexPath
// so we can supply the previous selection AND the current one
NSIndexPath* rowToReloadPrevious = [NSIndexPath indexPathForRow: selectedRowIndexPrevious inSection:0];
NSIndexPath* rowToReloadNew = [NSIndexPath indexPathForRow: selectedRowIndex inSection:0];
NSArray* rowsToReload = [NSArray arrayWithObjects:rowToReloadPrevious, rowToReloadNew, nil];
[tableView reloadRowsAtIndexPaths:rowsToReload withRowAnimation:UITableViewRowAnimationNone];
}
within the cellForRowAtIndexPath, you just need to look at selectedRowIndex instead of checking the text
if (indexPath.row == selectedRowIndex) {
cell.textLabel.textColor = [UIColor whiteColor];
} else {
cell.textLabel.textColor = [UIColor grey1Color];
}
}

Selecting one cell falsely marks several other cells as "selected"

In my app I have a UITableView which consists of multiple custom UITableViewCells. In my storyboard I ticked Single Selection because I only want one selected cell at a time. In my ViewController I override didSelectRowAtIndexPath and didDeselectRowAtIndexPath like this:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath*)indexPath {
TextsTableViewCell *selectedCell = [tableView cellForRowAtIndexPath:indexPath];
selectedCell.backgroundColor = [UIColor blueColor];
selectedCell.textLabel.textColor = [UIColor whiteColor];
self.chosenTextId = [NSNumber numberWithInteger:[selectedCell tag]];
self.chosenStaticText = [selectedCell.textLabel text];
NSLog(#"The textID: %# and the text: %#", self.chosenTextId, self.chosenStaticText);
}
-(void)tableView:(UITableView *)tableView didDeselectRowAtIndexPath:(NSIndexPath*)indexPath {
TextsTableViewCell *selectedCell = [tableView cellForRowAtIndexPath:indexPath];
selectedCell.backgroundColor = [UIColor whiteColor];
selectedCell.textLabel.textColor = [UIColor blackColor];
self.chosenTextId = nil;
self.chosenStaticText = nil;
}
As long as I am not scrolling everything seems to be working fine (although I can't check this without scrolling). My logs only contain the correctly selected cells I clicked. But when I scroll down there are other cells which are selected, too. Does anybody know what might go wrong?
The issue here is your UITableViewCell's are being reused and this keeps their state, hence why this only happens when you're scrolling. You need to store your selected cells indexPaths in a storage collection object like NSMutableArray. Then in your cellForRowAtIndexPath you can check if the cell should be selected and if it is, select it, if it isn't make sure its not selected.

Setting up a menu in UITableView showing the current item

I am using a slide in menu style which loads a UITableView. - ECSlidingViewController
I have about 7 cells in a table view setup as follows:
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = (UITableViewCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
cell.contentView.backgroundColor = [UIColor colorWithRed:75.0/255.0 green:83.0/255.0 blue:102.0/255.0 alpha:1.0];
UIView *topSplitterBar = [[UIView alloc] initWithFrame:CGRectMake(0, 0, cell.bounds.size.width, 1)];
topSplitterBar.backgroundColor = [UIColor colorWithRed:62.0/255.0 green:69.0/255.0 blue:85.0/255.0 alpha:1];
[cell.contentView addSubview:topSplitterBar];
cell.textLabel.backgroundColor = [UIColor clearColor];
cell.textLabel.textColor = [UIColor colorWithRed:196.0/255.0 green:204.0/255.0 blue:218.0/255.0 alpha:1];
cell.textLabel.font = [UIFont systemFontOfSize:18.0f];
cell.textLabel.shadowColor = [UIColor colorWithRed:27.0/255.0 green:31.0/255.0 blue:41.0/255.0 alpha:1];
cell.textLabel.shadowOffset = CGSizeMake(0, 1);
UIView *selectedBg = [[UIView alloc] initWithFrame:cell.frame];
selectedBg.backgroundColor = [UIColor colorWithRed:50.0/255.0 green:56.0/255.0 blue:73.0/255.0 alpha:1];
cell.selectedBackgroundView = selectedBg;
What would be the best way to show a cell as the selectedBg if that is the currently displayed controller?
I can access the following for example:
if ([self.slidingViewController.topViewController isKindOfClass:[MESHomeViewController class]]) {
However, I am not sure where would be best practice to set this up? I can do it in the switch case for the cell label setup... For example:
switch ( indexPath.row ) {
case 0: {
if ([self.slidingViewController.topViewController isKindOfClass:[MESHomeViewController class]]) {
cell.contentView.backgroundColor = [UIColor colorWithRed:50.0/255.0 green:56.0/255.0 blue:73.0/255.0 alpha:1];
}
cell.textLabel.text = NSLocalizedString(#"LMGames", #"Left Menu - Games");
break ;
However, when a new item is selected from the menu I would need to reload the table each time, is that good? Completing a self.tableView reloadData each time a cell is selected, or is there a better way to approach this?
Two ideas for you:
Set the selectedBg in the didSelectRowAtIndexPath method in order to set the selected cell.
Keep an integer reference to the currently selected row and the previously selected row and then refresh only those rows by using a method similar to: How to reload and animate just one UITableView cell/row?
I hope that helps!

placeholder for empty uitableview section

After lots of searching, I can't seem to find what I'm looking for.
I have a UITableview where some of the sections may be blank to begin with. Here's a picture to help get an idea of what I'm talking about. I want to have some TEXT (not a table cell) in the middle between the footer and the header. Is there anything that I may have overlooked?
What I did was create a UILabel with the same size as the tableview and add it to the tableview, something like:
UILabel* emptyLabel = [[UILabel alloc] init];
emptyLabel.textAlignment = UITextAlignmentCenter;
emptyLabel.backgroundColor = [UIColor clearColor];
emptyLabel.frame = self.tableView.bounds;
emptyLabel.text = #"Empty";
[self.tableView addSubview:emptyLabel];
You can then use the hidden property to show it or hide it, e.g. emptyLabel.hidden = TRUE;
Because of the nature of UITableViews, I'm not sure you could achieve replacing the UITableCell view with something else. However there's no reason you can't completely alter the table cell itself to look like a plain UITextLabel and not a cell! You could do something like this:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
/* Initial setup here... */
if (thisCellHasNoDataYet) {
// Prevent highlight on tap
[cell setSelectionStyle:UITableViewCellSelectionStyleNone];
cell.backgroundColor = [UIColor clearColor];
cell.textLabel.textColor = [UIColor blackColor];
cell.textLabel.text = #"TEXT YOU WANT THE 'CELL' TO DISPLAY";
// etc...
}
else {
// Otherwise we have data to display, set normal cell mode
[cell setSelectionStyle:UITableViewCellSelectionStyleBlue];
cell.backgroundColor = [UIColor whiteColor];
// etc...
}
The benefit here is that once your condition is met, you just have to set the boolean (I used thisCellHasNoDataYet) to TRUE and call reloadData on your table!

Grouped UITableView rows with varying heights causes content shuffle

Tried my hardest to find the answer here first, but I'm stuck. I have a UITableView set with UITableViewStyleGrouped with 4 sections, each with one or two rows. In two of the sections I needed a row to be a larger height to hold the content I'm sticking in there.
Looks nice except when I scroll up and down, textLablels, accessories and extra subviews start to shift into different rowss and I can't figure out why.
This screenshot shows the table view when it loads and then after I scroll up and down a few times. Each time I scroll different row content shuffles.
I thought I read something about this being an issue with the grouped style. Sure enough, I don't see this issue if I change the table style to default. Am I not allowed to dynamically set the height for some rows when using the grouped style?
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
if (indexPath.section == kSection_Info && indexPath.row == kSectionRow_InfoPhoto)
{
return 84.0;
}
else if (indexPath.section == kSection_Level && indexPath.row == kSectionRow_LevelLevel)
{
return 70.0;
}
return 44.0;
}
I'm setting up each row manually in celForRowAtIndexPath:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"RecipientEntryCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil)
{
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
switch (indexPath.section)
{
case kSection_Info:
{
switch (indexPath.row)
{
case kSectionRow_InfoName:
{
cell.textLabel.text = #"Name";
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
self.nameLabel = [[UILabel alloc] initWithFrame:CGRectMake(74, 8, 195, 25)];
self.nameLabel.textAlignment = UITextAlignmentRight;
self.nameLabel.font = [UIFont systemFontOfSize:16];
self.nameLabel.textColor = [UIColor blueColor];
self.nameLabel.text = self.currentRecipient.fullName;
[cell.contentView addSubview:self.nameLabel];
break;
}
case kSectionRow_InfoPhoto:
{
cell.textLabel.text = #"Photo";
self.imageButton = [UIButton buttonWithType:UIButtonTypeCustom];
self.imageButton.frame = CGRectMake(10, 14, 64, 64);
[self.imageButton addTarget:self action:#selector(onImageButtonTouch:) forControlEvents:UIControlEventTouchUpInside];
NSString *imageName = #"add_image.png";
UIImage *thumb = [UIImage imageNamed:imageName];
[self.imageButton setImage:thumb forState:UIControlStateNormal];
cell.accessoryView = self.imageButton;
break;
}
default:
{
break;
}
}
break;
}
case kSection_List:
{
switch (indexPath.row)
{
case kSectionRow_ListHasList:
{
cell.textLabel.text = #"Is Listed";
self.listSwitch = [[UISwitch alloc] initWithFrame:CGRectZero];
cell.accessoryView = self.listSwitch;
break;
}
case kSectionRow_ListBudget:
{
cell.textLabel.text = #"List Amount";
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
self.budgetLabel = [[UILabel alloc] initWithFrame:CGRectMake(124, 8, 145, 25)];
self.budgetLabel.textAlignment = UITextAlignmentRight;
self.budgetLabel.font = [UIFont systemFontOfSize:16];
self.budgetLabel.textColor = [UIColor blueColor];
self.budgetLabel.text = [#"$" stringByAppendingFormat:#"%0.2f", [self.currentRecipient.budget floatValue]];
[cell.contentView addSubview:self.budgetLabel];
break;
}
default:
{
break;
}
}
break;
}
case kSection_Level:
{
switch (indexPath.row)
{
case kSectionRow_LevelLevel:
{
self.levelSlider = [[UISlider alloc] initWithFrame:CGRectMake(8, 2, 284, 40)];
self.levelSlider.minimumValue = 0.0;
self.levelSlider.maximumValue = 100.0;
self.levelSlider.continuous = YES;
UIImage *meterImage = [UIImage imageNamed:#"meter_labels.png"];
UIImageView *meterView = [[UIImageView alloc] initWithFrame:CGRectMake(8, 32, 284, 24)];
[meterView setImage:meterImage];
[cell.contentView addSubview:self.levelSlider];
[cell.contentView addSubview:meterView];
[meterImage release];
break;
}
case kSectionRow_LevelHasLevel:
{
cell.textLabel.text = #"Show Level";
self.levelSwitch = [[[UISwitch alloc] initWithFrame:CGRectZero] autorelease];
cell.accessoryView = self.levelSwitch;
break;
}
default:
{
break;
}
}
break;
}
case kSection_RecipientDelete:
{
cell.textLabel.text = #"Delete Recipient";
cell.textLabel.textAlignment = UITextAlignmentCenter;
cell.textLabel.textColor = [UIColor blueColor];
break;
}
default:
{
break;
}
}
}
return cell;
}
The "content shuffling" you see is most likely due to improper handling of cell re-use.
There is no issue specifically with the grouped style. The problem is more likely to manifest itself in that style because fewer cells fit in the screen which requires more scrolling and requires more cell re-use.
You are setting up the cell contents only when creating cells (when cell == nil). When a cell scrolls off the screen, it goes into the re-use queue. The row at the other end that is now visible re-uses the cell view that is in the re-use queue. The re-used cell contains the contents of some other row.
When all the cells are alike (at least in regard to the UI controls and not the data), this isn't a problem. When all or some of the cells are different, you get controls appearing where you don't expect them.
Because you only have a small number of rows and each one is layed out differently, the quick (and perhaps dirty) solution is to use a different re-use identifier for each cell like this:
NSString *CellIdentifier =
[NSString stringWithFormat:#"RecipientEntryCell-%d-%d",
indexPath.section, indexPath.row];
This is not at all recommended if the table view is going to have lots of different cells since every cell for every row in the table will be in memory at the same time (instead of just the few on the screen). Do not use a unique identifier as a way to solve any and all cell re-use problems.
The Table View Programming Guide shows an alternate way to design table views like this where you have a few cells with different layouts. See "The Technique for Static Row Content" on this page.
Ultimately, it's better if you understand that the table view re-uses cells for good reasons and not try to workaround it all the time. Generally, the cellForRowAtIndexPath should look like this:
static NSString *CellIdentifier = #"CellIdentifier";
UITableViewCell *cell = [tableView dequeueReusableCell...
if (cell == nil) {
//Create cell...
//Set UI content that applies to all rows (if any)...
}
else {
//Cell is being re-used.
//Remove UI content that doesn't apply to this row...
}
//Add UI content that applies only to this row...
//Copy values from data source to cell UI controls...
return cell;
If you construct the cells as shown above, do not maintain class-level references to UI controls inside cells (like nameLabel, imageButton, etc). Instead, the control values should be set in cellForRowAtIndexPath from a backing data variable (model) and the value should be read or saved back to the backing data variable as soon as the UI control changes.
For example, instead of storing a reference to a UISlider in a cell, store the current slider value as a float ivar. Initialize the float where appropriate (eg. viewDidLoad). In cellForRowAtIndexPath, create the UISlider, set its value using the float ivar, and have the slider call a method when its value changes. In that method, copy the slider's value to the float ivar.
Hope this helps.

Resources