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.
Related
So I've got a universal app, and I'm trying to hide a border of a cell, if the next cell in the tableView has an error state. I use this code in tableView:cellForRowAtIndexPath: to determine if the cell should hide it's bottom border or not:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
DCSMCDynamicBaseCell * cell = [self.cellManager dynamicCellForComponent:[self componentForIndexPath:indexPath] atIndexPath:indexPath canShowErrors:self.shouldDisplayErrors];
NSIndexPath *nextIndexPath = [self.tableView nextRowForIndexPath:indexPath];
if (nextIndexPath) {
DCSMCDynamicBaseCell *nextCell = [self.tableView cellForRowAtIndexPath:nextIndexPath];
DCSMCDynamicBaseCell *currentCell = [self.tableView cellForRowAtIndexPath:indexPath];
if (nextCell.invalidState) {
cell.shouldHideBottomBorder = YES;
NSLog(#"%#", currentCell);
}
}
if ([cell isKindOfClass:[DCSMCDynamicCopyCell class]]) {
[((DCSMCDynamicCopyCell *)cell).webView setNavigationController:self.navigationController];
}
[cell setDelegate:self];
self.previousIndexPath = indexPath;
return cell;
}
My nextRowForIndexPath looks like this:
- (NSIndexPath *)nextRowForIndexPath:(NSIndexPath *)indexPath {
NSInteger currentRow = indexPath.row;
NSInteger currentSection = indexPath.section;
NSInteger numberOfRows = [self numberOfRowsInSection:indexPath.section];
NSInteger numberOfSections = [self numberOfSections];
NSIndexPath *newIndexPath;
if (currentRow + 1 < numberOfRows) {
newIndexPath = [NSIndexPath indexPathForRow:currentRow + 1 inSection:currentSection];
} else if (currentSection + 1 > numberOfSections) {
newIndexPath = [NSIndexPath indexPathForRow:0 inSection:currentSection + 1];
} else {
newIndexPath = nil;
}
return newIndexPath;
}
On my iPhone 6 simulator, everything works fine. It gets the cell and hides the bottom border of it. I may refactor this a bit in order to make sure it works in all scenarios, but for a first pass before testing, it works great.
The issue is on the iPad, every single cell returns nil when using the cellForRowAtIndexPath: method. Even weirder, on iPad, the screen is big enough so that all cells are visible at all times, so when I call reload data, the cells are already visible and should get returned by this method.
Does anyone know why a cell wouldn't get returned by cellForRowAtIndexPath:on iPad but would on iPhone?
This is the type of screen I'm making:
So when the UISwitch state changes it should change the label to ON or OFF. In my cellForRowAtIndexPath I'm calling [cell.mainSwitch addTarget:self action:#selector(switchChanged:) forControlEvents:UIControlEventValueChanged]; and switchChanged is as follow:
-(void) switchChanged:(id)sender {
UISwitch* cellNew = (UISwitch*)sender;
if ([cellNew isOn]) {
cell.funcStatus.text = [funcStatusArr objectAtIndex:0];
} else {
cell.funcStatus.text = [funcStatusArr objectAtIndex:1];
}
}
Now the problem I'm facing is that it is not changing the Label at the specific cell butt all the switches are changing the Label of 4th(last) cell as shown in the figure below.
As you can see that the first label is off but it is changing the label of last row. Any ideas why it is happening or how to tell the functions that this index.row is sending the request.
So u must add tag property to all UISwitch. Fasters way its in - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath call
// code
// its good work if u have only 1 section
mySwitch.tag = indexPath.row.
//code
Than fix u're code
-(void) switchChanged:(UISwitch*)switch {
SettingsTableViewCell *selectedCell;
NSIndexPath *selectedIndexPath;
if(switch.tag == 0)
// create selectedIndexPath with correctrly row and section
selectedIndexPath = [NSIndexPath indexPathForRow:'YourRow'inSection:'Your section']
// create cell
selectedCell = [self.tableView cellForIndexPath:selectedIndexPath];
} else if(switch.tag == 1) {
// same logic;
}
// and replace text for selected cell
if ([switch isOn]) {
selectedCell.funcStatus.text = [funcStatusArr objectAtIndex:0];
} else {
selectedCell.funcStatus.text = [funcStatusArr objectAtIndex:1];
}
}
it's must work.
You doing wrong thing.First you need to get cell at specific changed event.
follow this code.
-(void) switchChanged:(id)sender {
UISwitch* switchBtn = (UISwitch*)sender;
//self.tableView is UITableView's outlet named tableView
CGPoint buttonPosition = [sender convertPoint:CGPointZero toView:self.tableView];
NSIndexPath *indexPath = [self.tableView indexPathForRowAtPoint:buttonPosition];
//First detect Tableview's Cell then do the stuff
//CustomCellTableViewCell replace it with you custom cell class
CustomCellTableViewCell *cell = [self.tableView cellForRowAtIndexPath:indexPath];
if ([switchBtn isOn]) {
cell.funcStatus.text = [funcStatusArr objectAtIndex:0];
} else {
cell.funcStatus.text = [funcStatusArr objectAtIndex:1];
}
[self.tableView reloadRowsAtIndexPaths:[[NSArray alloc] initWithObjects:indexPath, nil] withRowAnimation:UITableViewRowAnimationNone];
}
Cheers.
I'm trying to retrieve the textfield data from custom cell after tap button which is placed in last cell of index, that means my table have a submit button to retrieve the data from custom cell, for that reason I placed my button in xib and loaded after my array count, for that I written code like this
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
return [[dic_demo objectForKey:#"Demo"] count]+1;
}
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSInteger lastSectionIndex = [tableView numberOfSections] - 1;
NSInteger lastRowIndex = [tableView numberOfRowsInSection:lastSectionIndex] - 1;
NSIndexPath *pathToLastRow = [NSIndexPath indexPathForRow:lastRowIndex inSection:lastSectionIndex];
if (indexPath==pathToLastRow)
{
// for submit button through xib
}
else
{
normal custom cell for textfields
}
}
to retreive the data i written code like this
[mutarr_txt_TCelltext removeAllObjects];
for (int i =0; i<[[dic_demo objectForKey:#"Demo"] count]; i++) {
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:i inSection:0];
CustomTableViewCell *cell = (CustomTableViewCell *)[self.tbl_SBeneficiaries cellForRowAtIndexPath:indexPath];
for(UIView *view in [cell.contentView subviews])
{
if([view isKindOfClass:[UITextField class]])
{
UITextField *textField = (UITextField *)view;
NSLog(#"%#",textField.text);
NSString *str_txt=[NSString stringWithFormat:#"%#",textField.text];
NSLog(#"First text is %lu",(unsigned long)[str_txt length]);
if ([str_txt length]>1) {
[mutarr_txt_TCelltext addObject:str_txt];
}
}
}}
but my problem is not getting first indexpath tetxtfield data & remaining textfield data is retreving. Could anyone help on this,Please find my screen shots
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.
Im using storyboards with prototypecells what looks like:
after that im adding dynamic count of subviews:
white screen
uitableviewcell in app
Problem what sometimes white screen appears on half of uitableviewcell then scrolling.
And cell==nil not called.
Or just explain how to add dynamic count of subviews to uitableviewcell.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *cellIdentifer;
cellIdentifer = #"PhotoWorkCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifer];
if (cell == nil){
NSLog(#"is nil");
}
[self addSubviews:cell atIndexPath:indexPath];
[self configureCell:cell atIndexPath:indexPath];//just setting images and text to views
return cell;
}
-(void)addSubviews:(UITableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath{
PhotoWork *work = [self.tableData objectAtIndex:indexPath.row];
PhotoWorkCell *workCell = (PhotoWorkCell*)cell;
for(UIView *view in workCell.contentView.subviews){
if ([view isKindOfClass:[CustomPoints class]])
[view removeFromSuperview];
}
for(int i=0; i <[work.pointsArray count];i++)
{
NSArray *subviewArray = [[NSBundle mainBundle] loadNibNamed:#"CustomPoints" owner:self options:nil];
CustomPoints *customPoints = [subviewArray objectAtIndex:0];
customPoints.frame = CGRectMake(10, 135+i*customPoints.frame.size.height, customPoints.frame.size.width, customPoints.frame.size.height);
customPoints.label= #"test";
workCell.photoButton.tag = indexPath.row;
[workCell.photoButton addTarget:self action:#selector(onHistory:) forControlEvents:UIControlEventTouchDown];
[workCell.contentView addSubview:customPoints];
}
}
- (CGFloat)tableView:(UITableView *)t heightForRowAtIndexPath:(NSIndexPath *)indexPath {
PhotoWork *work = [self.tableData objectAtIndex:indexPath.row];
return 135+[work.pointsArray count]*57;
}
From Apple's documentation of the dequeueReusableCellWithIdentifier: method:
This method dequeues an existing cell if one is available or creates a new one using the class or nib file you previously registered. If no cell is available for reuse and you did not register a class or nib file, this method returns nil.
This means that, if a cell with that identifier exists, the method will never return nil, but will rather initialise the cell for you through the initializer method initWithStyle:reuseIdentifier:.
Then from the documentation of the UITableViewCell's method prepareForReuse:
The table view's delegate in tableView:cellForRowAtIndexPath: should always reset all content when reusing a cell.
Which means that you should remove the additional subviews it might have before adding the new ones in your addSubviews:atIndexPath: method.
You could simply do:
NSMutableArray* tmpArray = [NSMutableArray arrayWithArray:workCell.contentView.subviews];
Then, assuming that the only extra subview you add to contentView is this CustomPoints, just do:
[tmpArray removeObject:tmpArray.lastObject];
workCell.contentView.subviews = tmpArray;
If, instead, you want to be safe just do:
__block NSInteger foundIndex = NSNotFound;
[tmpArray enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
if ([obj isKindOfClass:[CustomPoints class]]) {
foundIndex = idx;
// stop the enumeration
*stop = YES;
}
}];
if (foundIndex != NSNotFound){
[tmpArray removeObjectAtIndex:foundIndex];
workCell.contentView.subviews = tmpArray;
}
Let me know if this worked for you!