Return NSIndexPath from custom cell ? (UITableView) - objective-c

I need to get the NSIndexPath for a custom cell in a UITableView. Here's the problem: I send a NSNotification from my custom cell to my UITableViewController when editingDidBegin gets called from a UITextField in my custom cell. In my UITableViewController, I resize the UITableView when the UITextField began editing, and then want the table view to scroll to the Cell in which the UITextField is first responder. But I can't figure out how to return the indexPath of the cell where the UITextField is being edited. I have tried so many ways, but its still not working. One way is this: in my cstomCell class i select the row using [self setSelected:YES] and in my TV controller then if I NSLog the row of [self.tableV indexPathForSelectedRow] it always returns 0 even though its always not 0.

Just give the cell a value for its tag property. Then you can get that cell by calling this
UITableViewCell *cell = (UITableViewCell *)[self.tableView viewWithTag:tagValue];
then once you have the cell you can get the NSIndexPath like this
NSIndexPath *indexPath = [self.tableView indexPathForCell:nextResponderCell];
Since you have a UITextField in your custom cell you can place cell.textField.delegate = self; in the
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
data source method. That way you will not have to setup a NSNotification in the Custom Cell. Also in this same method you can tag both your cells text field like this cell.textField.tag = indexPath.row; and the cell like this cell.tag = indexPath.row;
Now that you have set the UITextField delegate, you can now place this method in your UITableViewController class
- (void)textFieldDidBeginEditing:(UITextField *)textField {
CustomCell *cell = (CustomCell *)[self.tableView viewWithTag:textField.tag];
NSIndexPath *indexPath = [self.tableView indexPathForCell:cell];
}
The above UITextField delegate method should get you the indexPath for the cell you have currently selected.

