Can't change image in selected custom cell - ios

I created a custom cell to display a text and 2 images, when the user selects the cell, the image is supposed to change. I can access the properties of the cell, but can't change them :
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
[tableView deselectRowAtIndexPath:indexPath animated:YES];
CustomCell *cell = (CustomCell *)[self.tableView cellForRowAtIndexPath:indexPath];
cell.check.image = setImage:[UIImage imageNamed:#"1355327732_checkbox-checked"];
[cell.check setImage:[UIImage imageNamed:#"1355327732_checkbox-checked"]]; }
cell.check is a UIImageView
Am i missing something?

If you are using a custom cell then you can override the function setSelected:animated: like so...
- (void)setSelected:(BOOL)selected animated:(BOOL)animated
{
[super setSelected:selected animated:animated];
// Configure the view for the selected state
if (selected) {
self.check.image = [UIImage imageNamed:#"1355327732_checkbox-checked"];
} else {
self.check.image = nil;
}
}
Then you don't have to do anything in the tableView code to change this. It will just work.
A better alternative to this is to keep the image the same inside self.check. Then you can just set hidden to YES or NO accordingly. This will be more performant also.
To have this so that you get multiple selections from the table then in the TableViewController put...
self.tableView.allowsMultipleSelection = YES;
This will set it so that you can select multiple rows. One tap selects and another tap deselects.
To get the selected rows you can run...
NSArray *selectedRows = [self.tableView indexPathsForSelectedRows];

Why are you calling setImage and cell.check.image on the same line? Try this and see if the result is the same.
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
[tableView deselectRowAtIndexPath:indexPath animated:YES];
CustomCell *cell = (CustomCell *)[self.tableView cellForRowAtIndexPath:indexPath];
//cell.check.image = setImage:[UIImage imageNamed:#"1355327732_checkbox-checked"];
[cell.check setImage:[UIImage imageNamed:#"1355327732_checkbox-checked"]];
}

I noticed two issues in your code
1) This code :
cell.check.image= setImage:[UIImage imageNamed:#"1355327732_checkbox-checked"];
2) There is no extension provided for the image
Replace it with:
cell.check.image= [UIImage imageNamed:#"1355327732_checkbox-checked.png"];

Related

TableView deselectRowAtIndexPath does not call setHighlighted

I'm trying to change states of the label in my table view cell.
I want to keep a cell selected while I push a different view controller and move back to the view controller with my tableview.
When I select another row I want to remove highlight of previously selected row's lable (deselect the previously selected row) and Highlight the current row's label.
Is - (void)deselectRowAtIndexPath:(NSIndexPath *)indexPath animated:(BOOL)animated supposed to call - (void)setHighlighted:(BOOL)highlighted animated:(BOOL)animated with highlighted 'NO' for that Cell ?
Note: I'm not UITableViewController.
Keep cell selected:
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
cell.selected = YES;
//Other code
}
Ensure that the class cell don't have selected = NONE in interface builder.
Unselect last row:
- (NSIndexPath *)tableView:(UITableView *)tableView willSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
NSIndexPath *currentSelectedIndexPath = [tableView indexPathForSelectedRow];
if (currentSelectedIndexPath)
{
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
cell.selected = NO;
}
return indexPath;
}
First, you should set TableviewCell properly [cell setSelectionStyle:UITableViewCellSelectionStyleGray]; in method :
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
Other then this, you can set custom background of selected cell by
UIView *bgColorView = [[UIView alloc] init];
bgColorView.backgroundColor = [UIColor colorWithWhite:0.97 alpha:1.0];
bgColorView.layer.masksToBounds = YES;
[cell setSelectedBackgroundView:bgColorView];
For Your highlight selected cell problem, you can use Simple flag like
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
NSIndexPath *ip= [NSIndexPath indexPathForRow:flagForLastSelectedRow inSection:0];
[tableView deselectRowAtIndexPath:indexPath animated:YES];
flagForLastSelectedRow=indexPath.row;
}

Having problems with the method: prepareForReuse

I have a custom UITableViewCell, and when it's selected, it expands and adds a UILabel to the selected cells UIView that I added in the storyBoard.
When I run the app and select a cell, the label gets added to myView as expected. The problem is, when I scroll down, the label is also shown at another cell.
Apparently the reason its behaving like so, is because I'm reusing the cell and I don't clean them as Emilie stated. I'm trying to call the method of prepareForReuse and 'cleaning' the cell, but I'm having trouble doing that. Here is my code:
- (void)prepareForReuse {
NSArray *viewsToRemove = [self.view subviews];
for (UILablel *v in viewsToRemove) {
[v removeFromSuperview];
}
Doing that, cleans even the selected cells label.
- (void)viewDidLoad {
self.sortedDictionary = [[NSArray alloc] initWithObjects:#"Californa", #"Alabama", #"Chicago", #"Texas", #"Colorado", #"New York", #"Philly", #"Utah", #"Nevadah", #"Oregon", #"Pensilvainia", #"South Dekoda", #"North Dekoda", #"Iowa", #"Misouri", #"New Mexico", #"Arizona", #"etc", nil];
self.rowSelection = -1;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
CategorieCell *customCell = [tableView dequeueReusableCellWithIdentifier:#"cellID" forIndexPath:indexPath];
customCell.title.text = [self.sortedDictionary objectAtIndex:indexPath.row];
return customCell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[tableView deselectRowAtIndexPath:indexPath animated:YES];
CategorieCell *customCell = (CategorieCell *)[tableView cellForRowAtIndexPath:indexPath];
if (self.info) {
[self.info removeFromSuperview];
}
self.info = [[UILabel alloc] init];
[self.info setText:#"Hello"];
[self.info setBackgroundColor:[UIColor brownColor]];
CGRect labelFrame = CGRectMake(0, 0, 50, 100);
[self.info setFrame:labelFrame];
[customCell.infoView addSubview:self.info];
NSLog(#"%ld", (long)indexPath.row);
self.rowSelection = [indexPath row];
[tableView beginUpdates];
[tableView endUpdates];
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
if ([indexPath row] == self.rowSelection) {
return 159;
}
return 59;
}
The answer is quite simple : you reuse your cell like you should, but never clean them
Reusing your UITableViewCell means that the cell you clicked on previously will be reused when it will go off-screen.
When clicked, you add a view to your UITableViewCell. When reused, the view is still there because you never remove it.
You have two choices : One, you could set a tag of the self.info view (or check with the indexpath you're keeping in memory), then check when you dequeue the cell if the info view is there, and remove it. The cleaner solution would be to implement the view removal by overriding the prepareForReuse method of your custom UITableViewCell
Precision
The first thing you need to do is set a tag for your self.info view after initializing it:
[self.info setTag:2222];
If you want to keep it as simple as possible, you could check and remove the self.info view directly in your cellForRowAtIndexPath method :
CategorieCell *customCell = [tableView dequeueReusableCellWithIdentifier:#"cellID" forIndexPath:indexPath];
customCell.title.text = [self.sortedDictionary objectAtIndex:indexPath.row];
if [customCell.infoView viewWithTag: 2222] != nil {
[self.info removeFromSuperview]
}
return customCell;
I am not a percent sure this code compiles, I cannot test it on my side for now. Hope it works !

Custom UITableViewCell weird behavior with fast scroll

I have a grouped tableView with 5 sections. The tableView uses a custom UITableViewCell with 2 label and 4 buttons in it. When i select 1 or more buttons at the beginning of the table and then scroll to the end of it, i find those buttons selected in the last cell, sometimes in the second-last. It seems to me that there is some issues with the dequeueReusableCellWithIdentifier but i cannot figure it out.
For clarity i have this code in my viewDidLoad:
// table cell
UINib *nib = [UINib nibWithNibName:#"SMRateMeetingTableViewCell" bundle:nil];
[self.tableView registerNib:nib forCellReuseIdentifier:#"SMRateMeetingTableViewCell"];
and this in my cellForRowAtIndexPath:
SMRateMeetingTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"SMRateMeetingTableViewCell" forIndexPath:indexPath];
// Configure the cell...
if (cell == nil) {
cell = [[SMRateMeetingTableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"SMRateMeetingTableViewCell"];
}
Pretty basic stuff.
I added some screens for better understanding.
EDIT: adding buttons code.
For an easier analysis let's assume i only have 1 button in the custom cell
This is the table view code:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
SMRateMeetingTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"SMRateMeetingTableViewCell" forIndexPath:indexPath];
// Configure the cell...
if (cell == nil) {
cell = [[SMRateMeetingTableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"SMRateMeetingTableViewCell"];
}
//the tag will allow me to understand in which section is the button
cell.firstYesButton.tag = indexPath.section;
[cell.firstYesButton addTarget:self action:#selector(firstYesButtonAction:) forControlEvents:UIControlEventTouchUpInside];
return cell;
}
And the relative method associated to the button:
-(IBAction)firstYesButtonAction:(id)sender
{
UIButton *senderButton = (UIButton *)sender;
[self.votesArray replaceObjectAtIndex:(senderButton.tag*2) withObject:[NSNumber numberWithInt:1]];
//NSLog(#"%#", self.votesArray );
}
An this is the code in the implementation file of the custom cell:
#implementation SMRateMeetingTableViewCell
- (void)awakeFromNib {
// Initialization code
[self.firstYesButton setImage:[UIImage imageNamed:#"yesRateDisable.png"] forState:UIControlStateNormal];
}
- (IBAction)firstYesAction:(id)sender {
[self.firstYesButton setImage:[UIImage imageNamed:#"yesRateEnable.png"] forState:UIControlStateNormal];
}
This is because the table view is recycling your cell. This is what the dequeueReusableCellWithIdentifier: method is doing. Because it is recycling views instead of a new one always being created, you will have to set certain properties, such as the selected state of your buttons, otherwise they will retain the properties that they had when they were enqueued.
As UITableViewCell's get recycled which is quite unpredictable.
So one approach is to maintain the state with key value pairs
that this NSDictionary and set images as per state changed in NSDictionary like
#{
"0":"EnableImage",
"1":"DisableImage",
"2":"DisableImage",
"3":"EnableImage",
}
so set image as per the above dictionary in cellForRowAtIndexPath.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
.... ....
You need set image as per state maintained in above dictionary
return cell;
}
And Remove below code
(void)awakeFromNib {
// Initialization code
[self.firstYesButton setImage:[UIImage imageNamed:#"yesRateDisable.png"] forState:UIControlStateNormal];
}
- (IBAction)firstYesAction:(id)sender {
[self.firstYesButton setImage:[UIImage imageNamed:#"yesRateEnable.png"] forState:UIControlStateNormal];
}

Change Image on click button in table cell in ios

I want to change image on the button, when button click. That button is placed in table cell. Image is changing successfull. But when table scroll by default after every 5 cell button image also changed, while I didn't changed it. Means new cells in table, unwilling change. This cell is reusable, and created by storyboard.
How to use that button as switch button. How can I achieve this.
- (NSIndexPath *)indexPathWithSubview:(UIView *)subview {
while (![subview isKindOfClass:[UITableViewCell self]] && subview) {
subview = subview.superview;
}
return [self.table indexPathForCell:(UITableViewCell *)subview];
}
- (IBAction)btnCheckedCellPressed:(id)sender{
NSIndexPath *myIP = [self indexPathWithSubview:(UIButton *)sender];
MyWantsTableViewCell* cell = (MyWantsTableViewCell *)[self.table cellForRowAtIndexPath:myIP];
if (cell.btnEdit.currentImage == [UIImage imageNamed:#"checkbox-active"]) {
[cell.btnEdit setImage:[UIImage imageNamed:#"checkbox-deactive"]
forState:UIControlStateNormal];
} else {
[cell.btnEdit setImage:[UIImage imageNamed:#"checkbox-active"]
forState:UIControlStateNormal];
}
// [self.table beginUpdates];
// [self.table reloadRowsAtIndexPaths:#[myIP] withRowAnimation:UITableViewRowAnimationFade];
// [self.table endUpdates];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
MyWantsTableViewCell *cell=nil;
static NSString *AutoCompleteRowIdentifier = #"MyWantsTableViewCellEdit";
cell = [tableView dequeueReusableCellWithIdentifier:AutoCompleteRowIdentifier forIndexPath:indexPath];
if (cell == nil)
{
cell = [[MyWantsTableViewCell alloc]
initWithStyle:UITableViewCellStyleDefault reuseIdentifier:AutoCompleteRowIdentifier];
[[AsyncImageLoader sharedLoader] cancelLoadingImagesForTarget:cell.imgProduct];
}
cell.selectionStyle = UITableViewCellAccessoryNone;
cell.btnEdit.tag = indexPath.row;
return cell;
}
Use simple way as I have described below, and use an object to keep the current selection, since it will go if you scroll the UITableView
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
[cell.btnEdit setImage:[UIImage imageNamed:#"checkbox-active"]
forState:UIControlStateNormal];
[cell.btnEdit setImage:[UIImage imageNamed:#"checkbox-deactive"]
forState:UIControlStateSelected];
MyClass *obj=[arr objextAtIndex:indexPath.row];
cell.btnEdit.selected=obj.selected;
}
- (IBAction)btnCheckedCellPressed:(id)sender{
NSIndexPath *myIP = [self indexPathWithSubview:(UIButton *)sender];
MyWantsTableViewCell* cell = (MyWantsTableViewCell *)[self.table cellForRowAtIndexPath:myIP];
cell.btnEdit.selected=!cell.btnEdit.selected;
MyClass *obj=[arr objectAtIndex:myIP.row];
obj.selected=!obj.selected;
}
Sample
Find sample for help here
1.. Take a NSInteger variable like "selectedBtn" and initialize it with -1, in viewDidLoad().
2.. go to cellForRowAtIndexPath() set button.tag == indexpath.row;
3.. Now in btnCheckedCellPressed() set that selectedBtn variable with tag value of that sender button.
4.. Now again go to cellForRowAtIndexPath() and place a check
if(cell.btnEdit.tag == selectedBtn)
// set selected image on button
else
// set unselected Image

didSelectRowAtIndexPath not working

I am having issues with my tableView not firing the didSelectRowAtIndexPath method. I have implemented the delegates as such:
#interface ViewController : UIViewController<UITableViewDataSource, UITableViewDelegate,UIScrollViewDelegate>
And in my storyboard the tableView's data source and delegate are both pointed at the base View Controller. I have User Interactions enabled as well as Selection set to Single Selection, and it is not the TapGesture problem since my tap gestures are not bound to the view and I have checked and they do not fire.
This is the code for setting up the table:
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
return menuArray.count;
}
-(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView{
return 1;
}
-(UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"menuCell"];
NSDictionary *menuItem = [menuArray objectAtIndex:indexPath.row];
cell.textLabel.text = menuItem[#"Title"];
cell.detailTextLabel.text = menuItem[#"Subtitle"];
return cell;
}
-(void)showMenu{
[UIView animateWithDuration:.25 animations:^{
[content setFrame:CGRectMake(menuTable.frame.size.width, content.frame.origin.y, content.frame.size.width, content.frame.size.height)];
}];
}
-(void)hideMenu{
[UIView animateWithDuration:.25 animations:^{
[content setFrame:CGRectMake(0, content.frame.origin.y, content.frame.size.width, content.frame.size.height)];
}];
}
-(IBAction)showMenuDown:(id)sender {
if(content.frame.origin.x == 0)
[self showMenu];
else
[self hideMenu];
}
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
//whatever
}
The table is initially out of view on the storyboard (origin.x is set to -150), then when the user clicks on a button in the navigationBar, the view slides over to reveal it, which is what might be causing the problem I think.
Is there anything wrong with my code or implementation that would be causing this to not work?
If you already see your table populated with values from your dictionary then you can rule out data source and delegate as being the problem. i.e. your storyboard connections are working.
Your code looks fine to me. the only difference I see is I usually define my table like this. Try this and see if it helps.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
//NSLog(#"Inside cellForRowAtIndexPath");
static NSString *CellIdentifier = #"Cell";
// Try to retrieve from the table view a now-unused cell with the given identifier.
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
// If no cell is available, create a new one using the given identifier.
if (cell == nil)
{
// Use the default cell style.
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
//Your code here
// ....
return cell;
}
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"menuCell"];
This will return nil in case there was never a cell created.
so checking if cell is nil is mandatory and if so, you need to create a cell.
static NSString *CellIdentifier = #"menuCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if(cell == nil) {
cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
}
as you are using storyboard you can alternatively use
UITableViewCell * cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
for prototype cells. Make sure you use the same identifier in the storyboard and that you registered your the cell's class
- (void) viewDidLoad {
[super viewDidLoad];
[self.tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:#"menuCell"];
}

Resources