ios: button in custom table cell not respond when scroll - ios

Sorry for my poor English. I want to add some clickable buttons in my custom tablecell and it works fine but i face a problem when i scroll down the table. After scrolling, the buttons in the upper cell will have no response when i click on it. but when i scroll back to the original position of the cell, the buttons have response again.
I put this
[cell.btn addTarget:self action:#selector(btnPressed:) forControlEvents:UIControlEventTouchUpInside];
in cellForRowAtIndexPath method;
and btnPressed had been triggered before i scroll down. but btnPressed cannot be triggered after i scroll out the bound of the original position of that cell.
Hope someone can understand my problem thanks.
Please tell me how can I make the button response even the table scroll ? thanks in advance
method 1
when i tried the method addtarget in cellForRowAtIndexPath and the coding is like this
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *cellIdentifier = #"CustomTableViewCell";
CustomTableViewCell *cell = (CustomTableViewCell *)[tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if (cell == nil) {
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:cellIdentifier owner:self options:nil];
cell = [nib objectAtIndex:0];
}
[cell.btn addTarget:self action:#selector(btnPressed:)
return cell;
}
method 2
i also tried another method which do not addtarget in cellForRowAtIndexPath
Here's my code with delegate
in customcell.h, i added this
#protocol CustomCellDelegate <NSObject>
- (void)didClickOnbtn;
#end
in customcell.m, i added this
#interface CustomTableViewCell()
#property (nonatomic, strong) NSDictionary *currentDict;
#property (nonatomic,assign) id<CustomCellDelegate>delegate;
#end
and
- (IBAction)didClickOnbtn:(id)sender {
if (self.delegate && [self.delegate respondsToSelector:#selector(didClickOnbtn)]) {
[self.delegate didClickOnbtn];
}
}
and i have set the custom cell delegate to the tableview
in tabelview controller i also have the method didClickOnbtn and <CustomCellDelegate>
and the button can trigger didClickOnbtn method in tableview controller response before scrolling the table.
UPDATE:
I had made a section header view in the table. And I find out when i delete the section header view, the problem is solved. Although I don't know why the section header view would make the button disable, it works well now. Hope this can help someone who are in the same situation as me.
p.s. Both methods work.

I had the same problem. I used the workaround to use Touch Down instead of Touch Up Inside. It's not a perfect solution but a work around as the IBAction for Touch Down as being called even when scrolling is not finished.

How are you setting the frame of button ?
is it in reference to cell frame ? or hardcode like below
UIButton *button = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[button addTarget:self
action:#selector(aMethod:)
forControlEvents:UIControlEventTouchUpInside];
[button setTitle:#"MyButton" forState:UIControlStateNormal];
button.frame = CGRectMake(80.0, 210.0, 160.0, 40.0); // example co-ordinate
[cell addSubview :button] ;
In this case , after scrolling down the button co-ordinate will not change and wont work as u said best way to make it right is
create a interface file for UITableViewCell , place a button there and do the wiring
this will solve the scroll problem for you.
also you can try doing this .
button.frame = CGRectMake(cell.frame.origin.x+samplevalue ,cell.frame.origin.y+sampleValue , 160.0, 40.0);
Hope this helps.

Adding target for a button in a tableViewCellForRowAtIndexPath: API is not not recommended way. This API gets called mutiple times when app needs to display the cell.
Move this code to cell class. Create custom class if not done and add this code. In the action method of the button, reference your controller and call some API that does action work for the cell (say open up other controller or so). You can make your view controller as delegate of the cell for this purpose.
Hope this change may solve your problem

I am assuming that you want to add the button to second cell of tableView.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *cellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if (cell == nil)
{
cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier];
}
if (indexPath.row == 0)
{
cell.textLabel.text = #"Cell1";
}
else if (indexPath.row == 1)
{
//Add button to cell and use different reuseIdentifier
UITableViewCell *cell1 = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"btnCell"];
UIButton *button = [[UIButton alloc]initWithFrame:CGRectMake(cell1.frame.origin.x+10, cell1.frame.origin.y, self.view.frame.size.width-20, cell1.frame.size.height-10)];
[button setTitle:#"ButtonTitle" forState:UIControlStateNormal];
shareButton.backgroundColor = [UIColor grayColor];
[cell1 addSubview:button];
[shareButton addTarget:self action:#selector(buttonTapped) forControlEvents:UIControlEventTouchUpInside];
return cell1;
}
//Your code...
return cell;
}

Related

Messing UIButton in each UICollectionViewCell in Scrolling Filmstrip

This is a follow-up question to my previous one and this time I have a problem with UIButton that I have added in each UICollectionViewCell. Before diving into the problem, let me brief out all I've got so far.
Here's the basic rundown of how my UICollectionView in Scrolling Filmstrip style within UITableView works:
Create a normal UITableView with a custom UITableViewCell
Create a custom UIView that will be added to the cell's contentView
The custom UIView will contain a UICollectionView
The custom UIView will be the datasource and delegate for the
UICollectionView and manage the flow layout of the UICollectionView
Use a custom UICollectionViewCell to handle the collection view data
Use NSNotification to notify the master controller's UITableView when
a collection view cell has been selected and load the detail view.
So far, I've been able to add UIButton in each UICollectionViewCell and when I tap the UIButton its image will change to checked mark but the problem occurs when I scroll up/down. Once the cell with checked mark UIButton is off-screen and scrolled back on-screen again, UIButton image starts to get messed around. For example, when UIButton image in cell1 should be checked mark but it's not. Instead checked mark will be appeared in another cell.
Here below is my relevant code:
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
PostsCollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:#"PostsCollectionViewCell" forIndexPath:indexPath];
NSDictionary *cellData = [self.collectionData objectAtIndex:[indexPath row]];
cell.postTextLabel.text = [cellData objectForKey:#"text"];
cell.locationNameLabel.text = [cellData objectForKey:#"locationName"];
// >>> Select Button <<<
UIButton *selectButton = [UIButton buttonWithType:UIButtonTypeCustom];
[selectButton setFrame:CGRectMake(110, 150, 20, 20)];
[selectButton setImage:[UIImage imageNamed:#"uncheckedDot.png"] forState:UIControlStateNormal];
[selectButton setImage:[UIImage imageNamed:#"checkedDot.png"] forState:UIControlStateSelected];
[cell.contentView addSubview:selectButton];
[selectButton addTarget:self
action:#selector(buttonPressed:)
forControlEvents:UIControlEventTouchUpInside];
// >>> End Select Button <<<<
return cell;
}
// >>> Select Button Method <<<
-(void) buttonPressed:(UIButton *)sender
{
if([sender isSelected]){
//...
[sender setSelected:NO];
} else {
//...
[sender setSelected:YES];
}
}
Any help would be greatly appreciated! Thanks in advance.
Man, you should be aware that cells (both for UITableView and UICollectionView) are reused via the reuse deque under the hood of UIKit and are not obligated to keep their state consistent with regard to their position in a table view or a collection view. What you need to do is to make sure that the return value of dequeueReusableCellWithReuseIdentifier: is then properly initialized (or, in your case, re-initialized) in accordance with the state of your data model. Basically, you need to store the state of your "checkmarks" (or "checkboxes", what have you) in an array or any other data structure. Then, when you call dequeueReusableCellWithReuseIdentifier:, you should apply that stored state to the return value (the cell you're going to return for the collection view) you just got. Hope it's explained good enough. Good luck.
Maintain in your Data source which cell is to be selected by adding a key for each data source dict. For example:#"isSelected"
Then in cellForRowAtIndexPath:
if ([[cellData valueForKey:#"isSelected"] boolValue]) {
[selectButton setSelected:NO];
} else {
[selectButton setSelected:YES];
}
and in your -(void) buttonPressed:(UIButton *)sender Method:
CGPoint point = [self.collectionView convertPoint:CGPointZero fromView:sender];
NSIndexPath *indexPath = [self.collectionView indexPathForItemAtPoint:point];
NSDictionary *cellData = [self.collectionData objectAtIndex:[indexPath row]];
if ([[cellData valueForKey:#"isSelected"] boolValue]) {
[cellData setValue:#"false" forKey:#"isSelected"];
} else {
[cellData setValue:#"true" forKey:#"isSelected"];
}
[self.framesCollectionView reloadItemsAtIndexPaths:#[indexPath]];
AND
In your cellForRowAtIndexPath set a tag for the UIButton. Then before adding a UIButton as subview to the cell's contentView check if a view with this tag already exists. In case the cell has been reused, just use the already existing button.

Disabling a UIButton in a UITableViewCell with cell reusability

I have a UIButton in each of my UITableViewCells. When the button is pressed I am disabling it so that the user cannot press it again (it is a like button). However, when the user scrolls passed a cell and the scrolls back to the cell the button is selectable once again. I'm guessing this is because the cell is redrawn when the user comes back to it, resetting the button. Is there a way I can avoid this? My code is below
Code
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *CellIdentifer = #"cellName";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifer];
if (cell == nil)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifer];
}
UIButton *ilikeit = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[ilikeit addTarget:self action:#selector(like:) forControlEvents:UIControlEventTouchUpInside];
ilikeit.frame = CGRectMake(55, h+70, 45, 25);
[cell addSubview:ilikeit];
return cell;
}
-(void) like:(id) sender {
((UIButton *)sender).enabled = NO;
}
You can store the button state in your data model for the tableview class.
Say the TableView is loading data from a class MyData which looks like this:
#interface MyData: NSObject
// Your other data here such as strings etc
NSString *otherData;
// Here add a selected flag
BOOL selected;
#end
in the TableViewDelegate, read and write to this model.
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
MyData *thisCellData = [yourGlobalMyDataArray objectAtIndex:indexPath.row];
// set other data
if (thisCellData.selected){
// Hide the button
// Do other stuff as needed for this button
}
else {
// Show the button
// Do other stuff as needed for this button
}
}
The same data model should be updated when user selects the button.
Hope this helps.
Problem- 1 You are creating your button every time cellForRowAtIndexPath gets called.
Problem- 2 You have no code to keep the state of your button with cell reusability, that's why it is refreshed when cell is going for reuse.
Solution- For this you have to keep your selected button state. Make a global NSInteger variable. And set its value as sender.tag value in like: method. And use -
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
if (cell == nil)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifer];
// here create your UIButton
// Set its tag to indexPath.row
// add this to cell as subview
}
// find button of current cell using tag
UIButton *btn = (UIButton*)[cell.contentView viewWithTag:indexPath.row];
// place your check here if button tag matches with selected tag then disable btn here.
// Else use setEnable with YES parameter.
}

Differentiate button and row click event in Table View

In my Application in UITableView i put UIButton in every cell.
but when i click on Button then some times button event will come and some time Cell's Row clicked.
I need to get button click event all the time when button click and row click event when row clicked.
How to come over this issue ?
All the Row are identical (means i didn't use reusable).
My code for this is...
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *cellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if (cell == nil)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:#"Cell1"];
[cell clearsContextBeforeDrawing];
UIButton *btn = [UIButton buttonWithType:UIButtonTypeRoundedRect];
btn.frame = CGRectMake(5,220,70, 30);
[btn setTitleColor:[UIColor blackColor] forState:UIControlStateNormal ];
[btn setTitle:[NSString stringWithFormat:#"button == %ld",(long)indexPath.row] forState:UIControlStateNormal];
btn.tag = indexPath.row;
[btn addTarget:self action:#selector(methodOfButton:) forControlEvents:UIControlEventTouchUpInside];
[cell.contentView addSubview:btn];
}
return cell;
}
Any tutorial, code, link will be great help.
Most likely the problem you're seeing happen when user tapped close to the button but not on it. In other interface elements you will not notice this, since no other event will be generated but in your cases 'didSelectRow' will fire.
You need to make sure button occupies as much room as possible on the cell (at least full cell height). This way you will get user less chance to miss tap. Right now your button is only 30px tall, what the height of the cell?

UIButton Image Changing In Multiple UITableViewCells

I am currently building an iOS app that uses a UITableView with custom UITableViewCells that include a button. What I want to accomplish is having the UIButton's image change on touch up inside. That part is working fine, the issue is when you scroll, suddenly every few row buttons located in the UITableViewCell have this new image. Below is some of my code. Any help would be appreciated.
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
NSString *cellID = #"CellID";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellID];
if(cell == nil){
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:cellID];
//YEP BUT
yepBut = [[UIButton alloc] initWithFrame:CGRectMake(23, 16, 64, 32.5)];
[yepBut addTarget:self action:#selector(yepPost:) forControlEvents:UIControlEventTouchUpInside];
[cell.contentView addSubview:yepBut];
}
[yepBut setImage:[UIImage imageNamed:#"yepBtn"] forState:UIControlStateNormal];
return cell;
}
-(void) yepPost:(id) sender{
//UIButton *clicked = (UIButton *) sender;
[sender setImage:[UIImage imageNamed:#"yepBtnACT"] forState:UIControlStateNormal];
}
Thanks!
You need to have a property that you set when the button is touched that you check in cellForRowAtIndexPath. So, in the button's method, have a property, say lastTouched (#property (strong,nonatomic) NSIndexPath *lastTouched, or NSInteger if you don't have sections), that you set to the indexPath (or indexPath.row) of the cell in which the touched button resided (gotten from the cell which you get by searching up through the superviews of the button until you find the cell, or by using tags on your buttons equal to the indexPath.row). After that, you have to reloadData or reloadRowsAtIndexPaths to make the change happen. In cellForRowAtIndexPath, you would set your images like this:
if ([self.lastTouched isEqual:indexPath]) {
[yepBut setImage:[UIImage imageNamed:#"yepBtnACT"] forState:UIControlStateNormal];
else{
[yepBut setImage:[UIImage imageNamed:#"yepBtn"] forState:UIControlStateNormal];
}
When you first initialize lastTouched, you want to set it to an indexPath that doesn't occur in your table, so nothing will have the yepButAct image until you touch one.
Subclassing UITableViewCell fixed my issue.

Why the UIButton in UITableViewCell.contentView won't work?

I've did lots of tests and searches and still couldn't find why the UIButton that I added into a UITableViewCell.contentView won't work. I can guarantee I did everything right, the button still won't answer the TouchUpInside event.
code like this won't work:
[self.contentView addSubview:theButton];
But this would work:
[self addSubview:theButton];
As you may expected, self was a subclass of UITableViewCell. and recording to the doc, shouldn't we always add things to contentView?
Sorry for my poor English. I hope I described it clearly.
try this code in cellForRowAtIndexPath method
UIButton *scanQRCodeButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
scanQRCodeButton.frame = CGRectMake(260.0f, 6.0f, 45.0f, 25.0f);
scanQRCodeButton.backgroundColor = [UIColor whiteColor];
[scanQRCodeButton setTitle:#"button" forState:UIControlStateNormal];
[cell.contentView addSubview:scanQRCodeButton];
it will work.
if your UITableViewCell has a customer height(not the default 44.0f)。
if so, you should set the code like this:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *cellID = #"cellID";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellID];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellID];
cell.contentView.frame = CGRectMake(0, 0, tableView.width, [self tableView:tableView heightForRowAtIndexPath:indexPath]);
// add a UIButton that you create
}
return cell;
}
the reason for this problem is the cell.contentView.size is default (320.f, 44.0f), if your button is over the rect, then the button could not accept the event, so you should set the cell.contentView.frame yourself, like i do the code。
Good Luck。

Resources