try this code..
- (void)textFieldDidBeginEditing:(UITextField *)textField
{
UITableView *tableView;
UITableViewCell* superViewCell = [self returnSuperCellViewOfTextField:textField];
if(superViewCell)[tableView scrollToRowAtIndexPath:[tableView indexPathForCell:superViewCell] atScrollPosition:UITableViewScrollPositionMiddle animated:YES];
}
-(id)returnSuperCellViewOfTextField:(id)viewToCheck
{
id superView = [viewToCheck superview];
if([viewToCheck isKindOfClass:[UITableViewCell class]] && superView)return superView;
else if(([viewToCheck respondsToSelector:#selector(superview)] && superView)) return [self returnSuperCellViewOfTextField:superView];
return nil;
}

Does this (your answer that you gave before below)still works if you have multiple texfields in each cell?:
UITableViewCell *cell = (UITableViewCell *)[self.tableView viewWithTag:tagValue];
then once you have the cell you can get the NSIndexPath like this
NSIndexPath *indexPath = [self.tableView indexPathForCell:nextResponderCell];
Since you have a UITextField in your custom cell you can place cell.textField.delegate = self; in the
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
data source method. Also in this same method you can tag both your cells text field like this cell.textField.tag = indexPath.row; and the cell like this cell.tag = indexPath.row;
Now that you have set the UITextField delegate, you can now place this method in your UITableViewController class
- (void)textFieldDidBeginEditing:(UITextField *)textField {
CustomCell *cell = (CustomCell *)[self.tableView viewWithTag:textField.tag];
NSIndexPath *indexPath = [self.tableView indexPathForCell:cell];
I have a tableview with 8 cells, and in each cell i have 6 texfields, and also the tableview is dynamic, so the user can insert more cells...but i´m having trouble in how to save the text the user enters in each texfield. Is it possible that with "cell.textField.tag = indexPath.row", the controller identify´s the texfield i´m in?

I like to tag my cells like so:
/* Create a tag based on the section and row of a cell(used to retrive the cell for the beginEditingNextCell method. */
-(NSInteger)tagForIndexPath:(NSIndexPath*)path {
return path.section * 1000 + path.row + 1;
}
The other way is to pass the beginEditing data using a custom delegate, and include a cell model, which you can then use to figure out your path depending on how you are storing data.
Check out my github project, it has the code to handle this case, as well as resizing for the keyboard and tabbing to the next editable cell: https://github.com/andrewzimmer906/XCell

Related

How to find a cell textfield outside of a tableView delegate method?

I have a custom tableView cell with a textfield. Now, I want to recognise the textField outside of the tableView delegate method. I tried this-
UIView *cell = textField;
[cell isKindOfClass:[UITableViewCell class]];
but it's not working.
To get concrete cell from table view you need to know index path of this cell. Then you need to do:
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:0 inSection:0]; //you should set your indexes of row and section instead of zeros
MyCustomCell *cell = [myTableView cellForRowAtIndexPath:indexPath];
Now if text field is a property of your custom cell you can get it using cell.myTextField
For example:
UITextField *textField = cell.myTextField;
Now you can do what you want with it.

Is there a way to use UITextField's on a dynamic UITableView, AND 'tab' between them?

This may be a tad lengthy, but bear with me. It's mostly simple code and log output. Normally, if I wanted to have a UITextField as a part of a UITableViewCell, I would likely use either a) static rows or b) I would create the cell in the storyboard, outlet the cell and outlet the field to my ViewController, and then drag the cell outside of the "Table View", but keeping it in the scene.
However, I need to create a View where I accept input from 28 various things. I don't want to outlet up 28 different UITextField's.
I want to do this dynamically, to make it easier. So I've created a custom UITableViewCell with a Label and UITextField.
My ViewController has two arrays.
#property (nonatomic, strong) NSArray *items;
#property (nonatomic, strong) NSArray *itemValues;
My cellForRowAtIndexPath looks something like this...
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
NSString *cellIdentifier = #"ItemCell";
MyItemTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier forIndexPath:indexPath];
if (!cell) {
cell = [[MyItemTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier];
cell.categoryValue.tag = indexPath.row;
cell.categoryValue.delegate = self;
}
cell.item.text = [self.items objectAtIndex:indexPath.row];
cell.itemValue.text = [self.itemValues objectAtIndex:indexPath.row];
return cell;
}
- (BOOL)textFieldShouldReturn:(UITextField *)textField
{
NSInteger tag = [textField tag];
NSLog(#"TFSR tag: %zd/%zd", tag, self.categories.count-1);
if (tag < (self.categories.count - 1)) {
NSIndexPath *nextIndex = [NSIndexPath indexPathForRow:tag+1 inSection:0];
NSLog(#"TFSR nextRow: %zd/%zd\n", nextIndex.row);
FFFCategoryTableViewCell *cell = (MyItemTableViewCell *)[self.tableView cellForRowAtIndexPath:nextIndex];
[self.tableView scrollToRowAtIndexPath:nextIndex atScrollPosition:UITableViewScrollPositionMiddle animated:YES];
[cell.categoryValue becomeFirstResponder];
}
else {
NSLog(#"DONE!");
}
return YES;
}
This is proving to be problematic. The goal is, the user should be able to select the UITextField on the first row, enter a value, and when they hit the 'Next' key on the keyboard, they will be sent to the UITextField on the second row. Then 3rd, 4th,... 27th, 28th.
However, let's say I first highlight the UITextField on the Cell with IndexPath.row = 11. If I tap 'Next', this is what my output looks like...
======== OUTPUT ========
TFSR tag: 11/27
TFSR nextRow: 12/27
TFSR tag: 12/27
TFSR nextRow: 13/27
TFSR tag: 13/27
TFSR nextRow: 14/27
TFSR tag: 0/27
TFSR nextRow: 1/27
Now I fully understand why this is happening. With UITableView trying to save memory in loading various cells, and with the use of dequeueReusableCellWithIdentifier... I only have 14 cells (0-13). Then it loops back to the beginning.
My problem is... I don't know a solution to this problem. I want the user to be able to his Next until the 28th UITextField row.
Any ideas/solutions on how I could achieve this?
The problem is your use of tags. You only set the tag the first time the cell is created. It needs to be set on every call to cellForRowAtIndexPath:. You want:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
NSString *cellIdentifier = #"ItemCell";
MyItemTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier forIndexPath:indexPath];
if (!cell) {
cell = [[MyItemTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier];
cell.categoryValue.delegate = self;
}
cell.categoryValue.tag = indexPath.row;
cell.item.text = [self.items objectAtIndex:indexPath.row];
cell.itemValue.text = [self.itemValues objectAtIndex:indexPath.row];
return cell;
}
Please note that setting the tag to the index path only works if the rows are fixed. If your table rows are more dynamic (some can be added, removed, or reordered) then using the index path as the tag will fail and another approach must be used.

Min and Max button functionality in UITableVIew

I want to do min/max (expand/collapse) button functionality.Please see bellow image for more information.Is anybody know how to develop this functionality.I have some examples but those are not like exactly what i want.Please help me to solve this problem.
I've got a similar kinda requirement few days back. I have done this using custom tableview cell.
Firstly, you have to pass the indexpath of each row to the cell. For this i have taken a property in custom tableview cell.
i.e, #property(nonatomic,assign) NSIndexPath *indexPathOfCell;
Now, you have to pass the indexpath of row in datasource method.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath;
And you have to write a delegate method in custom tableviewcell so that when the user clicks on the min/max button you can reload that cell through this delegate call.
-(void)reloadCellWithIndexPath:(NSIndexPath*)indexPath;
You have to set the view controller as delegate for the custom tableview cell in which you are populating the table. In that class you have to maintain an NSMutableArray.
NSMutableArray *selectedIndexPaths;
When ever the reloadCellWithIndexPath gets called you can simply do same.
-(void)reloadCellWithIndexPath:(NSIndexPath*)indexPath{
if(![selectedIndexPaths containsObject:indexPath]){
[selectedIndexPaths addObject:indexPath];
}else
[selectedIndexPaths removeObject:indexPath];
[tableView reloadData];
}
In heightForRowAtIndexPath method you can change the height for a particular row.
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
BOOL cellModeChanged = NO;
for (NSIndexPath *indexpath in selectedIndexPaths) {
if(indexpath.section == indexPath.section && indexpath.row == indexPath.row)
cellModeChanged = YES;
}
if(cellModeChanged)
return 400.0;
else
return 100.0;
}

UITableView Center Cell Details

I am trying to sync a UITableView with a UILabel to make sure they show the same data; of course, things will be different in the end, but for testing this is what I need to do.
See the arrow? I want that middle cell (from px44-88) to show the cell.textLabel.text in a UILabel when it is the "middle cell".
I tried using - (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath but I was having so many problems I figured I'd come here to ask if anyone has a better way of doing this. I'm not sure if it would make a difference or not but I am using NSFetchedResultsController to populate my UITableView.
UITableView is a subclass of UIScrollView, so probably you can use UIScrollViewDelegate
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
member to detect which cell is currently in the middle.
I.e. assuming you have a plain table with no sections, and tableView is an outlet for the table, label is an outlet for the label, than this will function in your controller will work:
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
int row = (scrollView.contentOffset.y + tableView.frame.size.height / 2) / 44;
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:row inSection:0];
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
label.text = cell.textLabel.text;
}
Of course, you need to do some scrolling to make it work ;)
You can use visibleCells method to get layout of visible cells for non-plain table and use it to detect cell in the middle of the table.

Add editing elements to UITableViewCell

I have a couple of rows in a table view which I like to edit.
I thought that setEditing method would give me a Edit and Delete button, but it only shows a Delete button. Because I don't have a detail view controller that's going to be pushed in didSelectRowAtIndexPath I thought I could show a couple of editing elements in the selected cell.
I need to add three buttons to specify priority on assignments: Low, High and Medium priority. This means that I have to change the height of the selected cell to make room for these buttons, I think that's rather easy to do.
What I'm wondering is if this is the correct path to choose?
I have done quite a lot research today without finding examples of how other have solved editing in a UITableViewCell. If you edit a contact in the Contacts app in the iPhone the UITableViewCells changes to enable quick and easy editing, that's what I'm looking for.
So what do you have for tips for me regarding this question?
Edit #1
My code in didSelectRowAtIndexPath is:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
Cell *selectedCell = (Cell *)[tableView cellForRowAtIndexPath: indexPath];
UIButton *btn = [UIButton buttonWithType: UIButtonTypeRoundedRect];
btn.frame = CGRectMake(0, 0, 50, 100);
btn.titleLabel.text = #"Set this item to High Priority";
btn.backgroundColor = [UIColor greenColor];
[selectedCell.contentView addSubview: btn];
self.editing = YES;
[tableView reloadRowsAtIndexPaths:[NSArray arrayWithObject: indexPath] withRowAnimation:UITableViewRowAnimationFade];
}
Code for heightForRowAtIndexPath:
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
Cell *cell = nil;
if (self.editing)
cell = (Cell *)[tableView cellForRowAtIndexPath: indexPath];
else
cell = nil;
BOOL expandCell = NO;
NSInteger expandationHeight = 0;
if (cell != nil)
{
for (UIButton *btn in cell.contentView.subviews)
{
NSLog(#"A button was found.");
expandationHeight = 70;
expandCell = YES;
}
}
return expandationHeight + heightOfOtherElements;
}
When I click at a cell nothing happends but everything becomes disabled, I can't click any elements on the hole view. This has something to do with [tableView cellForRowAtIndexPath: indexPath], because if I uncomment that line the UI does not become disabled.
What am I doing wrong?
That's not too complicated though it requires some stuff.
You want to change the content of one cell or of all cells?
To specify a specific height for one cell, use the - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath delegate method.
It is called for each cell each time the table view is displayed. If you want to change a single row, call reloadRowsAtIndexPaths:withRowAnimation: method from UITableView. If you want to update all cells, simply use - (void)reloadData method.
You can also access a specific cell using - (UITableViewCell *)cellForRowAtIndexPath:(NSIndexPath *)indexPath method from UITableView. Then you can reconfigure it to add various elements on it, as you want.
Thus, when a cell is selected, check whether you have to edit it or not, then :
update your cell get from cellForRowAtIndexPath
be sure your method tableView:heightForRowAtIndexPath: will return the good height
tell the table view to update the view using reloadRowsAtIndexPaths:withRowAnimation: