How to determine a Button click in a Custom UITableViewCell? - ios

i have a TableView with a Custom Cell and a Section Header. The Header shows me the Country and the custom Cell shows me the City.
which look like this:
USA
New York
Los Angeles
Germany
Berlin
Frakfurt
every Cell got a Button. after a press it will navigate and push the city name to the detail view .
The Problem is, that i don't know how to determine the city name after i pressed the button.
I had a solution but it doesn't work anymore:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSManagedObject *managedObject = [self.fetchedResultsController objectAtIndexPath:indexPath];
StandortCell *cell = (StandortCell *)[tableView dequeueReusableCellWithIdentifier:#"ortCell"];
[cell.detailButton setTag:indexPath.row];
cell.ortLabel.text = [managedObject valueForKey:#"stadtname"];
}
- (IBAction)detailButton:(id)sender {
UIView *contentView = [sender superview];
UITableViewCell *cell = (UITableViewCell *)[contentView superview];
NSIndexPath *cellIndexPath = [self.tableView indexPathForCell:cell];
NSInteger section = cellIndexPath.section;
UIButton * myButton = sender;
int row=myButton.tag;
NSIndexPath * indexPath = [NSIndexPath indexPathForRow:row inSection:section];
StrasseViewController *detail = [self.storyboard instantiateViewControllerWithIdentifier:#"strasse"];
self.selectedStandort = [self.fetchedResultsController objectAtIndexPath:indexPath];
detail.currentStandort = self.selectedStandort;
[self.navigationController pushViewController:detail animated:YES];
}
when i use my old code it will just show the correct street row in the first section. it didn't detect the other sections(Countries);
When i choose the first city in the thirt section. it will display me the first city in the first section.
I hope you can help me.

use this I hope it will solve your purpose.
Create a button pressed method in your view controller
(IBAction)detailButton:(UIButton *)sender {
CustomUITableViewCellName *cell = (CustomUITableViewCellName *)[[sender superview]superview];
NSIndexPath *cellIndexPath = [YOUR_TABLE_NAME indexPathForCell:cell];
}
For ios 7 add another superview.
Now in this way you will get your IndexPath and you an get the current row by using
indexPath.row or indexPath.section.
You can get the title using sender.title property.
You can use this indexPath in your array to the required details.

You need to send row and section information seperately so make tag as
Tag= row*1000+section.
now from tag get section and row value using row= tag/1000
section =tag%1000
use this value to make correct index path or find some better then 2 mins solution to get that.
Caution its a workaround correct will be to
Subclass UIButton
Add a indexpath property int it
make your class to make button object and insted of tag set indexpath property.
in CustomButton.h
#interface CustomButton :UIButton
#property (nonatomic,assign)NSIndexPath *path;
#end
in CustomButton.m
#implementation CustomButton
#synthesize path;
#end
Use this custom button on your table view cell and instead of tag set path property

Related

Get UICollectionViewCell from indexPath when UIBarButtonItem is tapped

I have created a View Controller with a Navigation bar and UiCollectionView. UI Collection View contains custom UICollectionViewCell. Navigation bar contains two UIBarButton items, one is on the left corner - prepared segue to previous page and other item is on the right corner - arranged to delete cell(s) in the UI CollectionView as show in the picture below:
Main Screen
Now I want to remove the selected UICollectionViewCell when UIBarButtonItem in the right corner, is tapped.
This how my cellForItemAtIndexPath method look like:
-(UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(nonnull NSIndexPath *)indexPath{
self.GlobalIndexPath = indexPath;
MessagesCollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:#"messagesCell" forIndexPath:indexPath];
cell.MessageHeading.text = [self.Message_Heading objectAtIndex:indexPath.row];
cell.MessageSubject.text = [self.Message_Subject objectAtIndex:indexPath.row];
cell.MessageContent.text = [self.Message_Details objectAtIndex:indexPath.row];
[cell.Checkbox setHidden:YES];
[cell.Checkbox setChecked:NO];
}
I have tried a solution like Declaring Indexpath as global variable and use it in the button event as below:
#property (strong,nonatomic) NSIndexPath *GlobalIndexPath;
some other code .......
//When Bin Icon(UIBarButtonItem) Clicked
- (IBAction)DeleteMessages:(id)sender {
[self.view makeToast:#"You clicked delete button !"];
NSIndexPath *indexPath = [self.MessageCollectionView.indexPathsForVisibleItems objectAtIndex:0] ;
BOOL created = YES;
// how to get desired selected cell here to delete
MessagesCollectionViewCell *cell = [self.MessageCollectionView cellForItemAtIndexPath:self.GlobalIndexPath];
if([cell.Checkbox isHidden])
{
[cell setHidden:YES];
}
else{
[cell.Checkbox setChecked:NO];
[cell.Checkbox setHidden:YES];
}
}
It's not worked.
For showing the UICollectionViewCell selected as checked, i'm using #Chris Chris Vasselli's solution
Please help me with this. Thanks in Advance.
There are a few steps. First, determine the selected indexPath, but don't assume there is a selection when the method is run....
// in your button method
NSArray *selection = [self.MessageCollectionView indexPathsForSelectedItems];
if (selection.count) {
NSIndexPath *indexPath = selection[0];
[self removeItemAtIndexPath:indexPath];
}
There are two more steps to remove items from a collection view: remove them from your datasource, and tell the view it has changed.
- (void)removeItemAtIndexPath:(NSIndexPath *)indexPath {
// if your arrays are mutable...
[self.Message_Heading removeObjectAtIndex:indexPath.row];
// OR, if the arrays are immutable
NSMutableArray *tempMsgHeading = [self.Message_Heading mutableCopy];
[tempMsgHeading removeObjectAtIndex:indexPath.row];
self.Message_Heading = tempMsgHeading;
// ...
Do one or the other above for each datasource array. The last step is to inform the collection view that the datasource has changed, and it must update itself. There are a few ways to do this. The simplest is:
// ...
[self.MessageCollectionView reloadData];
OR, a little more elegantly:
[self.MessageCollectionView deleteItemsAtIndexPaths:#[indexPath]];
} // end of removeItemAtIndexPath

Enabling UIButton in TableViewCell enables in more Cells

I have a TableViewController with my customed TableViewCell, on each row there are two buttons, one is hidden the other is not, when I tap the shown button I want to show the other button, I´ve covered this, my problem is, if I scroll down other hidden buttons are showed because of the Reuseidentifier.
What can I do to only show up the button from the row I`ve selected.
Hope I got clear if not please ask me.
Thank you all.
When you scroll the tableView remaining buttons are hidden because you wrote setHidden method in cellForRowAtIndexPath Without putting any condition so take one NSMutableArray and allocate memory to it. Whatever the index you select just add it MutableArray.Then in cellForRowAtIndexPath Put one condition if that array contains the indexPath don't hide else hide the button.
-(UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
CutomTableView *cell=[tableView dequeueReusableCellWithIdentifier:#"CellId" forIndexPath:indexPath];
[cell.show setTag:indexPath.row];
[cell.show addTarget:self action:#selector(showButtonACtion:) forControlEvents:UIControlEventTouchUpInside];
NSIndexPath *selectedIndexPath = [NSIndexPath indexPathForRow:indexPath.row inSection:0];
//self.selectedRow is NSMutable Array
if ([self.selectedRow containsObject:selectedIndexPath]) {
[cell.hideBtn setHidden:NO];
}else{
[cell.hideBtn setHidden:YES];
}
return cell;
}
-(void)showButtonACtion:(UIButton*)sender{
NSIndexPath *selectedIndexPath = [NSIndexPath indexPathForRow:indexPath.row inSection:0];
[self.selectedRow addObject:myIP];
[self.tableV reloadData];
}
When you click on particular button to show another button, just save that NSIndexpath.
For example,
If you are click on 2nd row, then add value in NSMutableDicionary as
[mutableDict setValue:#"YES" forKey:[NSString stringWithFormat:#"%#", indexPath.row]];
And then check for particular indexPath in cellForRowAtIndexPath method.
Whenever you find #"YES" for particular row, implement your desired logic to show or hide buttons.
Hope this helps you.
-(IBAction)firstButton:(UIControl *)sender
{
UIButton *button = (UIButton*)sender;
NSIndexPath *myIP = [NSIndexPath indexPathForRow:sender.tag inSection:0];
CustomCell *cell = (CustomCell*)[self.tableView cellForRowAtIndexPath:myIP];
[cell.button2 setHidden:NO];.
}

Custom Cell button Click

My scenario is as follow's:
Custom Cell View
1) I am populating data from server in the custom cell view which is perfectly populated, i want a link store in my array which i want to open in browser, so i want to check which index.row button is clicked so that against that row i can get the url link and open it in browser, so far i have found the solution of button tags but that doesn't work's as well, as if we have two cell's in the screen at same time on click of button both button return's same tag.
2) As the image attached i have another storyboard in which i want to segue to a new view controller, same as mentioned above, i want to pass a specific post title and key as well.
I hope every thing is clear.
Thank's in advance.
To answer the first part of your question. Tags are a pain. It's better to pass along the UIButton as the sender, and, in the action for the button, then you can say something like:
-(void) buttonPresse:(UIButton *)sender
{
CGPoint location = [self.tableView convertPoint:sender.bounds.origin fromView:sender];
NSIndexPath *indexPath = [self.tableView indexPathForRowAtPoint:location];
UITableViewCell *cell = [self.table cellForRowAtIndexPath];
//Do what you want with the cell or indexPath
}
I have found the solution of button tags but that doesn't work's as well
Perform following steps to detect which button is selected.
Type the code snippet given below in cellForRowAtIndexPath: method.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
// DEFINE CELL
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"cell" forIndexPath:indexPath];
// SET ACTION METHOD ON CALL BUTTON
UIButton *button = (UIButton *) [self.view viewWithTag:40];
[button setUserInteractionEnabled:YES];
[button addTarget:self action:#selector(checkButtonTapped:) forControlEvents:UIControlEventAllTouchEvents];
}
Add following method for checkButtonTapped:.
- (void)checkButtonTapped:(UIButton *)sender
{
// GET POINT OF THE BUTTON PRESSED
CGPoint buttonPosition = [sender convertPoint:CGPointZero toView:self.tableView];
// GET INDEXPATH FOR THE CELL WHERE THE BUTTON IS PRESSED
NSIndexPath *indexPath = [self.tableView indexPathForRowAtPoint:buttonPosition];
if (indexPath != nil)
{
//Use indxPath.row to get item
//Perform operations
}
}
There is another approach if you want to perform action according to the cell selected by the user.
For the case where you want to check which row of the cell is clicked you should use following method.
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
NSLog(#"ShowindexPath%ld", (long)indexPath.row); }
Here we check for the user interaction on cell. indexPath.row will return index number for the cell selected by user. You can perform other actions in place of NSLog method.
For the purpose to send value while performing segue use following steps:
Create property in the NewViewController.h file where you want to perform segue as follow:
#property (nonatomic) NSString* receivedValue;
#synthesize property by writing following code in NewViewController.m file within #implementation and #end
#synthesize receivedValue;
ImportNewViewController.h file in TableViewController.m file.
Go to prepareForSegue: method in the TableViewController.m file from where you want to send the value and write following code according to your class names within it.
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
NewViewController *nVC = [segue destinationViewController];
[nVC setReceivedValue:sendValue];
}
Here sendValue is the value that you want to send.
That's all.
Build and run your project and enjoy.
You can extend UITableViewCell and add a touple property to it. key containing the index.row and value containing the URL eg:- 1:"www.stackoverflow.com". When you click on a button, all you have to figure out is the index.row and from that you can get the value that you are looking for.
you can add target for button in cellForRowAtIndexPath like :
[cell.yourButton addTarget:self action:#selector(buttonAction:event:) forControlEvents:UIControlEventTouchUpInside];
here you can receive the action for this button
-(void)buttonAction:(UIButton *)sender event:(UIEvent *)event{
UITouch *touch = [[event allTouches] anyObject];
CGPoint touchPos = [touch locationInView:self.tblview];
NSIndexPath *indexPath = [self.tblview indexPathForRowAtPoint:touchPos];
if(indexPath != nil){
// handle click event..
}
}
surely it will work.........

UIButton tag value doesn't pass in prepareForSegue method in UITableViewController

The feed of my app is managed by a UITableViewController where I set custom cells that are instances of a UITableViewCell sub class.
One property of that sub class is a UIButton called flameRelation:
#property (weak) IBOutlet UIButton* flameRelation;
I am creating a dynamic segue to a detail view controller depending on the class of the sender. If the sender is the cell itself, I pass a given set of data to my detail view controller. If the sender is the flameRelation button, I pass another.
To achieve this, I am looking at the following 2-step approach:
1) Since I need to know the indexpath.row of the cell where the FlameRelation button was pressed, I set the tag of that button in the UITableView method that sets each cell of my feed as follows:
- (UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
//Set a bunch of things before setting UIButton flameRelation tag
[cell.flameRelation setTag:indexPath.row];
And I am printing the tag which allows me to check that the tag is being set when I scroll down my feed after launching my build (I see the numbers incrementing as I scroll down, as expected).
NSLog(#"The button tag for this cell is set to %ld",(long)[cell.flameRelation tag]);
2) In the prepareForSegue method that allows the user to transition to the detail view controller with the relevant information, I test the class of the sender first. If the class of the sender is UIButton, I attempt to pass the right information to the detail view controller by setting the value of the indexpath to the value of the tag:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if([segue.identifier isEqualToString:#"FlameDetail"]) {
FlameDetailViewController* flameDetailController = (FlameDetailViewController*) segue.destinationViewController;
if ([sender isKindOfClass:[FlameCellTableViewCell class]])
{
NSIndexPath* indexPath = [self.tableView indexPathForCell:sender];
NSLog(#"The indexPath is: %#", indexPath);
flameDetailController.flame = [self.feed flameAtIndex:indexPath.row];
NSLog(#"class for object sender: %#", NSStringFromClass([sender class]));
}
else if ([sender isKindOfClass:[UIButton class]])
{
NSLog(#"The button tag is set to %ld",(long)[sender tag]);
NSIndexPath* indexPath = [NSIndexPath indexPathForRow:[sender tag] inSection:0];
NSLog(#"The indexPath from UIButton is: %#", indexPath);
flameDetailController.flame = [self.feed flameAtIndex:indexPath.row];
NSLog(#"The value of indexPath.row is %ld",(long)indexPath.row);
NSLog(#"class for object sender: %#", NSStringFromClass([sender class]));
flameDetailController.findOutView.hidden = NO;
[flameDetailController.flameAction setTitle:#"CHAT NOW" forState:UIControlStateNormal];
NSLog(#"Yes, the sender is UIButton");
NSLog(#"The title of flameAction is set to %#", flameDetailController.flameAction.titleLabel.text);
}
}
Everything works well if I tap on the cell itself, but if I tap on the flameRelation button, I see in my logs that the getter on tag returns 0. Which obviously doesn't transfer the proper information to my target view controller.
Thanks!
First of all why you are converting your tag value to indexPath and then getting your model data with the row number?
Your code:
NSIndexPath* indexPath = [NSIndexPath indexPathForRow:[sender tag] inSection:0];
flameDetailController.flame = [self.feed flameAtIndex:indexPath.row];
Try this instead:
flameDetailController.flame = [self.feed flameAtIndex:sender.tag];
I do not see a reason why this approach would not work. If it does not you need to share more code/details.
However, I would want to suggest another alternate (no so great though):
UITableViewCell *clickedCell = (UITableViewCell *)[[sender superview] superview];
NSIndexPath *clickedButtonPath = [self.tableView indexPathForCell:clickedCell];

how to use the function dequeueReusableCellWithIdentifier:forIndexPath?

I know that dequeueReusableCellWithIdentifier:forIndexPath is called by tableView method inside the tableviewcontroller. And if I understand correctly, tableView method is called several times until all the cells are populated. But what I don't know is where do you get the value for the parameter IndexPath? I want to use dequeueReusableCellWithIdentifier:forIndexPath for a method that I created because I want to access my cell to copy some values of its properties.
NOTE:
I already populated my cell, which means that I successfully used the method tableView.
(Edit) ADDITIONAL INFO:
I'm trying to create a profile and edit profile tableviews. Inside the profile tableview, I displayed the name, address, contact#, etc., of the user. Also, I have a segue called edit profile. In the edit profile, I have textfields for each category (name, address, etc.). What I want to do is, if I edit the contents of the textfields, I should be able to display the new contents in my profile tableview. An example case would be: in the profile view I'm displaying -> name:human, address:earth (each in its own cell). Now if I go to editprofile tableview, I will edit the contents such that -> name:alien, address:mars. After that, there is a button called 'apply' to end editing of contents and go back to profile tableview. If I go back to profile view, the display should now be name:alien, address:mars and not name:human, address:earth.
Here is some code if it's any help. The code is called by a button in tableviewcontroller. "MyCell" is the class of my cell. This code is not working properly. I hope someone can help me fix this.
- (IBAction)updateCopies:(id)sender {
static NSString *ident = #"MyCell";
NSIndexPath *indexPath;
//create cell
MyCell *cell = [self.tableView dequeueReusableCellWithIdentifier:ident forIndexPath:indexPath];
//create variable for accessing cells
int row = [indexPath row];
_labelValues[row] = cell.textField.text
}
You should only use dequeueReusableCellWithIdentifier when you need to supply the table view with a cell to display. If you want to get the UITableViewCell object at a certain index, you should use cellForRowAtIndexPath.
Your problem
What you really need is a model class. You can then pass this to the edit controller, which changes the properties. Then when you return to the tableView, you can reload it and display the new properties.
What you could also do is create a delegate protocol for your edit profile controller, something like EditProfileViewControllerDelegate with something like:
protocol EditProfileViewControllerDelegate {
- (void)editProfileViewController:(EditProfileViewController *)controller didUpdateName:(NSString *)name address:(NSString *)address;
}
You can implement this delegate in your table view controller and use it to update the values when the text is changed. However, this quickly becomes unwieldy, I would not recommend it over using a proper model class.
You can get indexPath using CGPoint..You can use dequeueResusableCell for reusability of the cell..
- (IBAction)updateCopies:(id)sender {
CGPoint position = [sender convertPoint:CGPointZero
toView:self.tableView];
NSIndexPath *indexPath = [self.tableView indexPathForRowAtPoint:position];
//create variable for accessing cells
MyCell *cell = [self.tableView cellForRowAtIndexPath:indexPath];
int row = [indexPath row];
_labelValues[row] = cell.textField.text
}
Hope it helps you..
Use this
- (IBAction)updateCopies:(id)sender {
MyCell *parentCell = (MyCell *)sender.superview;
while (![parentCell isKindOfClass:[MyCell class]]) { // iOS 7 onwards the table cell hierachy has changed.
parentCell = parentCell.superview;
}
UIView *parentView = parentCell.superview;
while (![parentView isKindOfClass:[UITableView class]]) { // iOS 7 onwards the table cell hierachy has changed.
parentView = parentView.superview;
}
UITableView *tableView = (UITableView *)parentView;
NSIndexPath *indexPath = [tableView indexPathForCell:(MyCell *)parentCell];
NSLog(#"indexPath = %#", indexPath);
}
Well I got what you want to accomplish.
Firstly, there is a delegate which is being called when you click/select a cell and go to the Edit Profile page. That delegate is
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
///
}
Make a global variable, say selectedIndexPath which holds the current cell index path which is being edited. Update this value each time when you go to edit profile page.
Like this
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
selectedIndexPath = indexPath;
// code to go to edit page...
}
Now in your updateCopies Method, do like this
- (IBAction)updateCopies:(id)sender {
//get the existing cell with the indexPath
[self.tableView beginUpdates];
[self.tableView reloadRowsAtIndexPaths:#[selectedIndexPath]];
[self.tableView endUpdates];
//rest of your code goes here...
}

Resources