my app was running fine under ios6.1. tried the ios7 simulator and the following part does not work:
EditingCell *cell = (EditingCell*) [[textField superview] superview];
NSIndexPath *indexPath = [self.tableView indexPathForCell:cell];
NSLog(#"the section is %d and row is %d", indexPath.section, indexPath.row);
NSUInteger section = [indexPath section];
NSUInteger row = [indexPath row];
NSString *rowKey = [[keysForRows objectAtIndex: section] objectAtIndex: row];
It always comes:
the section is 0 and row is 0
although another section / row were selected.
Has someone an idea why this does not work under ios7?
Your approach to find the "enclosing" table view cell of a text field is fragile,
because is assumes a fixed view hierarchy (which seems to have changed between
iOS 6 and iOS 7).
One possible solution would be to traverse up in the view hierarchy until the table view cell is found:
UIView *view = textField;
while (view != nil && ![view isKindOfClass:[UITableViewCell class]]) {
view = [view superview];
}
EditingCell *cell = (EditingCell *)view;
A completely different, but often used method is to "tag" the text field with the row
number:
cell.textField.tag = indexPath.row; // in cellForRowAtIndexPath
and then just use that tag in the text field delegate methods.
I was finding cells the same way you were. Now I use this quick method if I have a button in a cell and know the tableview I'm in. It'll return the tableviewcell.
-(UITableViewCell*)GetCellFromTableView:(UITableView*)tableView Sender:(id)sender {
CGPoint pos = [sender convertPoint:CGPointZero toView:tableView];
NSIndexPath *indexPath = [tableView indexPathForRowAtPoint:pos];
return [tableView cellForRowAtIndexPath:indexPath];
}
Experiencing this problem in iOS 11, but not in 9 or 10, I overrode the func indexPath(for cell: UITableViewCell) -> IndexPath? method using the technique that #drexel-sharp detailed previously:
override func indexPath(for cell: UITableViewCell) -> IndexPath? {
var indexPath = super.indexPath(for: cell)
if indexPath == nil { // TODO: iOS 11 Bug?
let point = cell.convert(CGPoint.zero, to: self)
indexPath = indexPathForRow(at: point)
}
return indexPath
}
Related
I have a custom UITableView with UITextFields inside. In cellForRow... I made the textFields delegate to self. (In my main VC class.) The way I get the text from the textField is at textFieldDidEndEditing, and I add it to a mutableArray.
I then have different cell ids that get added when a button gets selected:
- (IBAction)addRow:(id)sender
{
NSInteger row = [self.rowArray cound];
[self.rowArray insertObject:#"anotherCell" atIndex:row];
NSIndexPath *indexPath = [NSindexPath indexPathForRow:row inSection:0];
[self.myTableView insertRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
}
(There is a textField in that cellID and I set the delegate to self.)
In textFieldDidEndEditing, I made an NSLog of textField.text, and when that method gets called from a textField that was there initially, it works as expected.
But when textFieldDidEndEditing gets called from the textField that's in the cell of anotherCell (the added cell), then the whole simulator freezes.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *cellID = [self.rowArray objectAtIndex:[indexPath row]];
customCell *cell = [tableView dequeuereusablecellwithidentifier:cellID forIndexPath:indexPath];
cell.name.delegate = self; // From cell that is initially there
cell.phoneNumber.delegate = self; // From the added cell
return cell;
}
(If this is confusing, or if you need more code, just let me know in the comments. Thanks)
Edit
- (void)textFieldDidEndEditing:(UITextField *)textField
{
if (textField.tag <= 9)
{
NSLog(#"%#", textField.text); // This works
}
UIView *superview = textField.superview;
while (![superview isMemberOfClass:[UITableViewCell class]]) {
superview = superview.superview;
}
CustomCellClass *cell = (CustomCellClass *)superview;
NSIndexPath *indexPath = [self.myTableView indexPathForCell:cell];
if (textField.tag >= 12)
{
if ([self.inputArray count] > indexPath.row) // So I won't get the error message of [__NSArrayM objectAtIndex:]: index 1 beyond bounds for empty array'
{
for (NSUInteger i = [self.inputArray count]; i < indexPath.row; i++) {
[self.inputArray insertObject:#"" atIndex:i];
NSLog(#"%lu", (unsigned long)i);
}
}
NSLog(#"%#", self.inputArray);
}
}
Your code is stuck in an infinite loop here:
while (![superview isMemberOfClass:[UITableViewCell class]]) {
superview = superview.superview;
}
because isMemberOfClass will return true only if the superview class is UITableViewCell, but NOT if it is a subclass of UITableViewCell. If you change isMemberOfClass to isKindOfClass, it should work. Check the Apple docs here.
I understand this is suppose to happen but I haven't been able to find a way to call this method when the button is tapped The method gets called but the wrong cell is selected.
- (UITableViewCell *) tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath object:(PFObject *)object{
_postCell = (postCell *) [self.tableView dequeueReusableCellWithIdentifier:#"postCell"];
_postCell.personStringPost.text = [object objectForKey:#"stringPost"];
[_postCell.nameLabel setTitle:[object objectForKey:#"User_Name"] forState:UIControlStateNormal];
_postCell.userId = [object objectForKey:#"userId"];
_postCell.selectionStyle = UITableViewCellSelectionStyleNone;
PFFile *imageFile = [object objectForKey:#"profileImage"];
NSData *data = [imageFile getData];
_postCell.profileImage.image = [UIImage imageWithData:data];
[_postCell.nameLabel addTarget:self action:#selector(personProfile:tableView:) forControlEvents: UIControlEventTouchUpInside];
[_postCell.profileImageButton addTarget:self action:#selector(personProfile:tableView:) forControlEvents:UIControlEventTouchUpInside];
return _postCell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
[super tableView:tableView didSelectRowAtIndexPath:indexPath];
postCell *cell = (postCell *)[tableView cellForRowAtIndexPath:indexPath];
self.userId = cell.userId;
NSLog(#"Did Select: %#", self.userId);
}
- (void) personProfile: (NSIndexPath *) indexPath tableView: (UITableView *) tableView{
[self tableView:tableView didSelectRowAtIndexPath:indexPath];
[self performSegueWithIdentifier:#"personProfile" sender:self];
}
Few things i hope it will help
while dealing with button with only one section
in cellfor row method
yourcell.mybutton.tag=indexPath.row;
-(void)myCellbuttonAction:(id)sender
{
UIButton *button = (UIButton *)sender; // first, cast the sender to UIButton
NSInteger row = button.tag; // recover the row
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:row inSection:0];
// apply your logic for indexpath as in didselect row
}
While dealing with multiple sections and multiple rows it this might help you
in cell for row method
yourcell.tag=indexPath.section;
yourcell.contentview.tag=indexPath.row;
and your button action might look like this
-(void)myCellbuttonAction:(id)sender
{
UIButton *button = (UIButton *)sender; // first, cast the sender to UIButton
id rowid =[button superview];
id sectionid = [rwoid superview];
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:[rowid tag] inSection:[sectionid tag]];
// apply your logic for indexpath as in didselect row
}
A few things here.
a) I would not use an instance variable (_postCell) in your cellForRowAtIndex method. You will likely have cell re-use problems and this may well be the source of your error. Replace it with a local variable:
postcell *cell = (postCell *)[self.tableView dequeueReusableCellWithIdentifier:#"postCell"];
You will also need to replace all references to _postCell with cell.
b) Note that in the same line your cast uses lowercase = (postCell *)... - I have done the same above, but it is best practice for class names to start with capital letters.
c) You have a property named nameLabel, which suggests its a UILabel, but you are using the setTitle:forState: method, which implies it's a UIButton. I would rename this property since debugging will be a lot easier if the names match the classes (or at least don't imply the wrong class).
d) When you call the addTarget:action:forControlEvents method, your selector is for personProfile:tableView:. The signature for that method is for an NSIndexPath and a UITableView. But your button will not be sending those arguments of those types. It will send details of the sender - i.e. the button which triggered the action. So you need to revise your method to accept arguments of that type:
[cell.nameLabel addTarget:self action:#selector(buttonPressed:) forControlEvents: UIControlEventTouchUpInside];
e) When the method gets called, you need a way to determine which cell the sending button was on. Ideally you would subclass UIButton to add some link to the cell, but (if you have only one section) you might get away with putting the row number as a tag. To do this add:
cell.nameLabel.tag = indexPath.row;
to your cellForRowAtIndexPath:. Then you can implement a different method to handle the button press, as follows:
-(void)buttonPressed:(id)sender {
UIButton *button = (UIButton *)sender; // first, cast the sender to UIButton
NSInteger row = button.tag; // recover the row
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:row inSection:0]; // derive the indexPath, assuming section is 0
[self.tableView selectRowAtIndexPath:indexPath animated:YES]; // select the relevant row in the table (assuming the table is self.tableView)
[self performSegueWithIdentifier:#"personProfile" sender:self]; // perform the segue
}
f) Note in the above that you should not call tableView:didSelectRowAtIndexPath:. This is for the tableView to call when the user selects the row, not for selecting rows programmatically. Use selectRowAtIndexPath:animated: instead.
This is basically pbasdf answer above with Swift 3
I do call didSelectRowAt directly since it is not triggered by selectRowAtIndexPath
The tag is set on cell creation with the row of the indexpath
The getTableView function is my own
#IBAction func actionButtonPushed(_ sender: Any) {
guard let button = sender as? UIButton else { return }
guard let tableView = getTableView() else { return }
let indexPath = IndexPath(row: button.tag, section: 0)
tableView.selectRow(at: indexPath, animated: false, scrollPosition: .none)
if let delegate = tableView.delegate {
delegate.tableView!(tableView, didSelectRowAt: indexPath)
}
}
I have more than 20 cells in my custom table view, in execution time 6 cells will be visible. Now i select the 4 th cell means, that 4th cell have to come in first position and 5th cell in 2nd position and so on. how to do this process, i tried like this.
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
MACalendarCustomCell *cell = (MACalendarCustomCell*) [tableView dequeueReusableCellWithIdentifier:[MACalendarCustomCell reuseIdentifier]];
if(cell == nil)
{
[[NSBundle mainBundle] loadNibNamed:#"MACalendarCustomCell" owner:self options:nil];
cell = customCell;
customCell = nil;
}
cell.lbl_CalendarTitle.text = [arr_CalendarTitle objectAtIndex:indexPath.row];
cell.lbl_CalendarSubTitle.text = [arr_CalendarSubTitle objectAtIndex:indexPath.row];
cell.lbl_calendarEventTime.text = [arr_CalendarEventTime objectAtIndex:indexPath.row];
cell.lbl_calendarDate.text = [arr_CalendarDate objectAtIndex:indexPath.row];
cell.lbl_CalendarMonth.text = [arr_CalendarMonth objectAtIndex:indexPath.row];
cell.lbl_CalendarYear.text = [arr_CalendarYear objectAtIndex:indexPath.row];
cell.img_BackGround.image = [UIImage imageNamed:#"calendar_Cell_Up.png"];
//here click event is occurred.
cell.btn_CollapseExpand.tag = indexPath.row;
[cell.btn_CollapseExpand addTarget:self action:#selector(method_Expand:) forControlEvents:UIControlEventTouchUpInside];
return cell;
}
ButtonPressed event calls
- (void)method_Expand:(UIButton*)sender
{
UITableViewCell *cell = (UITableViewCell *)sender.superview;
NSIndexPath *indexpath = [tbl_CalendarList indexPathForCell:cell];
[tbl_CalendarList moveRowAtIndexPath:[NSIndexPath indexPathForItem:indexpath.row inSection:indexpath.section] toIndexPath:[NSIndexPath indexPathForItem:0 inSection:indexpath.section]];
int_SelectedIndex = sender.tag;
NSLog(#"Selected Button : %ld",(long)int_SelectedIndex);
if ( int_TempSelectedIndex != int_SelectedIndex)
{
int_TempSelectedIndex = int_SelectedIndex;
}
else
{
int_TempSelectedIndex = -1;
}
[tbl_CalendarList reloadData];
}
Resizing the cell
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
if (indexPath.row == int_TempSelectedIndex )
{
cellSize = 300;
isRowSelected[indexPath.row] = YES;
}
else
{
cellSize = 100;
isRowSelected[indexPath.row] = NO;
}
return cellSize;
}
Now i got Like this in simulator.
when i pressed it comes like this.
This selected cell should come to first position.
You can scroll your table view to that cell and you can specify that you want to scroll it on top when you select the cell:
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[tableView scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionTop animated:YES];
}
Hope this is what you are after.
In the method_Expand method after fetching the selected row you have to remove the object at the selected index and insert the removed object at 0 index.
Also you want to move the move next item to the second position
For that you have to increment the selected index and check if that index is with in the array bounds then remove that item and add it to the index 1;
- (void)method_Expand:(UIButton*)sender
UITableViewCell *cell = (UITableViewCell *)sender.superview;
NSIndexPath *indexpath = [tbl_CalendarList indexPathForCell:cell];
int nextIndex=indexpath.row+1;
// first remove the object
NSString *str=[arr_CalendarTitle objectAtIndex];
[arr_CalendarTitle removeObjectAtIndex:indexpath.row];
[arr_CalendarTitle insertObject:str atIndex:0];
//do the same to arr_CalendarEventTime,arr_CalendarDate,arr_CalendarMont etc
if([arr_CalendarTitle count]-1<nextIndex)// check if nextIndex within bounds to avoid crash
{
// remove next object and addit to the index 1 to all array
}
[tbl_CalendarList reloadData];
}
As i wrote in the comments:
Upon selection, move selected arr_CalendarTitle entry to the top of array and call reloadData() on tableView. Table view displays data as is sorted in arr_CalendarTitle.
moveRowAtIndexPath is not enough, must resort the array too.
So, before reloadData() call (in button click method), do this:
id object = [[[arr_CalendarTitle objectAtIndex:int_SelectedIndex] retain] autorelease];
[arr_CalendarTitle removeObjectAtIndex:int_SelectedIndex];
[arr_CalendarTitle insertObject:object atIndex:0];
For ARC you can use :
__autoreleasing id object = [arr_CalendarTitle objectAtIndex:int_SelectedIndex];
[arr_CalendarTitle removeObjectAtIndex:int_SelectedIndex];
[arr_CalendarTitle insertObject:object atIndex:0];
Since you have more than one array that holds data (only noticed that now) you must do this for every array thats holds data for tableView cells.
Use below method to scroll your tableview.
[tableview setContentOffset:CGPointMake(0, button.tag*tableview.rowHeight) animated:YES];
This method will make tableview scroll to specified point.
Change value of Y in CGPointMake according to your code.
This question already has answers here:
indexPathForCell returns nil since ios7
(3 answers)
Closed 9 years ago.
Before asking this question, I searched a lot on Google and Stackoverflow. Tried also some examples, but I can't make the function work.
Since the hierarchy of the tableview is changed since iOS 7, is it kind of hard to find a solution.
I got a standard tableview with a couple items and one button on the screen.
I need to get the indexPath.row number when selecting an item from the tableview and clicking on the button.
This is my code
- (IBAction)buttonGetNumber:(id)sender {
NSIndexPath *indexPath = [self.tableView indexPathForCell:(UITableViewCell *)[(UIView *)[button superview] superview]];
NSLog(#"%i", indexPath.row);
}
This keeps returning a '0', no matter which item I select from the tableview.
I also tried this (2):
- (IBAction)buttonGetNumber:(id)sender {
UIButton *button = (UIButton *)sender;
UITableViewCell *cell = (UITableViewCell *) [[button superview] superview];
NSIndexPath *index = [self.tableView indexPathForCell:cell];
NSLog(#"%i", index.row);
}
This also returns a '0'.
I also tried this (3):
- (IBAction)buttonGetNumber:(id)sender {
UIButton *senderButton = (UIButton *)sender;
UITableViewCell *buttonCell = (UITableViewCell *)[[senderButton superview] superview];
UITableView* table = (UITableView *)[buttonCell superview];
NSIndexPath* pathOfTheCell = [table indexPathForCell:buttonCell];
NSInteger rowOfTheCell = [pathOfTheCell row];
NSLog(#"%i", rowOfTheCell);
}
And this makes the application crash.
Any clue how I can solve this?
Create an instance variable _lastClickedRow Set it with tableview delegate like below. And when you click the to "Get Row" button use _lastClickedRow.
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
_lastClickedRow = indexPath.row;
}
- (IBAction)buttonGetNumber:(id)sender {
NSLog(#"%d" , _lastClickedRow);
}
If for some reason selected cell doesn't work for you (e.g. for a multiple selection case), you could get row from sender's frame:
- (IBAction)buttonGetNumber:(id)sender
{
CGPoint buttonOrigin = sender.frame.origin;
CGPoint pointInTableview = [self.tableView convertPoint:buttonOrigin fromView:sender.superview];
NSIndexPath *indexPath = [self.tableView indexPathForRowAtPoint:pointInTableview];
if (indexPath) {
// Do your work
}
}
You simply set the tag of the UIButton same as the indexPath.row using:
yourButton.tag = indexPath.row;
in didSelectRowForIndexPath:.
Then in buttonGetNumber: method, you get the row number using:
int rowNum = [(UIButton*)sender tag];
Here, you have the advantage of not using any third variable.
I want to get index of touched Text Box in tableview cell. The fact that I could do it before IOS 7. In the past I could get the text box index in below. This method also fails to IOS 7. I read tag should use instead of this method. But I also use tag for each different text box. Because Clicking to the text box I am doing different things, for example opening the picker or opening to the selection view. Plase help me, how can I get the row number of each text box When touched to text box.
-(NSInteger)getTextFieldCellRow:(id)element{
UITableViewCell *cell = (UITableViewCell *)[[(UITextField*)element superview] superview];
UITableView *table = (UITableView *)[cell superview];
NSIndexPath *textFieldIndexPath = [table indexPathForCell:cell];
return textFieldIndexPath.row;
}
Ok, I solved
-(NSIndexPath *) getButtonCellRow:(UITextField *) b {
UITableView *tv = nil;
UIView *superViewTemp = [b superview];
UITableViewCell *cell=nil;
BOOL isFoundTable=FALSE;
BOOL isFoundCell=FALSE;
while(superViewTemp != nil && ![superViewTemp isKindOfClass:[UIWindow class]]){
if ([superViewTemp isKindOfClass:[UITableViewCell class]]) {
cell=(UITableViewCell*)superViewTemp;
isFoundCell = TRUE;
}else if ([superViewTemp isKindOfClass:[UITableView class]]){
tv = (UITableView*)superViewTemp;
isFoundTable = TRUE;
}
superViewTemp = [superViewTemp superview];
if(isFoundCell && isFoundTable){
break;
}
}
if(tv != nil && cell != nil){
return [tv indexPathForCell:(UITableViewCell*)cell];
}
return nil;
}
this should be a very simple solution
UIView *view = textField;
while (![view isKindOfClass:[UITableViewCell class]])
{
view = [view superview];
}
CellClass *cell = (CellClass *)view;
Try with following code:
UIView *contentView = (UIVIew *)[textfield superview];
UITableViewCell *cell = (UITableViewCell *)[contentView superview];
NSIndexPath *indexPath = [self.tableview indexPathForCell:cell];
NSLog(#"%d", indexPath.row);
But in iOS 7, you can get UITableViewCell by following code:
UITableViewCell *cell = (UITableViewCell *)[[contentView superview] superview];