I am building an app similar to iPhone photo app. I am able to show the images in a grid like view using PSUICollectionView. When I tap on a grid cell of collection view a checkbox image should appear. My problem is when I am using following code, I see that multiple random cells are being populated with check box images.
- (void)collectionView:(PSUICollectionView *)collectionView didSelectItemAtIndexPath: (NSIndexPath *)indexPath
{
NSLog(#"%# - %d", NSStringFromSelector(_cmd), indexPath.item);
ImageGridCell *cell = (ImageGridCell *)[collectionView cellForItemAtIndexPath:indexPath];
UIButton *chkboxBtn = [UIButton buttonWithType:UIButtonTypeCustom];
[chkboxBtn setFrame:CGRectMake(60, 60, 30, 30)];
[chkboxBtn setImage:[UIImage imageNamed:#"Checkmark-iPhone.png"] forState:UIControlStateNormal];
[cell addSubview:chkboxBtn];
}
The problem is likely your custom cell has not implemented the prepareForReuse method. As a cell is reused, it may or may not have the check box, depending on if the check box was added on a previous use.
Several ways to address this. One simple way is to add a tag to the ckhboxBtn, and then remove the chkboxBton in the prepareForReuse method. E.g., when adding the check box, add the following:
[chkboxBtn setTag:100];
Then in your UICollectionViewCell class implementation, add/expand the prepareForReuse method:
-(void)prepareForReuse{
[[self viewWithTag:100] removeFromSuperview];
}
Related
I am using a UICollectionView to display some data. I am having one button on each cell. Now on the button click of at some specific index(ex: 9) I want to change buttons image for all cells. Can some one suggest how to do that?
I would loop through all the visible cells and change the image. For example:
for (CustomCellClass *cell in collectionView.visibleCells) {
[cell.button setImage:[UIImage imageNamed:#"new image"] forState:UIControlStateNormal];
}
For off-screen cells, I would make sure to set the new image in collectionView:cellForItemAtIndexPath: as the user scrolls to those cells.
We can simply add the logic in cellForItemAtIndexPath like this.
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:reuseIdentifier forIndexPath:indexPath];
if (self.buttonClicked) { // boolean check
[cell.newButton setImage:buttonClickedImage];
}else {
[cell.newButton setImage:buttonNotClickedImage];
}
return cell;
}
After clicking the button, you can also just reload all visible cells.
[self.collectionView reloadItemsAtIndexPaths:self.collectionView.indexPathsForVisibleItems];
Feel free to suggest edits to make this answer better :)
I am new to app development. In my app when I click on my table view cell, the cell is selected and didSelectRowAtIndexPath method is triggered irrespective of whether I clicked on image or cell. I want the image to be selected when I click on the image, not the cell. Thanks in advance.
Add UITapGestureRecognizer on your image view, and if you not want cell selection then off table view cell selection . on Tap function on image do what you want.
In your cellForRowAtIndexPath method, add a custom UIButton and assign the image as background image of the button. Then add target to the button and handle your code inside the button target method
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"CellIdentifier"];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"CellIdentifier"];
UIButton *imageButton = [[UIButton alloc] initWithFrame:<Give the frame you want>];
[imageButton setBackgroundImage:<your image> forState:UIControlStateNormal];
[imageButton addTarget:self action:#selector(imageButtonClicked:) forControlEvents:UIControlEventTouchUpInside];
[cell addSubview:imageButton];
}
return cell;
}
Here the best method is to create a custom UITableViewCell which have imageButton as an instance variable instead of creating it inside cellForRowAtIndexPath method. Now in the imageButtonClicked method
-(void)imageButtonClicked:(UIButton *)sender {
UIImage *image = sender.currentBackgroundImage;
//Do the logic what you want here
}
Please give the frame of the button so that it is placed at the position you want.
I haven't tried out this code. If sender.currentBackgroundImage is not giving you the image, then create a custom button extending from UIButton. The custom button should have a UIImage variable as an instance variable. Assign the variable to the image you want when it is initialized(in cellForRowAtIndexPath method). And then you can access the image in the button target method
I used this code in cellForRowAtIndexPath method. When I click on button or scroll table this button is shown two times. Why is this button shown 2 times please help me?
UIButton *trashbtn=[[UIButton alloc]initWithFrame:CGRectMake(cell.frame.size.width-20, cell.frame.size.height-30, 20, 20)];
[trashbtn setImage:[UIImage imageNamed:#"editor_trash"] forState:UIControlStateNormal];
[trashbtn addTarget:self action:#selector(DeleteMyAssociate:) forControlEvents:UIControlEventTouchUpInside];
[trashbtn setTag:indexPath.row];
[cell addSubview:trashbtn];
You should firstly add a UITableViewCell within your table in IB. Then give an identifier e.g. "MyCellIdentifier" to that cell. Still on IB, add your outlets, to that cell e.g. the button, the textFields... You can initially set the button to be invisible. Then in the method cellForRowAtIndexPath, you do:
- (UITableViewCell*)tableView:(UITableView*)tableView cellForRowAtIndexPath:(NSIndexPath*)indexPath
{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"MyCellIdentifier"];
if(cell){
myButton.hidden = NO; //
myTextField.text = #"BlaBlaBla";
}
}
Hey I can give you a suggestion that use auto layout and storyboard to add button rather than adding it programmatically. That is more better and cleaner approach.
The problem you are facing is that cells are reused in tableviews, So the first time you create a button and add it to the cell, it appears once. But if it is dequeued, it already has the button, so when you add another one you end up with multiple buttons.
There are two ways to correct this.
Firstly, remove the button in the cell's prepareForReuse method, which is called just after the cell is reused.
Secondly, avoid creating custom views in the cellForTableView... method. Use a custom cell that already has the button. Now, you may ask, how do I hook up the action for the button if I do it this way? You can either provide a delegate method for your cell that calls back to your view controller, or you can pass your cell a block to perform when the button is clicked.
as far as i can get u already have a custom cell.. add a button to existing cell and create an IbOutlet for the same in customCell.h file
then in cellForRowAtIndexPath method access the button using its IBOutlet
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
customCell *cell =(customCell*) [tableView dequeueReusableCellWithIdentifier:#"customCell" forIndexPath:indexPath];
[cell.btn setTitle:#"blabla" forState:UIControlStateNormal];
[cell.btn setTag:indexPath.row];
[cell.btn addTarget:self action:#selector(method:) forControlEvents:UIControlEventTouchUpInside];
if(condition)
{
cell.btn.hidden=YES;
}
else
{
cell.btn.hidden = NO;
}
return cell;
}
Do not forget to register for the customCell class in your ViewController class
[tableName registerNib:[UINib nibWithNibName:#"customCell" bundle:nil] forCellReuseIdentifier:#"customCell"];
you can hide or unhide the button as per your requirements as well as add different actions to the button in each class.
Happy Coding..!!
Do vote it if my code was helpful for you.. ;)
I have a UICollectionViewCell subclass, and a corresponding .xib file associated with it. The cell has some labels and a button with an action attached to it.
The problem is when I tap that button, only:
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath
{/*...*/}
gets called.
I guess there should be some way to change the tap event down the chain.
Did anyone face these issues before?
If yes, how did you solve this problem?
I've encountered this issue before when the button isn't in a Collection View Cell in the .xib. If you just have it in a view it won't be in the contentView and won't respond appropriately to events.
Once you do that you should be able to create an IBAction in your UICollectionViewCell subclass and it will be hit.
How did you add the button, as a subview of of your UICollectionViewCell? You need to do that instead, and add a selector method to your UIButton rather than handling it in didSelectItemAtIndexPath:.
Here is an example:
In your - (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath or in your custom cells class (Preferably due to efficiency),
Adding the button as a subview should appear as so:
self.icon = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[self.icon addTarget:self action:#selector(buttonTapped:) forControlEvents:UIControlEventTouchUpInside];
self.icon.frame = CGRectMake(-50, -50, 100, 100);
UIImage *image = [UIImage imageNamed:#"icon.png"];
[self.icon setImage:deleteImage forState:UIControlStateNormal];
if (ifYouNeedABOOL == YES) {
[cell addSubview:self.deleteIcon];
return cell;
}
You can then handle the tap event in your new selector method, buttonTapped:.
If you want to stick with adding it in Interface Builder using IBAction, you can utilize tags. Add them to the cells in cellForItemAtIndexPath:
Having issues updating uibutton in specific uitableviewcell. When I change the button color it updates every button in uitableview. Attached my code below:
PFObject *post = [self.objectArray objectAtIndex:indexPath.section];
cell.likeBtn.layer.cornerRadius = 5.0f;
//To access button methods
[cell.likeBtn setTag:indexPath.section];
NSMutableDictionary *attributes = [NSMutableDictionary
dictionaryWithDictionary:[[TMMemoryCache sharedCache] objectForKey:post.objectId]];
BOOL likedByCurrentUser = [[attributes objectForKey:#"likedByCurrentUser"] boolValue];
if (likedByCurrentUser) {
cell.likeBtn.backgroundColor = [UIColor flatRedColor];
[cell.likeBtn setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
[cell.likeBtn setTitle:#"Liked" forState:UIControlStateNormal];
}else{
//NSLog(#"Did not like %#", post.objectId);
cell.likeBtn.backgroundColor = [UIColor flatBlueColor];
[cell.likeBtn setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
[cell.likeBtn setTitle:#"Like" forState:UIControlStateNormal];
}
is their a better way to update uibutton in just one specific cell?
There are two ways to do what you want, depending on how the updated information comes in:
When configuring the cell in cellForRowAtIndexPath:.
Let the cell manage its own contents.
More often than not, #1 is the way to go. When new information comes into your UITableViewController, do 1 of the following:
Call reloadData to force a refresh of the entire table.
Figure out which cells need to be updated and call reloadRowsAtIndexPaths:withRowAnimation: passing in the correct index path values.
In either of those scenarios, you'll set up the button correctly somewhere in your tableView:cellForRowAtIndexPath: method. Generally, I create my UITableViewCell subclasses to accept an object and configure themselves with the data in that object. That way, there's less code in my UITableViewController that deals with cell configuration.
A typical implementation of tableView:cellForRowAtIndexPath: looks like this:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
Cell *cell = [tableView dequeueReusableCellWithIdentifier:#"item" forIndexPath:indexPath];
[cell configureForItem:self.dataSource[indexPath.row]];
return cell;
}
Each of your UITableViewCell instances will have their own button that will be configured by the data passed by the object in self.dataSource[indexPath.row]. That way, as cells are recycled, the button isn't just passed around, but a new one is recycled and configured each time a new cell is needed.
At a guess, without seeing more of your code I would say your issue is with your use of indexPath.section instead of indexPath.row
If you have multiple buttons per section then they will all be covered by this code.