Adding a custom nested UIView below UITableViewCell - ios

I am adding a expandale/collapsable UIView below UITableviewCell. I am able to add and hide the view on button action but unable to remove it when view is shown in another cell. I am updating the view height constraint to 0 on hiding.
Here's my code:
-(void)doneButtonClicked:(UIButton*)sender{
CGPoint buttonPosition = [sender convertPoint:CGPointZero
toView:_tableView];
NSIndexPath *indexPath = [_tableView
indexPathForRowAtPoint:buttonPosition];
CustomCell *cell = [_tableView cellForRowAtIndexPath:indexPath];
for (int i = 0; i < [_expandedIndexPaths count]; i++){
if (i != indexPath.row && [_expandedIndexPaths count] > 0){
[self.expandedIndexPaths removeObject:indexPath];
//Animation effect for expanding/collapsing view
[cell animateClosed];
}
}
if (sender.tag == 0) {
[self.expandedIndexPaths addObject:indexPath];
[cell animateOpen];
//[self.expandedIndexPaths removeObject:indexPath];
}else{
[self.expandedIndexPaths removeObject:indexPath];
[cell animateClosed];
}
[_tableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
}

The constraint update should also happen in the cellForRow method. Set the constant to 0 there.
If you want the view to appear again for the cell that open it in first
place and didn't close it. Add hidden state boolean var in the cell
class and update every time you update that constant. Then on
cellForRow check the value of that boolean and set the constant
accordingly.

Related

How to pass NSIndexPath object with addTarget method?

I want to pass NSIndexPath to #selector.
So I used following code, but there is no argument like withObject.
How can I do it?
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(nonnull NSIndexPath *)indexPath{
// User taps expanded row
if (selectedIndex == indexPath.row){
selectedIndex = -1;
[tableView reloadRowsAtIndexPaths:[NSArray arrayWithObjects:indexPath, nil] withRowAnimation:UITableViewRowAnimationFade];
}
// User taps different row
if (selectedIndex != -1){
NSIndexPath *prevPath = [NSIndexPath indexPathForRow:selectedIndex inSection:1];
selectedIndex = (int)indexPath.row;
[tableView reloadRowsAtIndexPaths:[NSArray arrayWithObjects:prevPath, nil] withRowAnimation:UITableViewRowAnimationFade];
}
// User taps new row with unexpanded
selectedIndex = (int)indexPath.row;
[tableView reloadRowsAtIndexPaths:[NSArray arrayWithObjects:indexPath, nil] withRowAnimation:UITableViewRowAnimationFade];
[favoriteButton addTarget:self action:#selector(favoritePressed:) withObject:indexPath forControlEvents:UIControlEventTouchDown];
[normalButton addTarget:self action:#selector(normalPressed) forControlEvents:UIControlEventTouchDown];
[halfButton addTarget:self action:#selector(halfPressed) forControlEvents:UIControlEventTouchDown];
}
-(void)favoritePressed:(NSIndexPath *)path{
}
There is a better way to do this. You can find the touch point on button click and convert that touch point in to your table frame, and through that point you can get indexpath.
-(void)favoritePressed:(UIButton *)sender{
CGPoint buttonPosition = [sender convertPoint:CGPointZero toView:self.tableview];
NSIndexPath *indexPath = [self.tableview indexPathForRowAtPoint:buttonPosition];
}
Try like this:
-(void)favoritePressed:(UIButton *)sender{
//CustomCell : your custom UITableViewCell class name
CustomCell *cell = (CustomCell) [[sender superview] superview];
NSIndexPath *indexPath = [self.tableView indexPathForCell:cell];
}
NOTE:
If you set button in content view of CustomCell this will work for you otherwise you need to add other superview for getting the cell.
The code below may work for certain condition but your button may be a inside other view inside a cell. In that case you may not find the cell.
CustomCell *cell = (CustomCell) [[sender superview] superview];
It is better to use it like this.
- (void)btnFavoriteTapped:(UIButton *)sender {
id view = [sender superview];
while ([view isKindOfClass:[CustomCell class]] == NO) {
view = [view superview];
}
CustomCell *selectedCell = (CustomCell *)view;
NSIndexPath *indexPath = [self.tableView indexPathForCell:selectedCell];
}

Find indexPath of cell by label

I have table with cells; each cell contain label with phone number. When i tap on the label, UIMenuController appears.
Is there a main way to find indexPath of cell, which contain selected label? (didSelectRowAtIndexPath: should not be called)
I can imagine many dirty hacks, but i looking for a main/good solution.
upd
I have reference to selected label.
Edit:
This is a better way.
- (IBAction)someMethod:(id)sender {
CGPoint hitPoint = [sender convertPoint:CGPointZero toView:self.tableView];
NSIndexPath *hitIndex = [self.tableView indexPathForRowAtPoint:hitPoint];
}
From your label, search for the cell object in the label's hierarchy:
-(UITableViewCell*) cellContainingView:(UIView*)view
{
UIView* currentView = view;
while (currentView)
{
if ([currentView isKindOfClass:[UITableViewCell class]])
{
return (UITableViewCell*)currentView;
}
currentView = currentView.superview;
}
return nil;
}
Once you have the cell, call [tableView indexPathForCell:cell]
The use of this method instead of hard coding view.superview.superview makes your code stronger because it handle possible changes in cell's view hierarchy through system's versions.
You could go:
NSMutableArray *cells = [[NSMutableArray alloc] init];
for (NSInteger j = 0; j < [tableView numberOfSections]; ++j)
{
for (NSInteger i = 0; i < [tableView numberOfRowsInSection:j]; ++i)
{
[cells addObject:[tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:i inSection:j]]];
}
}
for (UITableViewCell *cell in cells){
for (UILabel *label in cell.subviews){
if (label == yourLabel)
indexPath = [tableView indexPathForCell: cell];
}
}

Why can't I change UITableViewCell's ContentView after initiation

I am able to add a 'line separator' above my uitableviewcells manually (out-of-box iOS isn't cutting it for this). So I have created an array of UIViews, called _separatorLines, I add one UIView from each index to the uitableviewcells' contentView in my 'cellForRowAtIndexPath' with
UIView *v = [_separatorLines objectAtIndex:indexPath.row];
[cell.contentView addSubview:v];
however when i select a cell, i would like all other cells' line separators to turn red.
in 'didSelectRowAtIndexPath' I have:
{
for(unsigned int i = 0; i < _rowsInSection-1; i ++)
{
//make all other rows have a red content view
if(i != indexPath.row)
{
NSIndexPath *I = [NSIndexPath indexPathForRow:i inSection:1];
UITableViewCell *cell = [self tableView:tableView cellForRowAtIndexPath:I];
UIView *separatingLineView = [_separatorLines objectAtIndex:I.row+1];
separatingLineView.backgroundColor = [UIColor redColor];
[cell.contentView bringSubviewToFront:separatingLineView];
[separatingLineView setNeedsDisplay];
[cell setNeedsDisplay];
[cell.contentView setNeedsDisplay];
}
}
[self setNeedsDisplay];
Instead I do not see the contentView of the uitableviewcells changing.
For me I think the issue was the line
NSIndexPath *I = [NSIndexPath indexPathForRow:i inSection:1];
UITableView starts on section '0' (just like the rows start on '0').
And also because my UITableView is the delegate, I stopped using
[self tableView:tableView cellForRowAtIndexPath:I];
and replaced it with
[self cellForRowAtIndexPath:I];
Not sure if that had anything to do with it

Callback for cellForRowAtIndexPath?

My Project:
I have an UITableView in My project. Each and every UITableViewCell have one UITextView as a subview. When user tap on 'Add' button, I need to add one more UITableViewCell with UITextView to UITableView and I have to set focus into the added UITextView.
What I've Tried:
Once user tap on add button, I will update the numberOfRowsInSection of UITableView and subsequently I'll call [UITableView reloadData] to invoke [UITableView cellForRowAtIndexPath]. Here, I'll append the UITextView as subview of UITableViewCell.It Works fine until this point.
My problem:
I need to set focus into the UITextView Once after the [UITableView cellForRowAtIndexPath] get called. When I called the set focus method for UITextView after [UITableView reloadData], It is not working. I want to know, Is there any callback method for [UITableView cellForRowAtIndexPath]?
Thanks for your answers.
Try this,
In Add button action
//Assuming only 1 section, if you have more section find sections by `numberOfSections` method
NSInteger totalRows = [self.tableView numberOfRowsInSection:0];
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:totalRows inSection:0];
[tableView beginUpdates];
[tableView insertRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationNone];
[tableView endUpdates];
//If you have custom cell
CustomCell *cell = (CustomCell *)[self.tableView cellForRowAtIndexPath:indexPath];
[cell.textView becomeFirstResponder];
//If you are not using custom cell, then set tag to textView and
//UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
//UITextView *textView = (UITextView *)[cell viewWithTag:kTextViewTag];
//[textView becomeFirstResponder];
after you have called your reloadData method you can do something like this:
To get a reference to the last row in the last section…
// First figure out how many sections there are
NSInteger lastSectionIndex = [self.myTableView numberOfSections] - 1;
// Then grab the number of rows in the last section
NSInteger lastRowIndex = [self.myTableView numberOfRowsInSection:lastSectionIndex] - 1;
// Now just construct the index path
NSIndexPath *pathToLastRow = [NSIndexPath indexPathForRow:lastRowIndex inSection:lastSectionIndex];
//Grab a reference of your cell from the last row
MyCustomCell *myCC = [self.myTableView cellForRowAtIndexPath:pathToLastRow];
//Use this cell reference to set focus to your UITextView
[myCC.myTextView setFirstResponder:YES];

Looking for in all performing functions in all rows of a tableview, instead of visible cells

My question is clear,
I have a UITableView and UIMapView with annotations, when an annotation is tapped on the map, it will be found on the table and be selected since the user can recognize it.
But, if i try something it is only in visible cells, i am not able to do as i expected.
- (void)annotationTapped:(UITapGestureRecognizer *)recognizer{
if ( recognizer.state == UIGestureRecognizerStateEnded ) {
//NSLog(#"%#",[recognizer.view subviews]);
UIImageView *temp = (UIImageView*)[recognizer.view viewWithTag:1000];
UILabel *temp2 = (UILabel*)[temp viewWithTag:1001];
NSArray *tapAndFind;
if(isFiltered)
{
tapAndFind = filteredListContent;
}
else
{
tapAndFind = eczaneler;
}
for(int i=0;i<tapAndFind.count;i++)
{
Pharma *tempPharm = [tapAndFind objectAtIndex:i];
if([tempPharm.CustomerIndex isEqualToString:temp2.text])
{
EczaneCell *cell = (EczaneCell*)[tableView1 cellForRowAtIndexPath:[NSIndexPath indexPathForRow:i inSection:0]];
for(EczaneCell * cell2 in [tableView1 visibleCells])
{
cell2.selected = NO;
}
cell.selected = YES;
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:[tableView1 indexPathForCell:cell].row
inSection:[tableView1 indexPathForCell:cell].section];
[tableView1 scrollToRowAtIndexPath:indexPath
atScrollPosition:UITableViewScrollPositionTop animated:YES];
}
}
}
}
This is because your UITableView is just a presentation of your data, not a data itself. So, when you tap on annotation, you should somehow find a correspondence with a data, the position of your annotation data in collection. Then you may calculate/find out the index of row in table, and then you can perform UITableView's scrollToRowAtIndexPath:atScrollPosition. Alternatively, you can mark the cell to make it distinguishable.
In your code
EczaneCell *cell = (EczaneCell*)[tableView1 cellForRowAtIndexPath:[NSIndexPath indexPathForRow:i inSection:0]];
can return nil for cell when the cell for given index path is invisible. That's why you should check against data, not table cells.

Resources